WebView JS交互 JSBridge 案例 原理 MD


Markdown版本筆記 我的GitHub首頁 我的博客 我的微信 我的郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

WebView JS交互 JSBridge 案例 原理 MD


目錄

簡介

Demo
JsBridge - Android專用
WebViewJavascriptBridge - iOS專用

DSBridge-Android
DSBridge-IOS

PS:
這個庫是專供 Android 端使用的,iOS 端可以使用 WebViewJavascriptBridge,這兩個庫對於前端JS代碼來說是兼容的!

android java and javascript bridge, inspired by wechat webview jsbridge
This project make a bridge between Java and JavaScript.
It provides safe and convenient way to call Java code from js and call js code from java.


JS調用Android有三種方式:

  • webView.addJavascriptInterface()
  • WebViewClient.shouldOverrideUrlLoading()
  • WebChromeClient.onJsAlert()/onJsConfirm()/onJsPrompt() 方法分別回調攔截JS對話框alert()、confirm()、prompt()消息

Android調用JS有兩種方式:

  • webView.loadUrl();
  • webView.evaluateJavascript()

gradle配置

在project的build.gradle中引入jitpack.io

allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
    }
}

在module中添加依賴:

implementation 'com.github.lzyzsd:jsbridge:1.0.4'

Java端:注冊提供給JS端調用的接口

Register a Java handler function so that js can call:

webView.registerHandler("submitFromWeb", new BridgeHandler() {
    @Override
    public void handler(String data, CallBackFunction function) {
        Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
        //do something
        function.onCallBack("submitFromWeb exe, response data from Java"); //回調
    }
});

js can call this Java handler method "submitFromWeb" through:

WebViewJavascriptBridge.callHandler(
    'submitFromWeb'  //方法名
    , {'param': str1}  //參數
    , function(responseData) { //回調
        document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
    }
);

You can set a default handler in Java, so that js can send message to Java without assigned handlerName:

webView.setDefaultHandler(new DefaultHandler());
window.WebViewJavascriptBridge.send(  //沒有方法名
    data
    , function(responseData) {
        document.getElementById("show").innerHTML = "repsonseData from java, data = " + responseData
    }
);

JS端:注冊提供給Java端調用的接口

Register a JavaScript handler function so that Java can call:

WebViewJavascriptBridge.registerHandler("functionInJs", function(data, responseCallback) {
    document.getElementById("show").innerHTML = ("data from Java: = " + data);
    var responseData = "Javascript Says Right back aka!";
    responseCallback(responseData);
});

Java can call this js handler function "functionInJs" through:

webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
    @Override
    public void onCallBack(String data) {
    }
});

You can also define a default handler use init method, so that Java can send message to js without assigned handlerName:
for example:

bridge.init(function(message, responseCallback) {
    console.log('JS got a message', message);
    var data = {
        'Javascript Responds': 'Wee!'
    };
    console.log('JS responding with', data);
    responseCallback(data);
});

when Java call:

webView.send("hello");

will print 'JS got a message hello' and 'JS responding with' in webview console.

JS 端注意事項

This lib will inject注入 a WebViewJavascriptBridge Object to window object. So in your js, before use WebViewJavascriptBridge, you must detect檢測 if WebViewJavascriptBridge exist. If WebViewJavascriptBridge does not exit, you can listen to WebViewJavascriptBridgeReady event, as the blow code shows:

if (window.WebViewJavascriptBridge) {
    //do your work here
} else {
    document.addEventListener(
        'WebViewJavascriptBridgeReady'
        , function() {
            //do your work here
        },
        false
    );
}

源碼解析

主要有三點:

  • Android調用JS是通過loadUrl(url),url中可以拼接要傳給JS的對象
  • JS調用Android是通過shouldOverrideUrlLoading
  • JsBridge將溝通數據封裝成Message,然后放進Queue,再將Queue進行傳輸

BridgeWebView

首先自定義了一個 WebView:

public class BridgeWebView extends WebView implements WebViewJavascriptBridge

繼承的是系統自帶的 WebView,鑒於很多人會使用騰訊的 X5 WebView,如果想保持使用 X5 的話,我們其實重新定義一個 WebView 並將其繼承的 WebView 改成 X5 的就行了。

里面定義了兩個集合,一個用於在調用 registerHandler 時保存Java中注冊的 方法名 和 BridgeHandler 的映射,一個用於在調用 send 時保存 Java 向 JS 發送的請求和 CallBackFunction 的映射:

