IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> webview_jsbridge源码分析及优化 -> 正文阅读

[移动开发]webview_jsbridge源码分析及优化

webview_jsbridge是native与H5交互的三方库,在github上面已有13K的start。

源码分析

H5发送消息给Android

const setupWebViewJavascriptBridge = function (e) {
  console.log("window.WebViewJavascriptBridge", window.WebViewJavascriptBridge);
  if (window.WebViewJavascriptBridge) return e(WebViewJavascriptBridge);
  if (window.WVJBCallbacks) return window.WVJBCallbacks.push(e);
  window.WVJBCallbacks = [e];
  var i = document.createElement("iframe");
  i.style.display = "none";
  i.src = "https://__bridge_loaded__";
  document.documentElement.appendChild(i);
  setTimeout(function () {
    document.documentElement.removeChild(i);
  }, 0);
};

// H5调用代码
setupWebViewJavascriptBridge(function (bridge) {
  bridge.callHandler() // 主动发送消息给原生
});

1.setupWebViewJavascriptBridge方法中做了三件事:

? ? ? ? 如果window.WebViewJavascriptBridge存在,则执行外部传入的方法e,并将WebViewJavascriptBridge作为参数传入。

? ? ? ? 如果window.WVJBCallbacks存在,则将外部传入的方法e存入WVJBCallbacks队列中;否之不存在,则为window.WVJBCallbacks赋值,并将方法e存入WVJBCallbacks队列中。

? ? ? ? 创建iframe,并设置display = “none”隐藏起来,设置src,最后通过宏任务删除掉。

2.H5最终调用的是window.WebViewJavascriptBridge.callHandler()方法。

看完后,我们先记录下问题点

1.window.WebViewJavascriptBridge在哪里创建,callHandler具体执行了啥。

2.window.WVJBCallbacks队列对象的作用。

3.iframe的创建与摧毁有什么用。

下面接着看...

// Android
private WebChromeClient mWebChromeClient = new WebChromeClient() {

        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            // 当进度大于80%的时候 加载WebViewJavascriptBridge.js
            if (newProgress > 80) {
                try {
                    InputStream is = view.getContext().getAssets()
                            .open("WebViewJavascriptBridge.js");
                    int size = is.available();
                    byte[] buffer = new byte[size];
                    is.read(buffer);
                    is.close();
                    String js = new String(buffer);
                    evaluateJavascript(js); // 执行js
                } catch (IOException e) {
                    e.printStackTrace();
                }

                ...
            }
       }
}

// WebViewJavascriptBridge.js
(function () {
    if (window.WebViewJavascriptBridge) {
        return;
    }
    ...

    // H5发送消息给Android
    function _doSend(message, responseCallback) {
        if (responseCallback) {
            var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
            responseCallbacks[callbackId] = responseCallback;
            message['callbackId'] = callbackId;
        }
        var msg=JSON.stringify(message || {});
        if(window.WVJBInterface){
           WVJBInterface.notice(msg);
        }else{
          prompt("_wvjbxx",msg);
        }
    }

    // window.WebViewJavascriptBridge对象
    var bridge = {
   
        callHandler: function (handlerName, data, responseCallback) {
            if (arguments.length == 2 && typeof data == 'function') {
                responseCallback = data;
                data = null;
            }
            _doSend({
                handlerName: handlerName,
                data: data
            }, responseCallback);
        },
       ...
    };
    // 赋值
    window.WebViewJavascriptBridge = bridge;

    ...

    var callbacks = window.WVJBCallbacks;
    delete window.WVJBCallbacks;
    // 循环调用
    if (callbacks) {
        for (var i = 0; i < callbacks.length; i++) {
            callbacks[i](bridge);
        }
    }
    

})();

1.WebChromeClient.onProgressChanged()是监听webview进度大于80%的时候,执行本地文件WebViewJavascriptBridge.js。

2.解析WebViewJavascriptBridge.js文件

? ? ? ? 判断window.WebViewJavascriptBridge是否存在,存在则return,保证对象的唯一性。

? ? ? ? 定义bridge对象,并赋值给window.WebViewJavascriptBridge。

? ? ? ? 遍历window.WVJBCallbacks队列,依次执行里面的方法,并删除window.WVJBCallbacks。

3._doSend解析

? ? ? ? 判断responseCallback是否存在,存在则定义唯一callbackId,并存入到message,同时将callbackId与responseCallback存入到responseCallbacks对象中。

? ? ? ? 判断window.WVJBInterface是否存在,存在则执行WVJBInterface.notice方法,不存在则执行prompt("_wvjbxx",msg)。这两种方法是Android WebView提供的两种原生的H5交互方法。

? ? ? ?

上面的代码分析完后,基本能解答最开始提出的那前两个问题了

1.window.WebViewJavascriptBridge在哪里创建,callHandler具体执行了啥。

在webview加载H5进度大于80%的时候会创建window.WebViewJavascriptBridge对象。

