Android Webview与JS交互之DSbridge源码分析

自从有了Webview与JS交互,而我又使用过并且有了一定的理解,真心的体会到让开发者能在web页相关开发为所欲为。demo地址

Android Webview与JS交互之DSbridge源码分析_第1张图片
image.png

比如,在一个活动页面,需要用户登陆后的userId,才能领取活动页面的奖品,怎么获取呢?这个时候,你可以通过js跳转到登录页,登陆成功带着userId回到活动页,就可以进行下一步动作了,美滋滋~
上面只是个简单的例子,本章的主角是DSbridge,不过,在这之前,我们先回顾下,在没封装的时候,Webview与JS交互的实现。(我之前一直在用的方式)

/**js调用android端的方法:**/
//主要看showLog这个方法
public void initView() { 
 webView.getSettings().setJavaScriptEnabled(true);                                     webView.addJavascriptInterface(new TestEntity(),"test");
}
public classTestEntity { 
 @JavascriptInterface
public void showLog(String data) {
      Log.i("android",data);
  }
}

js调用showLog()方法:

function handleAndroidMethod() {
    test.showLog('hhh');
}

android 调用js 的方法:

//js里面方法
function jsMethod(data) {
....方法实现
}

android这边这样调用:

webview.loadUrl('jsMethod('测试')')
  很好,传统的是如上这样实现,我们回到DSbridge,DSbridge其实就是在这样的交互中封装了一下。那么在分析封装实现之前,我们来看看,DSbridge完成交互中的流程。(还是以分享的例子)

android需要处理的:

public void initView() {
webView= (DWebView) findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.setJavascriptInterface(newJsApiEntity(this));
}
public class JsApiEntity {
private Activity mActivity;
public JsApiEntity(Activity mActivity) {
this.mActivity= mActivity;
}
//for synchronous invocation 同步
@JavascriptInterface
String testSyn(JSONObject jsonObject)throwsJSONException {
return jsonObject.getString("msg") +"[syn call]";
}
//for asynchronous invocation  异步
//分享相关
@JavascriptInterface
void share(JSONObject jsonObject,CompletionHandler handler)throwsJSONException {
      EventBus.getDefault().post(jsonObject,"SHOW_SHARE_DIALOG");
      handler.complete("对应后台里面的flag");//回调数据给H5
           }
}

js那边调用:


很对,上面就是使用DSbridge之后的使用详情了。其实还是挺有意思的,只要调用dsBridge.call()就可以了,参数是之前定义的方法名称(比如 share)。
DSbridge做的事情,就是在js调用的原生代码时候做了封装,每次调用都是用dsBridge.call()来处理,然后具体的就根据参数来区分了。
我们来看看他的主要源码部分 DWebView.java:

void init() {
......
 super.addJavascriptInterface(new Object() {
            int i = 0;

            @JavascriptInterface
            @Keep
            public String call(String methodName, String args) {
                String error = "Js bridge method called, but there is not a JavascriptInterface object, please set JavascriptInterface object first!";
                if(DWebView.this.jsb == null) {
                    Log.e("SynWebView", error);
                    return "";
                } else {
                    Class cls = DWebView.this.jsb.getClass();

                    try {
                        boolean asyn = false;
                        JSONObject arg = new JSONObject(args);
                        final String callback = "";

                        Method e;
                        try {
                            callback = arg.getString("_dscbstub");
                            arg.remove("_dscbstub");
                            e = cls.getDeclaredMethod(methodName, new Class[]{JSONObject.class, CompletionHandler.class});
                            asyn = true;
                        } catch (Exception var12) {
                            e = cls.getDeclaredMethod(methodName, new Class[]{JSONObject.class});
                        }

                        if(e == null) {
                            error = "ERROR! \n Not find method \"" + methodName + "\" implementation! ";
                            Log.e("SynWebView", error);
                            DWebView.this.evaluateJavascript(String.format("alert(decodeURIComponent(\"%s\"})", new Object[]{error}));
                            return "";
                        }

                        JavascriptInterface annotation = (JavascriptInterface)e.getAnnotation(JavascriptInterface.class);
                        if(annotation != null) {
                            e.setAccessible(true);
                            Object ret;
                            if(asyn) {
                                ret = e.invoke(DWebView.this.jsb, new Object[]{arg, new CompletionHandler() {
                                    public void complete(String retValue) {
                                        this.complete(retValue, true);
                                    }

                                    public void complete() {
                                        this.complete("", true);
                                    }

                                    public void setProgressData(String value) {
                                        this.complete(value, false);
                                    }

                                    private void complete(String retValue, boolean complete) {
                                        try {
                                            if(retValue == null) {
                                                retValue = "";
                                            }

                                            retValue = URLEncoder.encode(retValue, "UTF-8").replaceAll("\\+", "%20");
                                            String e = String.format("%s(decodeURIComponent(\"%s\"));", new Object[]{callback, retValue});
                                            if(complete) {
                                                e = e + "delete window." + callback;
                                            }

                                            DWebView.this.evaluateJavascript(e);
                                        } catch (UnsupportedEncodingException var4) {
                                            var4.printStackTrace();
                                        }

                                    }
                                }});
                            } else {
                                ret = e.invoke(DWebView.this.jsb, new Object[]{arg});
                            }

                            if(ret == null) {
                                ret = "";
                            }

                            return ret.toString();
                        }

                        error = "Method " + methodName + " is not invoked, since  it is not declared with JavascriptInterface annotation! ";
                        DWebView.this.evaluateJavascript(String.format("alert(\'ERROR \\n%s\')", new Object[]{error}));
                        Log.e("SynWebView", error);
                    } catch (Exception var13) {
                        DWebView.this.evaluateJavascript(String.format("alert(\'ERROR! \\n调用失败:函数名或参数错误 [%s]\')", new Object[]{var13.getMessage()}));
                        var13.printStackTrace();
                    }

                    return "";
                }
            }

            @JavascriptInterface
            @Keep
            public void returnValue(int id, String value) {
                OnReturnValue handler = (OnReturnValue)DWebView.this.handlerMap.get(Integer.valueOf(id));
                if(handler != null) {
                    handler.onValue(value);
                    DWebView.this.handlerMap.remove(Integer.valueOf(id));
                }

            }

            @JavascriptInterface
            @Keep
            public void init() {
                DWebView.this.injectJs();
            }
        }, "_dsbridge");

......
 public void setJavascriptInterface(Object object) {
        this.jsb = object;
    }
}

很好,我们先看init方法(@Keep,其实就是为了不被混淆,不用在意),从
Class cls = DWebView.this.jsb.getClass()里面一开始就很明显的说出了意图,使用反射来处理,通过e = cls.getDeclaredMethod获取到this.jsb里面的方法,方法区分同步和异步,还有这个call方法是不是很眼熟?public String call(String methodName, String args) 就是js调用原生代码里面用到的,methodName就是需要调用的方法。
我们回到这个jsb ,其实就是通过setJavascriptInterface方法设置进来的,在上面例子里面其实就是我们定义的JsApiEntity对象,现在,通过反射,把需要调用的Method (e)获取到了。接下来肯定是通过注解来调用方法了,我们接着看:

 JavascriptInterface annotation = (JavascriptInterface)e.getAnnotation(JavascriptInterface.class);
   if(annotation != null) {'....''}

使用了@JavascriptInterface注解的,才会进入里面,里面其实就是通过e.invoke来执行js调用的方法了。

本文demo地址:https://github.com/niyige/DSBridgeWebDemo
有什么问题欢迎交流:[email protected]

相关资料:
https://github.com/wendux/DSBridge-Android

你可能感兴趣的:(Android Webview与JS交互之DSbridge源码分析)