Map<String, BridgeHandler> messageHandlers = new HashMap<>();//供js調用
Map<String, CallBackFunction> responseCallbacks = new HashMap<>();//調用js

然后是一些初始化操作:

getSettings().setJavaScriptEnabled(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
   WebView.setWebContentsDebuggingEnabled(true);
}
setWebViewClient(new BridgeWebViewClient(this));

這里定義了一個 BridgeWebViewClient,里面並沒有什么復雜的邏輯,僅僅是重寫了 WebViewClient 的 shouldOverrideUrlLoading 方法和 onPageFinished 方法。我們先看一下 onPageFinished 方法,shouldOverrideUrlLoading 后面我們用到時再分析。

onPageFinished 主要用於在頁面加載后加載 js 文件,以及調用一些需要在頁面加載后才能調用的 js 邏輯:

public static final String toLoadJs = "WebViewJavascriptBridge.js";

@Override
public void onPageFinished(WebView view, String url) {
    super.onPageFinished(view, url);
    String jsContent = assetFile2Str(view.getContext(), toLoadJs);
    view.loadUrl("javascript:" + jsContent); //加載 JS 代碼
    if (webView.getStartupMessage() != null) {
        for (Message m : webView.getStartupMessage()) {
            webView.dispatchMessage(m);//需要在頁面加載完執行的邏輯
        }
        webView.setStartupMessage(null);//清空這些任務
    }
}

java 調用 js 中注冊的方法

調用過程

接口 WebViewJavascriptBridge 其實就是讓我們調用 js 中注冊的方法的:

public interface WebViewJavascriptBridge {
    void send(String data);
    void send(String data, CallBackFunction responseCallback);
}

鑒於 js 和 java 交互無非這兩種方式:webview.addJavascriptInterfaceWebViewClient.shouldOverrideUrlLoading,所以其實不用看源碼我們也知道,其最終肯定是通過兩者中的一種來實現的。

我們大致瞅一下其調用過程:

send(String data) -> send(data, null);
send(String data, CallBackFunction responseCallback) -> doSend(null, data, responseCallback);

兩者都會調用 doSend 方法,這個方法其實主要就是將消息相關信息封裝到 Message 中:

private void doSend(String handlerName, String data, CallBackFunction responseCallback) {
      Message m = new Message();
      if (!TextUtils.isEmpty(handlerName)) m.setHandlerName(handlerName);
      if (!TextUtils.isEmpty(data)) m.setData(data);
      if (responseCallback != null) {
            //為此次調用定義一個唯一的回調id,例如【JAVA_CB_1_541】【JAVA_CB_2_455】
            String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT, ++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));
            responseCallbacks.put(callbackStr, responseCallback);//將回調id和對應的回調方法保存起來
            m.setCallbackId(callbackStr);//將回調id賦給此消息,此id是實現交互的最關鍵的信息
            //之后,當具有同一id的消息通過 shouldOverrideUrlLoading 回調給我們時,我們就可以找到並執行這里定義的回調方法
      }
      queueMessage(m);
}

這里用到的 Message 是庫中定義的一個非常簡單的數據結構:

public class Message{
    private String callbackId; //callbackId
    private String responseId; //responseId
    private String responseData; //responseData
    private String data; //data of message
    private String handlerName; //name of handler

    private final static String CALLBACK_ID_STR = "callbackId";
    private final static String RESPONSE_ID_STR = "responseId";
    private final static String RESPONSE_DATA_STR = "responseData";
    private final static String DATA_STR = "data";
    private final static String HANDLER_NAME_STR = "handlerName";
}

接下來會做一個簡單的判斷,以決定是立即調用還是在頁面加載完成后調用:

private void queueMessage(Message m) {
      if (startupMessage != null) startupMessage.add(m); //在 onPageFinished 后調用
      else dispatchMessage(m); //立即調用(默認操作)
}

下面是最核心的部分之一了,簡單來說就是,經過一系列操作后,最終是通過 loadUrl 來實現原生調用 js 的方法的:

void dispatchMessage(Message m) {
      String messageJson = m.toJson(); //以Json格式通訊
      messageJson = messageJson.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");//轉義特殊字符
      messageJson = messageJson.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");//轉義特殊字符
      String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);//調用的js命令

      if (Thread.currentThread() == Looper.getMainLooper().getThread()) { //必須在主線程中調用
            this.loadUrl(javascriptCommand); //最核心的部分,原理就是通過 loadUrl 調用 js 方法
      }
}

