天天看點

android jsbridge實作原理簡述

假如生活欺騙了你,假如工作到處不順,請放松心态,提升自己,終有一天你會适應,然後。。。。。。。。。。。。。。。

本篇是面試小問題專欄的開篇文章,由于是面試小問題的解答,是以所有的文章都會力求把問題說明白的同時精簡字數,可能有了解不到位的地方後續會慢慢完善。

h5可以作為移動端跨平台的一種方式,其他方式還有rnjs,flutter,weekx等,android或者ios為了實作和js的互動都提供了原生方法,但都存在一定的問題,這時jsbridge就誕生了。

android原生和js互動方式,調用js,loadurl通用,evaluateJavascript支援回調但都涉及版本問題,js調用原生不同版本又設定到安全問題,雖然新版利用@JavascriptInterface  解決了安全問題但現實中依然有很多低版本。

jsbridge 優點說明:

1 統一android ,ios兩邊的調用方式,使綁定和調用函數接口統一,

2 完美解決安全性問題和回調函數問題。

原理說明:

互動通道:

要實作原生和js互動首先需要一條雙方互動的通道,一般的jsbridge都是利用攔截prompt函數,webView端攔截了js端發送的prompt函數,可以擷取上面的文本。原生執行js還是利用原來的loadurl。

重寫webView和 WebChromeClient,攔截prompt。

public static class WebChromeClientEx extends WebChromeClient {

        public void onProgressChanged(WebView view, int newProgress) {
            super.onProgressChanged(view, newProgress);
        }

        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
            Logger.d("JsBridge","onJsPrompt: " + message);

            try {
                //根據協定進行解析
                JSONObject jsonObject = new JSONObject(message);
                if (JavascriptBridge.matchBridgeProtocol(jsonObject)) {
                    result.confirm("prompt ok");
                    JavascriptBridge.onGetDataFromJs(view, jsonObject);
                } else {
                    result.confirm("data does not match bridge protocol");
                }
            } catch (Exception e) {
                e.printStackTrace();
                result.confirm("exception");
            }

            return true;
        }

        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
            if (view instanceof BaseWebView) {
                SimpleToastHelper.showShortToast(view.getContext(), message);
            }
            result.confirm();
            return true;
        }

        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            if (view instanceof BaseWebView) {
                SimpleToastHelper.showShortToast(view.getContext(), message);
            }
            result.confirm();
            return true;
        }
    }
           

定義協定:

js調用原生,會通過調用prompt函數,webView擷取到prompt傳遞的文本(協定),根據自定義協定解析文本,執行特定原生函數。

js端如何調用原生的函數和回調如何處理:

native(原生)的函數A會被js調用,首先A和實作會被存儲到本地的map中,然後native端回調用js端寫好的注冊函數,在對應的js端生成一個代表原生函數A的js函數B(其實兩者之間沒有必然聯系,不想系統提供的js調用native的方式,沒有注冊對象,這樣就保證了安全性),然後js調用想調用原生A函數時,就調用js函數B,利用prompt把調用原生A的協定傳過去,onjsprompt攔截協定,調用本地A函數。

是以jsbridge都會提供H5端注冊代碼用于生成js的javascriptBridge對象,類似

(function () {

            if (window.bridge) {

                return;

            }

            window.bridge = {};

            var messages = {};

            var call_id = 0;

native函數需要調用register函數,在h5端生成對應的函數           

bridge.register = function (name) {

                if (this[name]) {

                    return;

                }

                this[name] = function (params, callback) {

                    invokeNative(name, params, callback);

                };

            };

            bridge.obtain = function (id) {

                return JSON.stringify(messages[id].params);

            };

            bridge.callback = function (id, data) {

                var message = messages[id];

                if (!message) {

                    return;

                }

                if (message.callback) {

                    message.callback(JSON.parse(data));

                }

                delete messages[id];

            };

       最終實作調用原生的方法  prompt

           function invokeNative (name, params, callback) {

                call_id ++;

                messages[call_id] = {'params' : params, 'callback' : callback};

                if (navigator.userAgent.indexOf('Android') > -1 || navigator.userAgent.indexOf('Adr') > -1) {

                    prompt(JSON.stringify({'cmd':name, 'id':call_id, 'params':params}), '');

                } else {

                    var iframe = document.createElement('iframe');

                    iframe.src = 'bridge://invoke.native.method?handler=' + name + '&id=' + call_id;

                    iframe.style.display = 'none';

                    document.documentElement.appendChild(iframe);

                    setTimeout(function () { document.documentElement.removeChild(iframe); }, 0);

                }

            }

        })();

原生調用js如何實作回調:

可以Native根據協定生成js對象,webView加載網頁時将本地js注入到網頁中,原生調用注入js中的函數,js端接收調用首先調用js函數,然後根據協定調用原生端的對調函數。

jsbridge簡單介紹就到這裡。

繼續閱讀