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<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.lxweimin.com/p/9fd80b785de1

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • JSBridge 1. Why do we need JSBridge? 2. Why is “JS”Bridge...
    loveqin閱讀 9,248評論 0 7
  • 在Android中,JSBridge已經不是什么新鮮的事物了,各家的實現方式也略有差異。大多數人都知道WebVie...
    Jannonx閱讀 1,432評論 0 5
  • 為了提高客戶端的開發效率以及手機對h5技術的支持好不夠完美,hybird技術被推上了歷史舞臺。它的基本原理是使用前...
    狐尼克朱迪閱讀 3,790評論 0 10
  • JSBrige系列直通車,由淺入深理解JS-Native的通信過程:JSbridge系列解析(一):JS-Nati...
    zizi192閱讀 5,694評論 0 14
  • 一陣風過,熄滅了眼睛里的星星,傾倒了眼睛里的海。 一陣風過,夢里的白衣少年不在,夕陽稻草,空余一片暮靄。 那時心動...
    大昵昵閱讀 170評論 0 0