js命令的全部內容例如下,其中包含的內容有:唯一的callbackId、js中定義的方法名handlerName、傳遞過去的數據data

javascript:WebViewJavascriptBridge._handleMessageFromNative('{\"callbackId\":\"JAVA_CB_1_541\",\"data\":\"【包青天20094】\",\"handlerName\":\"showInHtml\"}');

至此,整個調用過程結束了。

回調過程

經過上面的操作,我們已經調用了 js 端的代碼了,下面我們探究在調用時我們注冊的回調方法是怎么被調用的。

我們不需要關心 js 那端的邏輯是怎么樣的,因為這完全是前端同學需要關注的事情,我們只關注 js 調用之后是怎么和我們原生交互的。

前面我們已經說過了,js和java交互只有兩種方式:webview.addJavascriptInterfaceWebViewClient.shouldOverrideUrlLoading,這個庫采用的就是重寫 shouldOverrideUrlLoading 這種方式。

我們首先看一下 shouldOverrideUrlLoading 這個方法,里面主要是根據回調 url 做不同的處理:

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(BridgeUtil.YY_RETURN_DATA)) {//格式為【yy://return/{function}/returncontent】
        webView.handlerReturnData(url);
        return true;
    } else if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) { //格式為【yy://】
        webView.flushMessageQueue();
        return true;
    } else return super.shouldOverrideUrlLoading(view, url); //不做任何處理
}

上面的案例中,我們回調的 url 為yy://__QUEUE_MESSAGE__/,所以會調用flushMessageQueue方法,從方法名來看,其作用應該是"刷新消息隊列"的,我們先看看其基本結構:

void flushMessageQueue() {
   if (Thread.currentThread() == Looper.getMainLooper().getThread()) { //主線程的調用才處理
       //JS代碼為【"javascript:WebViewJavascriptBridge._fetchQueue();"】
      loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {
         @Override
         public void onCallBack(String data) { //處理調用后的回調
          //...
      });
   }
}
public void loadUrl(String jsUrl, CallBackFunction returnCallback) {
   this.loadUrl(jsUrl);//調用 js 代碼
   responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback); //處理后的 id 為【_fetchQueue】
}

調用完 loadUrl 之后集合 responseCallbacks 中就有兩個元素了。既然又調用了一次 loadUrl,那我們就繼續追蹤shouldOverrideUrlLoading。這一次回調的 url 為:

yy://return/_fetchQueue/[{"responseId":"JAVA_CB_1_541","responseData":"【你好,我是白乾濤,這是JS名為showInHtml的Handler返回的數據】"}]

所以就會調用handlerReturnData這個方法,這個方法是用於將返回的數據回調給 CallBackFunction 的,邏輯非常簡單:

void handlerReturnData(String url) {
   String functionName = BridgeUtil.getFunctionFromReturnUrl(url); //固定為【_fetchQueue】
   CallBackFunction f = responseCallbacks.get(functionName); //找到對應的 CallBackFunction
   String data = BridgeUtil.getDataFromReturnUrl(url); //拿到 js 返回的數據
   if (f != null) {
      f.onCallBack(data); //將返回的數據回調給 CallBackFunction
      responseCallbacks.remove(functionName);//移除此 CallBackFunction
      return;
   }
}

解析后,這里的:

  • functionName 為 _fetchQueue
  • CallBackFunction 為上面我們在調用 flushMessageQueue 時定義的回調方法
  • 返回的 data 為 [{"responseId":"JAVA_CB_1_541","responseData":"【你好,我是白乾濤,這是JS名為showInHtml的Handler返回的數據】"}],可以看到這個 data 其實是一個 JSONArray

所以接下來就進入其 onCallBack 中了,這個方法里面的代碼稍多一點點,大致邏輯為:

