在移动App开发中,为了快速迭代,通常都会使用Native+Web的模式开发。具体来说就是使用Java提供接口,使用WebView控件嵌套Web页面来实现UI和交互。

在Android中,Java可以很方便的提供接口给WebView中的Js进行调用,只要以下一行代码就能搞定:

mWebView.addJavascriptInterface(new JavascriptInterface(), "custom_name");

这样,JavascriptInterface的所有声明为public的方法,都能被mWebView中的Js通过以下方式调用:

window.custom_name.xxx();//xxx为JavascriptInterface的公有方法

而Java需要调用Js时,则是通过WebView实例的loadUrl方法调用:

mWebView.loadUrl("javascript:xxx(yyy)");

这其实跟你在浏览器的地址栏敲下“javascript:alert(1)”的原理是一样的。

一切看起来都是那么的美好,只是……

当Java需要传递大量字符串给Js时,URL就力不从心了。另外,从URL执行的Js,在页面没加载完成时,是有可能导致页面出现undefined错误(因为要执行的那个方法可能还没有声明呢),会引发各种奇形怪状的错误。

more

那要怎么办呢?

再回看上面Js调用Java接口的地方,可以发现,Js是可以直接调用Java方法并取得Java给的返回值的(必须是可序列化的数据)。那么,为何不试试把Java需要执行的Js方法,保存起来,让Js来读取、执行、并把结果写回?

步骤如下:

  1. Java把Js调用(命令)和回调缓存,如保存到ArrayList;
  2. Js定时轮询Java提供的getCommandList接口;
  3. Js读取到要执行命令,执行它,并把结果通过setResult写回给Java;
  4. Java把对应命令的回调取出并执行。

如此即完成了Java调用Js的流程。

为了方便使用,简单的封装了下,名为AndroidJavascriptBridge,可移步到Github查看源码和示例(MainActivity.java和test.js )。

使用方法

Android端调用, 加入com.imatlas.jsb 和 com.imatlas.util包, 按如下步骤调用

//1. 创建JavascriptBridge实例  
final JavascriptBridge jsb = new JavascriptBridge(webView); 

//2. 调用Javascript方法
Bundle params = new Bundle();
params.putString("asdfasdf", "123123");
jsb.require("alert", params, new JavascriptBridge.Callback() {
    @Override
    public void onComplate(JSONObject response, String cmd, Bundle params) {
        Log.i("js response",response.toString());
    }
});

//3. 提供Java方法给Javascript调用
//添加个 messagebox 方法给js
jsb.addJavaMethod("messagebox", new JavascriptBridge.Function() {
    @Override
    public Object execute(JSONObject params) {
        Toast.makeText(getApplicationContext(), params.toString(), Toast.LENGTH_LONG)
                .show();
        return "{\"ret\":123}";
    }
});

Javascript端的调用, 须先引入web/js/jsb.js, 之后按如下方式调用

//1. 调用Java方法
jsb.require('messagebox', {'text': '你好, messagebox!'}, function(response){
    alert('调用messagebox回来啦\n' + JSON.stringify(response));
});

//2. 提供Javascript方法给Java调用
jsb.addJavascriptMethod('alert', function(params){
    alert( '------\n' + JSON.stringify(params) + '\n========\n');
    return {'text': 'alert ok'};
});

IOS的话就要反过来了,要改成由Objective-C来轮询Js,来实现Js对Native的调用。

嗯,等改天有时间了,就把IOS也封装进来,用起来就简单多了。

Comments
Write a Comment
  • airoschou reply

    java不太了解啊,救命啊

  • iAzrael reply

    @airoschou 呵呵,java相对objective-c来说还是比较简单的啦,花一个星期就基本能掌握了

  • pedant reply

    在页面UI线程一直跑着轮询,不会使页面性能瞬间下降吗,特别是在安卓2.x系统上的webview?

Tags

css3   魅力CSS   nodejs   loading   CSS   疯狂的菊花   html5   animation   compiler   编译脚本   png   WordPress   智能   旅行   优化   模板   历史记录   跨域   manifest   frame   canvas   动画   js   离线应用   codelet   transform   抽取   java   兼容问题   发布脚本   富文本   那一年在他乡   htaccess   iframe   帧动画   加载速度   intelligent   跨浏览器   DNS解析   插件   checkbox   单边   step-start   vary   复选框   自动更新   转换   文本溢出   盒子阴影   menu   blob   西安   滑动背景   box-shadow   内存占用   键盘事件   python   auto   text overflow   background   所见所得   android   rotate   字节数   合并   文本框   slide   字符串连接符   协议   伪类   兄弟选择符   网格   节点位置比较   空白   斜线拼接   自定义命令   溢出   clock   素描   无法更新   分隔符   字符编码   body   下载文件   精灵图   step   nodej   ubuntu   apache   css3选择器   创建文件   多级菜单   编辑状态   ajax   阴影   垂直   chrome   管道   时钟   firefox   背景   文件上传   createobjecturl   游记   下载   放射渐变   版本号   宽高   照片   localStorage   渐变背景   图片   图片拼接   属性值检测   自动生成   计算   返回键   oauth   合图   reset   调用   cavnas   漏洞   按钮   margin   线性渐变   xsrf   被黑   tab   checked   修复   border   消失   step-end   sprite   common-upload   菜单   兄弟选择器   字符串   svn   九寨沟   缩进   css遮罩   svg   添加系统服务   gzip   插入代码   动态   加速   模拟