JSBridge

作者: 呆呆爬爬 | 来源:发表于2017-04-17 14:48 被阅读0次

    JSBridge概念

    JSBridge顾名思义就是是一座用JavaScript搭建起来的桥,一端是web,一端是native。可以实现web 与Native之间相互调用。


    实现原理

    Native>>Web

    • Android

      webview.loadUrl(“JavaScript:function()”);

    • IOS

      webview.stringByEvaluatingJavaScriptFromString("JavaScript:function()");


    Web>>Native

    Javascript调用Native,并没有现成的API可以直接拿来用,而是需要间接地通过一些方法来实现

    IOS

    UIWebView有个特性:在UIWebView内发起的所有网络请求,都可以通过delegate函数在Native层得到通知。这样,我们就可以在UIWebView内发起一个自定义的网络请求,通常是这样的格式:jsbridge://methodName?param1=value1&m2=value2,于是在UIWebView的delegate函数中,我们只要发现是jsbridge://开头的地址,就不进行内容的加载,转而执行相应的调用逻辑

    func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        print("shouldStartLoadWithRequest")
        let url = request.URL
        let scheme = url?.scheme
        let method = url?.host
        let query = url?.query
    
        if url != nil && scheme == "jsbridge" {
            print("scheme == \(scheme)")
            print("method == \(method)")
            print("query == \(query)")
    
            switch method! {
                case "getData":
                    self.getData()
                case "putData":
                    self.putData()
                case "gotoWebview":
                    self.gotoWebview()
                default:
                    print("default")
            }
    
            return false
        } else {
            return true
        }
    }
    

    Android

    在Android开发中,能实现Js调用Java,有4种方法:

    1. JavascriptInterface
    2. WebViewClient.shouldOverrideUrlLoading();
    3. WebChromeClient.onConsoleMessage();
    4. WebChromeClient.onJsPrompt()
    JavascriptInterface

    这是Android提供的Js与Native通信的官方解决方案。
    首先Java代码要实现这么一个类,它的作用是提供给Js调用。

    public class JavascriptInterface {
    
      @JavascriptInterface
      public void showToast(String toast) {
        Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
      }
    }
    

    然后把这个类添加到WebView的JavascriptInterface中。webView.addJavascriptInterface(new JavascriptInterface(), “javascriptInterface”); 在Js代码中就能直接通过“javascriptInterface”直接调用了该Native的类的方法

    function showToast(toast) {
       javascript:javascriptInterface.showToast(toast);
    }
    

    但是这个官方提供的解决方案在Android4.2之前存在严重的安全漏洞(乌云)。在Android4.2之后,加入了@JavascriptInterface才得到解决。所以考虑到兼容低版本的系统,JavascriptInterface并不适合。

    WebChromeClient.onConsoleMessage()####

    这是Android提供给Js调试在Native代码里面打印日志信息的API,同时这也成了其中一种Js与Native代码通信的方法。在Js代码中调用console.log(‘xxx’)方法。

    console.log('log message that is going to native code')
    

    就会在Native代码的WebChromeClient.consoleMessage()中得到回调。consoleMessage.message()获得的正是Js代码console.log(‘xxx’)的内容。

    public class CustomWebChromeClient extends WebChromeClient {
    
    @Override
    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
        super.onConsoleMessage(consoleMessage);
        String msg = consoleMessage.message();//JavaScript输入的Log内容
      }
    }
    

    WebChromeClient.onJsPrompt()####

    其实除了WebChromeClient.onJsPrompt(),还有WebChromeClient.onJsAlert()和WebChromeClient.onJsConfirm()。顾名思义,这三个Js给Native代码的回调接口的作用分别是展示提示信息,展示警告信息和展示确认信息。鉴于,alert和confirm在Js的使用率很高,所以JSBridge的解决方案中都倾向于选用onJsPrompt()。
    Js中调用

     window.prompt(message, value)
    

    WebChromeClient.onJsPrompt()就会受到回调。onJsPrompt()方法的message参数的值正是Js的方法window.prompt()的message的值。

    public class CustomWebChromeClient extends WebChromeClient {
    
    @Override
    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)        {
        // 处理JS 的调用逻辑
        result.confirm();
        return true;
    }
    

    WebViewClient.shouldOverrideUrlLoading()####

    这个方法的作用是拦截所有WebView的Url跳转。页面可以构造一个特殊格式的Url跳转,shouldOverrideUrlLoading拦截Url后判断其格式,然后Native就能执行自身的逻辑了

    public class CustomWebViewClient extends WebViewClient {
    
      @Override
      public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (isJsBridgeUrl(url)) {
            // JSbridge的处理逻辑
            return true;
        }
        return super.shouldOverrideUrlLoading(view, url);
      }
    }  
    

    前端核心调用

    var url = 'jsbridge://doAction?title=分享标题&desc=分享描述&link=http%3A%2F%2Fwww.baidu.com';  
    var iframe = document.createElement('iframe');  
    iframe.style.width = '1px';  
    iframe.style.height = '1px';  
    iframe.style.display = 'none';  
    iframe.src = url;  
    document.body.appendChild(iframe);  
    setTimeout(function() {  
       iframe.remove();
      }, 100);
    

    通过修改window.location.href也可以达到发起网络请求的效果,但是有一个很严重的问题,就是如果我们连续多次修改window.location.href的值,在Native层只能接收到最后一次请求,前面的请求都会被忽略掉。所以JS端发起网络请求的时候,需要使用iframe,这样就可以避免这个问题

    实现方案

    Android:

    https://github.com/lzyzsd/JsBridge

    https://github.com/papastar/JsBridge(Vanke)

    IOS

    https://github.com/marcuswestin/WebViewJavascriptBridge

    样例代码

     //界面跳转
        webView.registerHandler("route", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                WebViewResult result = new WebViewResult();
                try {
                    //Router.getInstance().openUrl(data);
                    Uri uri = Uri.parse(data);
                    if(TextUtils.equals(uri.getScheme(),"zze")){
                        String lastPath = uri.getLastPathSegment();
                    }
                    result.setCode(WebViewResult.CODE_SUCCESS);
                }catch (Exception e){
                    result.setCode(WebViewResult.CODE_FAIL);
                }finally {
                    function.onCallBack(result.getResult());
                }
                //Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
                //function.onCallBack("submitFromWeb exe, response data 中文 from Java");
            }
    
        });
        //分享
        webView.registerHandler("share", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                WebViewResult result = new WebViewResult();
                try {
                    String url = JSONUtils.getString(data,"title","");
                    String desc = JSONUtils.getString(data,"desc","");
                    String link = JSONUtils.getString(data,"link","");
                    String imgurl = JSONUtils.getString(data,"imgurl","");
                    doShare(url,desc,link,imgurl);
                    result.setCode(WebViewResult.CODE_SUCCESS);
                }catch (Exception e){
                    result.setCode(WebViewResult.CODE_FAIL);
                }finally {
                    function.onCallBack(result.getResult());
                }
            }
    
        });
        //获取数据
        webView.registerHandler("getdata", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                WebViewResult<User> result = new WebViewResult<>();
                try {
                    String type = JSONUtils.getString(data,"type","");
                    if(TextUtils.equals("1",type)){
                        User user = new User();
                        user.name = "Papa";
                        user.phone = "134XXXXXXXX";
                        user.token = "755fgw4twtr1g5gewq";
                        user.userId = "5678995";
                        result.setData(user);
                    }
                    result.setCode(WebViewResult.CODE_SUCCESS);
                }catch (Exception e){
                    result.setCode(WebViewResult.CODE_FAIL);
                }finally {
                    function.onCallBack(result.getResult());
                }
            }
    
        });
    

    前端调用

          //call native method
            window.WebViewJavascriptBridge.callHandler(
                'share'
                , {'title': '分享标题','desc': '分享内容','link': '分享链接','imgurl': '分享图片链接'}
                , function(responseData) {
                    document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
                }
            );
    

    参考文档

    https://juejin.im/entry/573534f82e958a0069b27646

    http://blog.csdn.net/sbsujjbcy/article/details/50752595

    http://tech.youzan.com/jsbridge/

    http://www.jianshu.com/p/9fd80b785de1

    相关文章

      网友评论

          本文标题:JSBridge

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