List<Message> list = Message.toArrayList(data); //解析返回的數據
for (int i = 0; i < list.size(); i++) { //遍歷這個集合,本例中只有id為 JAVA_CB_1_541 的元素
    if (!TextUtils.isEmpty(responseId) { //如果消息的responseId不為空
        function.onCallBack(responseData); //【核心】,調用相應的回調方法
        responseCallbacks.remove(responseId); //執行完就移除掉
    }else{ //這里面的邏輯是在js主動調用原生方法時才會執行的,我們后面再分析
    }
}

完成以后,所有的回調都執行了,集合 responseCallbacks 中的所有元素也都移除了。至此整個回調過程也結束了。

一個疑惑

通過上面的流程分析,我們知道,在我們首次 loadUrl 后的 shouldOverrideUrlLoading 回調中,我們首先是進入 flushMessageQueue 方法里面又 load 了一段固定的 js 代碼,然后再在 shouldOverrideUrlLoading 回調中獲取 js 返回的數據以及執行回調,為什么要這么搞呢,明明一次交互就可以了的?

js 調用 java 中注冊的方法

這里的大多數流程和上面的基本是一致的,下面我們只簡單的過一下:

  • 首先肯定還是回調 shouldOverrideUrlLoading ,此時回調的url為固定的yy://__QUEUE_MESSAGE__/
  • 接着調用 flushMessageQueue,然后又調用一次 loadUrl,消息 id 同樣為_fetchQueue
  • 然后會再次回調 shouldOverrideUrlLoading ,此時的url為:
yy://return/_fetchQueue/[{"handlerName":"showTips","data":{"type":"dialog","title":"我是標題","msg":""},"callbackId":"cb_3_1540110644942"}]
  • 所以會進入 handlerReturnData,接着會回調flushMessageQueue中的 onCallBack方法,接着就是遍歷集合
  • 目前集合中只有一個元素,由於 responseId 為空,所以會走和上面分析的 java 調 js 不同的分支

我們着重分析此時要走的邏輯:

CallBackFunction responseFunction = null; //臨時定義一個回調
final String callbackId = m.getCallbackId();
if (!TextUtils.isEmpty(callbackId)) { //有 callbackId 時的回調是繼續load一個js代碼
   responseFunction = new CallBackFunction() {
      @Override
      public void onCallBack(String data) {
         Message responseMsg = new Message();
         responseMsg.setResponseId(callbackId); //將 callbackId作為 responseId
         responseMsg.setResponseData(data);
         queueMessage(responseMsg);
      }
   };
} else { //沒有 callbackId 時的回調是一個空實現(正常是不會走到這里的)
   responseFunction = data -> { };
}
BridgeHandler handler = TextUtils.isEmpty(m.getHandlerName() ? defaultHandler : messageHandlers.get(m.getHandlerName());
if (handler != null) handler.handler(m.getData(), responseFunction); //調用我們定義的 handler

里面首先是定義了一個 CallBackFunction 回調,然后會調用我們定義的 BridgeHandlerhandler 方法,例如下面是本案例中我寫的部分邏輯:

@Override
public void handler(String data, CallBackFunction function) {
    //...顯示Toast或彈Dialog或打印Log
    function.onCallBack(...); //返回數據
}

接下來就會走我們上面定義的 responseFunction 的 onCallBack 方法,接着會走 queueMessage 方法,和上面的流程一樣,我們接着會走 dispatchMessage,然后調用 loadUrl 調用 js 的方法的,此時的 js 命令為:

javascript:WebViewJavascriptBridge._handleMessageFromNative('{\"responseData\":\"{\\\"status\\\":\\\"success\\\",\\\"time\\\":0}\",\"responseId\":\"cb_2_1540112210451\"}');

至此,便完整實現了 js 調用原生方法,並在原生響應 js 的命令后回調數據給 js。

一個完整的Demo

MainActivity

public class MainActivity extends ListActivity {
    private BridgeWebView mBridgeWebView;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBridgeWebView = new BridgeWebView(this);
        registerHandler();
        mBridgeWebView.loadUrl("file:///android_asset/jsbridge.html");
        getListView().addFooterView(mBridgeWebView);

        String[] array = {"調用JS中的名為showInHtml的Handler",
                "調用JS中的默認Handler,沒有回調",
                "調用JS中的默認Handler,有回調",};
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new ArrayList<>(Arrays.asList(array))));
    }

    private void registerHandler() {
        mBridgeWebView.setDefaultHandler(new DefaultBridgeHandler());//默認的Handle
        mBridgeWebView.registerHandler(JBNativeIDs.BRIDGE_ID_DEVICEINFO, new DeviceInfoBridgeHandler());//獲取設備信息
        mBridgeWebView.registerHandler(JBNativeIDs.BRIDGE_ID_BACKNATIVE, new BackNativeBridgeHandler(this));//結束當前Activity
        mBridgeWebView.registerHandler(JBNativeIDs.BRIDGE_ID_SHOWTIPS, new ShowTipsBridgeHandler(this));//顯示原生toast或彈窗
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        switch (position) {
            case 0:
                mBridgeWebView.callHandler(JBJsIDs.BRIDGE_ID_SHOWINHTML, "【包青天20094】", new SimpleCallBackFunction());
                break;
            case 1:
                mBridgeWebView.send("【包青天20095】");
                break;
            case 2:
                mBridgeWebView.send("【包青天20096】", new SimpleCallBackFunction());
                break;
        }
    }

    class SimpleCallBackFunction implements CallBackFunction {
        @Override
        public void onCallBack(String data) {
            Log.i("bqt", "【JS回調】,是否在主線程:" + (Looper.myLooper() == Looper.getMainLooper()) + "\n" + data);
            //也可以用Thread.currentThread() == Looper.getMainLooper().getThread()
        }
    }
}

