Js调用Android回调处理

通常在混合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
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值