JSBridge

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 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)