注冊的BridgeHandler

public class DeviceInfoBridgeHandler implements BridgeHandler {
    /**
     * @param data js調用原生所傳遞的參數
     * @param function js調用原生所傳遞的回調接口
     */
    @Override
    public void handler(String data, final CallBackFunction function) {
        Log.i("bqt", "【獲取設備信息,參數】" + data);

        new Thread(() -> {
            SystemClock.sleep(2000);//模擬耗時操作
            DeviceInfo deviceInfo = new DeviceInfo("小米6", "8.0");
            JBRespBean bridgeRespBean = JBRespBean.newBuilder()
                    .status(JBRespBean.STATUS_SUCCESS)
                    .time(System.currentTimeMillis())
                    .data(deviceInfo)
                    .build();

            final String response = new Gson().toJson(bridgeRespBean);
            Log.i("bqt", "【給JS的響應】" + response);

            new Handler(Looper.getMainLooper()).post(() -> {
                function.onCallBack(response);//必須在主線程中回調給JS,因為JS中加載網頁也是UI操作,也必須在主線程中
            });
        }).start();
    }

    static class DeviceInfo {
        String phoneName;
        String sysVersion;

        DeviceInfo(String phoneName, String sysVersion) {
            this.phoneName = phoneName;
            this.sysVersion = sysVersion;
        }
    }
}

HTML+JS源碼

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>JSBridge測試頁面</title>
</head>
<body>
<p>
    <!--xmp標簽可以在頁面上輸出html代碼-->
    <xmp id="show"></xmp>
</p>
<p><input type="text" id="inputMsg" value=""/></p>
<p><input type="button" id="button1" value="獲取設備信息" onclick="deviceInfo();"/></p>
<p><input type="button" id="button2" value="顯示原生dialog" onclick="showTips();"/></p>
<p><input type="button" id="button3" value="結束當前Activity" onclick="backNative();"/></p>
<p><input type="button" id="button4" value="使用默認的Handle" onclick="defaultHandler();"/></p>
<p><input type="button" id="button5" value="顯示html源碼" onclick="showHtml();"/></p>
<p>
    <xmp id="htmlContent"></xmp>
