美文网首页
Android Webview与JS交互之DSbridge源码分

Android Webview与JS交互之DSbridge源码分

作者: 放码过来吧 | 来源:发表于2017-11-04 16:24 被阅读292次

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

    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那边调用:

    <script>
        var title = "我是标题";
        var content = "我是内容";
        var url = "落地页的链接";
        function share() {
            dsBridge.call("share",  {title: title,content:content,
                imageUrl: "图片链接哟",url:url}, function(flag){alert(flag);})
        }
    </script>
    

    很对,上面就是使用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
    有什么问题欢迎交流:893007592@qq.com

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

    相关文章

      网友评论

          本文标题:Android Webview与JS交互之DSbridge源码分

          本文链接:https://www.haomeiwen.com/subject/ohzfmxtx.html