Android WebView加载本地html并实现Java与JS交互

2017-03-24 15:13:47

最近做的一个项目中,用到自定义地图,将自定义地图转换成html页面,现在需要做的是如何将本地的html加载到android中,并可以实现交互。

相关讲解:

其实webview加载资源的速度并不慢,但是如果资源多了,当然就很慢。图片、css 、js 、html这些资源每个大概需要10-200ms ,一般都是30ms就ok了。不过webview是必须等到全部资源都完成加载,才会进行渲染的,所以加载的速度很重要!从Google上我们了解到,webview加载页面的顺序是:先加载html,然后从里面解析出css、js文件和页面上的图片资源进行加载。如果webkit的缓存里面有,就不加载。加载完这些资源之后,就进行css的渲染和js的执行。CSS的渲染一般不需要很长时间,几十毫秒就ok。关键是js的执行,如果用了jQuery,则执行起来需要5-6秒。而在这段时间,如果不在webview里设置背景,网页部分是白色的,很难看。这是一个很糟糕的用户体验。所以建议如果用网页布局程序,最好别用那些庞大的js框架。最好使用原生的js写业务脚本,以提升加载速度,改善用户体验。

在Android上怎样实现JAVA和JS交互呢?

Android的webview是基于webkit内核的,webview中集成了js与java互调的接口函数,通过addJavas criptInterface方法,可以将Java的类注册进webkit,给网页上的js进行调用,而且还可以通过loadUrl方法给webkit传递一个URL,供浏览器来进行解析,实现Java和js交互。

要想运行网页上的js脚本,webview必须设置支持Javas cript。

Java代码:1mWebview.getSettings().setJavascriptEnabled(true);

然后是设置webView要加载的网页:

  web的网页:webView.loadUrl("http://www.google.com");

  本地的网页:webView.loadUrl("file:///android_asset/XX.html"); //本地的存放在:assets文件夹中

webview做完基本的初始化后我们还要要给它,加进一个回调的代理类JavascriptInterface,并给它一个调用的名称:ncp

Java代码:1mWebView.addJavas criptInterface(new Javas criptInterface(),"ncp");

Javas criptInterface可以是一个普通的Java类,类实现的方法,均可被js回调:

final class Javas criptInterface {
  public int callOnJs() {
    return 1000;
  }
  public void callOnJs2(String mode) {
    //TODO
  }
}

ava要调用js的方法,只需知道js的方法名称即可:

  Java代码:1mWebView.loadUrl("javas cript:onSaveCallback()");

js 这边就更简单(存在的版本兼容问题,参考使用WebView过程中的问题总结):

  JS代码:  

window.onload = function(){

  document.getElementById('btn_1').addEventListener('click', onBtnClick, false);

  var _int_value = window.ncp.callOnJs();

  alert("get int from java:" + _int_value );

}

function onBtnClick() {

  window.ncp.callOnJs2("click");

}

Java和js交互有以下一些特点:

1.Java 调用 js 里面的函数,速度并不令人满意,大概一次一两百毫秒吧,如果要做交互性很强的事情,这种速度会让人疯掉的。而反过来就不一样了, js 去调 java 的方法,速度很快,基本上 40-50 毫秒一次。所以尽量用 js 调用 java 方法,而不是 java 去调用 js 函数。

2.Java 调用 js 的函数,没有返回值,而 Js 调用 java 方法,可以有返回值。返回值可以是基本类型、字符串,也可以是对象。如果是字符串,有个很讨厌的问题,第 3 点我会讲的。如果是对象,这个对象会被转换为 js 的对象,直接可以访问里面的方法。但是我不推荐 java 返回给 js 的是对象,除非是必须。因为 js 收到 java 返回的对象,会产生一些交换对象,而如果这些对象的数量增加到了 500 或 600 以上,程序就会出问题。所以尽量返回基本数据类型或者字符串。

3.Js 调用 Java 的方法,返回值如果是字符串,你会发现这个字符串是 native 的,不能对它进行一些修改操作,比如想对它 substr ,取不到。怎么解决呢?转成 locale 的。使用 toLocaleString() 函数就可以了。不过这个函数的速度并不快,转化的字符串如果很多,将会很耗费时间。

下面是一个具体的例子:

1.新建assets文件夹,将本地html页面拷贝到该文件夹下。

2.xml文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent"  
    tools:context=".MainActivity" >  
  
    <TextView  
        android:id="@+id/tv"  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:text="@string/hello_world" />  
    <WebView  
        android:layout_below="@+id/tv"  
        android:id="@+id/webview"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"  
    />  
  
</RelativeLayout>
package com.example.webview_workflowy;  
  
import android.app.Activity;  
import android.content.Intent;  
import android.net.Uri;  
import android.os.Bundle;  
import android.webkit.WebView;  
import android.widget.Toast;  
  
public class MainActivity extends Activity {  
  