callHandler实际调用的是bridge对象的callHandler,最终会执行_doSend方法,讲消息发送给原生

2.window.WVJBCallbacks队列对象的作用。

在window.WebViewJavascriptBridge还未创建时,执行setupWebViewJavascriptBridge会将回调e存入到window.WVJBCallbacks队列中,最后在创建WebViewJavascriptBridge对象时,遍历WVJBCallbacks队列,取出e然后执行,起到了缓存的作用。

?

已经将消息发送到原生了,现在我们再看看原生是如何处理,以及如何将回调发送了H5?

// 注册WVJBInterface
super.addJavascriptInterface(new Object() {
 @Keep
 @JavascriptInterface
 public void notice(String info) {
     Message msg = mainThreadHandler.obtainMessage(HANDLE_MESSAGE, info);
     mainThreadHandler.sendMessage(msg);
 }               

},“WVJBInterface”);


@Override
public void handleMessage(Message msg) {
    final Context context = mContextReference.get();
     if (context != null) {
       switch (msg.what) {
          ...        
       case HANDLE_MESSAGE:
          WVJBWebView.this.handleMessage((String) msg.obj);
          break;
        }
     }
}

private void handleMessage(String info) {
    try {
            JSONObject jo = new JSONObject(info);
            WVJBMessage message = JSONObject2WVJBMessage(jo);
            if (message.responseId != null) {
                WVJBResponseCallback responseCallback = responseCallbacks
                        .remove(message.responseId);
                if (responseCallback != null) {
                    responseCallback.onResult(message.responseData);
                }
            } else {
                WVJBResponseCallback responseCallback = null;
                if (message.callbackId != null) {
                    final String callbackId = message.callbackId;
                    responseCallback = new WVJBResponseCallback() {
                        @Override
                        public void onResult(Object data) {
                            WVJBMessage msg = new WVJBMessage();
                            msg.responseId = callbackId;
                            msg.responseData = data;
                            dispatchMessage(msg);
                        }
                    };
                }

                if (messageHandler != null) {
                    messageHandler.get(message.handleName).handler(message.data, responseCallback);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }        
}



private void dispatchMessage(WVJBMessage message) {
        String messageJSON = message2JSONObject(message).toString();
        evaluateJavascript(String.format("WebViewJavascriptBridge._handleMessageFromJava(%s)", messageJSON));
}

1.上面分析可以知道,H5发送消息最终会调用WVJBInterface.notice(),实际上调用webview注册的WVJBInterface的notice()方法,通过handler由子线程向主线程发送消息,最终会调用handleMessage(message)。

2.handleMessage解析

? ? ? ? 判断message是否存在responseId和callbackId,分别进行处理,上面分析可以知道,message中携带了callbackId,所以会执行callbackId的处理。

? ? ? ? 创建回调对象responseCallback,并通过messageHandler.get(message.handleName)发送到上层业务处理,当上层业务调用responseCallback.onResult回调方法时,会将data对象封装成message,并设置responseId=callbackId和responseData=data,通过dispatchMessage方法发送出去。

3.dispatchMessage解析

? ? ? ? 通过webview系统方法,调用H5的WebViewJavascriptBridge._handleMessageFromJava方法,并将数据发送出去。

又到了H5部分,接着往下看...

// WebViewJavascriptBridge.js

  var bridge = {
        registerHandler:..., 

        callHandler:...,
      
        _handleMessageFromJava: function (messageJSON) {
            _dispatchMessageFromJava(messageJSON);
        },
       
    };

function _dispatchMessageFromJava(message) {
        var messageHandler;
        var responseCallback;
        if (message.responseId) {
            responseCallback = responseCallbacks[message.responseId];
            if (!responseCallback) {
                return;
            }
            responseCallback(message.responseData);
            delete responseCallbacks[message.responseId];
        } else {
            if (message.callbackId) {
                var callbackResponseId = message.callbackId;
                responseCallback = function (responseData) {
                    _doSend({
                        handlerName: message.handlerName,
                        responseId: callbackResponseId,
                        responseData: responseData
                    });
                };
            }
            var handler = messageHandlers[message.handlerName];
            if (!handler) {
                console.log("WebViewJavascriptBridge: WARNING: no handler for message from java", message);
            } else {
                handler(message.data, responseCallback);
            }
        }
    }

1.我们在bridge中找到了_handleMessageFromJava方法,Android发送消息会执行到这个方法,最终会执行_dispatchMessageFromJava方法。

2._dispatchMessageFromJava解析

????????判断message是否存在responseId和callbackId,分别进行处理,上面分析可以知道,message中携带了responseId和responseData,并且responseId=callbackId。

? ? ? ? 从responseCallbacks对象通过responseId取出responseCallback,将responseData作为参数进行执行。

由H5发送消息到Android,Android接收到消息后并将消息回调给H5的链路基本走通了。

????????

????????

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-10 13:32:03  更:2021-08-10 13:33:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 1:04:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码