通常在混合app中经常会使用js调用native的方法,一般是:

window.nativeApp.call(XXX);

直接调用native方法,对于简单的处理倒是可以,如果需要回调呢?期待的方式是:

window.nativeApp.call(XXX,function(res){
//XXX处理回调
});

这样处理才更符合项目的需求!

基于这样的思路,自己实现了基于Webview拓展了新功能,代码已上传到git:

https://github.com/cmlbeliever/RX-Volley

核心类为CrossWebview,Extension

下面分析下native处理回调的实现方式:

主要实现步骤:

  1. 将需js的回调函数保存起来
  2. 本地执行业务代码
  3. 调用js的回调函数

下面分别分析每个步骤

1、原先直接调用native方法,需要改成调用带有保存回调函数功能的方法,在此方法中保存回调函数,然后调用native方法。Android在Webview上是允许直接执行js的

webview.loadUrl("javascript:xxxx");

在webview初始化完成后,实例化具有保存回调功能的方法,js代码如下

        window.#{alias}Exports={};
window.#{alias}Extension={};
var #{alias}Counter = 0;
var #{alias}SuccessCbs = {}; window.#{alias}Extension.callbackJs=function (message) {
var data=message;
var cb;
if(data.native_id){
cb = #{alias}SuccessCbs[data.native_id];
};
if (cb) {
cb(data);
};
}; window.#{alias}Exports.callNative = function(params,callback) {
if(callback){
#{alias}Counter++;
#{alias}SuccessCbs[#{alias}Counter] = callback;
}
try{
params = JSON.parse(params);
}catch(e){
}
window.#{alias}.postMessage(#{alias}Counter,JSON.stringify({
native_id: #{alias}Counter,
params: params
}));
};

#{alias}是为了在一个webview添加多个native实例提供的占位符。后面会处理

window.#{alias}Exports.callNative 方会保存js的回调函数,并且调用native方法!

2、java类处理

接收到js的调用请求后,native处理自己的业务逻辑,首先要注册js回调:

webView.addExtension("native", new MyExtension());
class MyExtension extends Extension {
@Override
@JavascriptInterface
public void postMessage(int instanceId, final String message) {
//
webView.post(new Runnable() {
@Override
public void run() {
try {
JSONObject paramObject = new JSONObject(message);
JSONObject object = new JSONObject();
object.put("native_id", paramObject.getInt("native_id"));
object.put("result", "我是系统返回:" + System.currentTimeMillis());
webView.callJs("native", object);
} catch (Exception e) {
}
}
});
}
}

3、处理回调

若干时间后,指定的业务逻辑处理后,回调js,只需要调用callJs方法即可

     webView.callJs("native", object);
demo:
public void callJs(View v) throws Exception {
JSONObject object = new JSONObject();
object.put("native_id", 1);
object.put("result", "我是系统主动调用:" + System.currentTimeMillis());
webView.callJs("native", object);
}

callJs会调用步骤1保存的回调函数!

这样一个js调用native,native回调js的闭环就完整的实现了。

4、多次回调处理

由于业务需要,可能会出现js调用一次native后,native需要回调多次js方法

此插件完成回调的前提要求是js调用过native。之后再native就可以无限次调用js

具体的实现方式到git上查看,下面贴出CrossWebview代码

package com.cml.framework.crosswebview;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.webkit.WebView; import org.json.JSONObject; import java.util.HashMap;
import java.util.Map; /**
* Created by cmlBeliever on 2016/2/3.
*/
public class CrossWebview extends WebView { private static final String TAG = CrossWebview.class.getSimpleName(); public static final String NATIVE_ID = "native_id"; private Map<String, Extension> extensions = new HashMap<String, Extension>(); public CrossWebview(Context context) {
super(context);
} public CrossWebview(Context context, AttributeSet attrs) {
super(context, attrs);
} public CrossWebview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CrossWebview(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
} public CrossWebview(Context context, AttributeSet attrs, int defStyleAttr, boolean privateBrowsing) {
super(context, attrs, defStyleAttr, privateBrowsing);
} @Override
public void addJavascriptInterface(Object object, String name) {
throw new IllegalStateException("addJavascriptInterface not support! see addExtension !!");
} /**
* 添加拓展功能,多个相同的extension只会保存第一个
*
* @param extension
*/
public void addExtension(String alias, Extension extension) { if (TextUtils.isEmpty(alias)) {
throw new IllegalArgumentException("alias can not be null!!");
} if (extensions.containsKey(alias)) {
return;
}
this.extensions.put(alias, extension);
super.addJavascriptInterface(extension, alias); String baseJs = new JsBuilder(getContext(), alias).build(); loadUrl(baseJs);
} /**
* 调用js的方法
* @param alias
* @param object
*/
public void callJs(String alias, JSONObject object) { if (object.optInt(NATIVE_ID, -1) == -1) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "callJs() : native_id is required!!");
}
return;
} String jsFormat = "javascript:window.%sExtension.callbackJs(%s)";
loadUrl(String.format(jsFormat, alias, object.toString()));
} static class JsBuilder {
private String baseJs;
private String alias; public JsBuilder(Context context, String alias) {
baseJs = context.getString(R.string.js_format);
this.alias = alias;
} public String build() {
baseJs = baseJs.replace("#{alias}", alias);
return "javascript:" + baseJs;
}
} }

最新文章

  1. 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)
  2. php多图上传问题笔记
  3. [原创]Hadoop默认设置导致NameNode启动失败一例
  4. 图片上传预览 (URL.createObjectURL)
  5. 使用开源DocX 生成Word
  6. Node.js与Sails~中间查询语言Waterline
  7. watchdog机制
  8. C/C++程序编译流程(预处理-&gt;编译-&gt;汇编-&gt;链接)
  9. leetcode&ndash;jump game II
  10. git 远程分支创建与推送
  11. 生产都消费者模式的一个demo,消费者设置缓存
  12. sgu128snack
  13. description方法的介绍及重写
  14. 简易RPC框架-过滤器机制
  15. HttpURLConnection发送请求
  16. 解决mapper绑定异常:nested exception is org.apache.ibatis.binding.BindingException:
  17. Linux下 $(cd `dirname $0`;pwd)
  18. [转]深刻理解Python中的元类(metaclass)以及元类实现单例模式
  19. iOS - 引用计数探讨
  20. TFS撤销其他人的迁出

热门文章

  1. 掌握游戏开发中类Message、Handle
  2. ubuntu安装Python3并与Python2自由切换
  3. Deepin15.11-mysql5.7安装与配置
  4. 20199308《Linux内核原理与分析》第十一周作业
  5. 2019-2020-1 20199329《Linux内核原理与分析》第八周作业
  6. java中的Atomic类
  7. js 函数的多图片懒加载(lazy) 带插件版完整解析
  8. 汇编 之 win10 下安装dosbox 和 MASM
  9. 【Linux网络基础】 DNS:介绍、作用、解析原理
  10. jQuery学习(二)