    private WebView webView;  
  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        //加载页面  
        webView = (WebView) findViewById(R.id.webview);  
        //允许JavaScript执行  
        webView.getSettings().setJavaScriptEnabled(true);  
        //找到Html文件,也可以用网络上的文件  
        webView.loadUrl("file:///android_asset/index.html");  
        // 添加一个对象, 让JS可以访问该对象的方法, 该对象中可以调用JS中的方法  
        webView.addJavascriptInterface(new Contact(), "contact");  
    }  
  
    private final class Contact {  
        //JavaScript调用此方法拨打电话  
        public void call(String phone) {  
//            startActivity(new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phone)));  
            Toast.makeText(MainActivity.this, phone, Toast.LENGTH_LONG).show();  
        }  
  
        //Html调用此方法传递数据  
        public void showcontacts() {  
            String json = "[{\"name\":\"zxx\", \"amount\":\"9999999\", \"phone\":\"18600012345\"}]";   
            // 调用JS中的方法  
            webView.loadUrl("javascript:show('" + json + "')");  
        }  
          
        public void toast(String str){  
            Toast.makeText(MainActivity.this, "aaaaaaaaaaaa  --- " + str, Toast.LENGTH_LONG).show();  
        }  
    }  
  
}
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
<html>  
    <head>  
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
        <title>Insert title here</title>  
        <script type="text/javascript">  
            function show(jsondata){              
                    var jsonobjs = eval(jsondata);  
                    var table = document.getElementById("personTable");  
                    for(var y=0; y<jsonobjs.length; y++){  
                        var tr = table.insertRow(table.rows.length);   
                        var td1 = tr.insertCell(0);  
                        var td2 = tr.insertCell(1);  
                        td2.align = "center";  
                        var td3 = tr.insertCell(2);  
                        td3.align = "center";  
                        td1.innerHTML = jsonobjs[y].name;   
                        td2.innerHTML = jsonobjs[y].amount;   
                        td3.innerHTML = "<a href='javascript:contact.call(\""+ jsonobjs[y].phone+ "\")'>"+ jsonobjs[y].phone+ "</a>";   
                    }  
            }  
        </script>  
    </head>  
    <body onload="javascript:contact.showcontacts()">  
       <button id="button" onclick = "javascript:contact.toast('123')">haha</button>  
       <table border="0" width="100%" id="personTable" cellspacing="0">  
            <tr>  
                <td width="30%">姓名</td>  
                <td width="30%" align="center">存款</td>  
                <td align="center">电话</td>  
            </tr>  
        </table>  
    </body>  
</html>


  • 2019-06-04 16:40:30

    为了美观当网页图片不存在时不显示叉叉图片

    当在页面显示的时候,万一图片被移动了位置或者丢失的话,将会在页面显示一个带X的图片,很是影响用户的体验。即使使用alt属性给出了”图片XX”的提示信息,也起不了多大作用。

  • 2019-06-04 17:38:44

    PHP时间转换今天昨天前天几天前

    实际情况应该是,昨天任何时间都算一天前,前天任意时间都算2天前,所以自己琢磨了一番,去动态更新时间与今天23:59:59相差的时间秒数与86400(24 x 3600)相除后,向下取整,这样就得到了相差的天数,比如昨天00:00~昨天23:59:59的任何时间与今天的23:59:59,都相差 86400~(86400 x 2) 天,也就是2天。

  • 2019-06-04 17:44:14

    【VUE】图片预览放大缩小插件

    在看项目时,突然看到预览图片的弹窗,感觉好僵硬,不能放大,不能切换,于是便在网上找下关于图片预览的插件,有找到三个插件,具体的优劣势的看自己的使用吧,我目前只是在电脑查看效果,分别是viewerjs插件、基于photoswipe的vue-photo-preview插件以及vue-picture-preview插件 作者:北极星丶超帅的 链接:https://www.jianshu.com/p/e3350aa1b0d0 来源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

  • 2019-06-04 21:32:05

    Android 全局Dialog的简单实现

    这里所说的全局Dialog是指无论当前应用是处于哪一个页面上,都能够及时弹出Dialog来提示用户一些信息,用户体验不会很好,一般应用于优先级非常高的通知。

  • 2019-06-06 16:14:50

    intent-filter的action,category,data匹配规则

    我们知道有两种方式来启动Activity,显示调用和隐式调用。当使用隐式调用时,又会涉及到IntentFilter的匹配规则。我确信大多数开发者很少关注隐式调用,因为平时开发中用到大多数是显示调用。例如:用Intent直接打开一个Activity,或者用Intent通过包名等其他信息打开另外一个应用等。而隐式调用则使用的比较少,当然也不是完全不使用。例如:当我们需要打开浏览器访问某个链接时,手机上可能存在多个浏览器,我们也无法拿到某一个浏览器的包名,那么一般情况下我们会写如下代码:

  • 2019-06-06 17:57:57

    input【type="checkbox"】标签与字体对齐

    今天分享一个比较实用的技巧,在实际项目中我们会经常遇到表单的input标签多选和单选的问题,但是往往由于标签自身的样式和我们项目的风格很不搭调,就不能实现了,今天就来告诉大家怎么去实现吧。

  • 2019-06-10 11:54:52

    html.div禁用点击事件

    今天做项目中偶然误把元素加上了pointer-events属性,结果导致后来在js中给该元素加点击事件不能用,检查了半天才发现是这个属性的问题。之前没有好好研究,于是决定仔细研究一下。