</p>
</body>
<script>
        //******************************************************************************************
        // 點擊事件
        //******************************************************************************************
        function deviceInfo() {
            var inputMsg = document.getElementById("inputMsg").value;
            var data = {
                content: inputMsg
            };
            window.WebViewJavascriptBridge.callHandler("deviceInfo",
                data,
                function(responseData) {
                    document.getElementById("show").innerHTML = "【返回數據】" + responseData
                }
            );
        }
        function showTips() {
            var inputMsg = document.getElementById("inputMsg").value;
            var data = {
                type: "dialog",
                title: "我是標題",
                msg: inputMsg
            };
            window.WebViewJavascriptBridge.callHandler('showTips',
                data,
                function(responseData) {
                    document.getElementById("show").innerHTML = "【返回數據】 " + responseData
                }
            );
        }
        function backNative() {
            window.WebViewJavascriptBridge.callHandler('backNative',
                null,
                function(responseData) {
                    console.log("【此時Activity關閉了,不要再操作UI了】" + responseData);
                }
            );
        }
        function defaultHandler() {
            var data = {
                type: "toast",
                msg: "使用默認的Handle處理"
            };
            window.WebViewJavascriptBridge.send(data,
                function(responseData) {
                    window.WebViewJavascriptBridge.callHandler('showTips', data, null); //顯示原生toast
                }
            );
        }
        function showHtml() {
            document.getElementById("htmlContent").innerHTML = document.getElementsByTagName("html")[0].innerHTML;
        }
        //******************************************************************************************
        // JS端注冊提供給Java端調用的接口
        //******************************************************************************************
        function bridgeLog(logContent) {
            document.getElementById("show").innerHTML = logContent;
        }
        // 調用注冊方法
        function connectWebViewJavascriptBridge(callback) {
            if (window.WebViewJavascriptBridge) {
                callback(WebViewJavascriptBridge)
            } else {
                document.addEventListener(
                    'WebViewJavascriptBridgeReady',
                    function() {
                        callback(WebViewJavascriptBridge)
                    },
                    false
                );
            }
        }
        connectWebViewJavascriptBridge(function(bridge) {
            //注冊默認的Handler
            bridge.init(function(data, responseCallback) {
                document.getElementById("show").innerHTML = ("【原生傳過來的數據】" + data);
                if (responseCallback) { //這行代碼意思是:如果有responseCallback這個方法,就執行以下邏輯
                    responseCallback("【你好,我是白乾濤,這是JS默認的Handler返回的數據】");
                }
            });
            //注冊供原生調用的Handler
            bridge.registerHandler("showInHtml", function(data, responseCallback) {
                document.getElementById("show").innerHTML = ("【原生傳過來的數據】" + data);
                if (responseCallback) {
                    responseCallback("【你好,我是白乾濤,這是JS名為showInHtml的Handler返回的數據】");
                }
            });
        })
</script>
</html>

DSBridge 簡介

DSBridge-IOS
DSBridge-Android

三端易用的現代跨平台 Javascript bridge, 通過它,你可以在Javascript和原生之間同步或異步的調用彼此的函數.

特性

  • 跨平台:同時支持ios/android
  • 支持同步、異步調
  • 雙向調用:js可以調用native, native可以調用js。
  • 三端易用:三端指ios 、android和前端
  • 支持進度回調:一次調用,多次返回
  • Android支持騰訊X5內核
  • 支持以類的方式集中統一管理API
  • 支持API命名空間
  • 支持調試模式
  • 支持API存在性檢測
  • 支持Javascript關閉頁面事件回調
  • 支持Javascript 模態/非模態對話框

DSBridge 官方是同時支持 ios/android 的,WebViewJavascriptBridge 官方說明是支持 ios/osx 的,但 並不支持android , 當然,由於 WebViewJavascriptBridge 的人氣 實在太高,也有一些人在 android 上實現了兼容的版本,如 gzsll/WebViewJavascriptBridge,但是總的來說,並非一家之作,這可能會給日后維護帶來問題。

到目前為止,據作者所知,跨平台的 js bridge 中,DSBridge 是唯一一個支持同步調用的!

使用

1、添加依賴

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}
implementation 'com.github.wendux:DSBridge-Android:3.0-SNAPSHOT'
//或
implementation 'com.github.wendux:DSBridge-Android:x5-3.0-SNAPSHOT' //支持X5內核

2、原生定義供JS調用的API

public class JsApi {
    @JavascriptInterface
    public String testSyn(Object msg) {
        return msg + "[syn call]"; //同步API
    }

    @JavascriptInterface
    public void testAsyn(Object msg, CompletionHandler<String> handler) {
        handler.complete(msg + " [ asyn call]"); //異步API
    }
}

3、JS定義供原生調用的API

//cdn方式引入初始化代碼(中國地區慢,建議下載到本地工程)
//<script src="https://unpkg.com/dsbridge@3.1.3/dist/dsbridge.js" />

//npm方式安裝初始化代碼
//npm install dsbridge@3.1.3

var dsBridge=require("dsbridge")

//注冊 javascript API
dsBridge.register('addValue',function(l,r){
   return l+r;
})

4、JS調用原生API

var str=dsBridge.call("testSyn","testSyn"); //同步調用
dsBridge.call("testAsyn","testAsyn", function (v) {
  alert(v); //異步調用
})

5、原生調用JS API

DWebView dwebView= (DWebView) findViewById(R.id.dwebview);
dwebView.addJavascriptObject(new JsApi(), null); //Object object, String namespace
dwebView.callHandler("addValue", new Object[]{3, 4}, new OnReturnValue<Integer>() {
    @Override
    public void onValue(Integer retValue) {
        Log.d("jsbridge", "call succeed, return value is " + retValue);
    }
});

2018-5-8


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM