Android JsBridge 原理解析

JsBridge 是 Android 中 WebView 与 Javascript 互相调用的一个库,github 地址 为 https://github.com/lzyzsd/JsBridge。

本文首先会介绍 JsBridge 中 Javascript 与 Java 通信的原理,其后会使用,最后具体介绍 JsBridge 项目的实现原理

一:JsBridge 中 Javascript 与 Java 通信的原理
1.webViewClient.shouldOverrideUrlLoading()

在加载一个新的 url 连接时的一个回调。目的是给宿主应用一个机会是否拦截该新的 url 连接请求。 如果返回 true,表示应用处理了该 url,返回false 则意味着将 url 请求交给当前的 WebView 来处理。

2. WebView 获取 Javascript 返回的内容

JsBridge 通过重载该方法,判断 WebView 加载的 url 中是否包含有与 JS 约定的 schema。Java 端正是通过解析 schema 拿到
Javascript 返回给我们的内容

3. Javascript 获取 WebView 的内容

webView.loadUr() 方法可以执行 JS代码。故我们可以通过该方式执行 JS 声明的方法。传入我们需要传递的数据。

比如:‘javascript:WebViewJavascriptBridge._handleMessageFromNative('%s');’

4. Javascript 刷新 webView 页面

html 中 iframe 元素
iframe 元素会创建包含另外一个文档的内联框架
通过设置 iframe.src = url 来刷新页面。其中 src 属性可设置或返回被载入 iframe 中的文档的 url。当 web 端想要传递数据给Java 端时,可以将内容序列化包装成 url 。

二:JsBridge 的使用
1. 添加 gradle 依赖
repositories {
    // ...
    maven { url "https://jitpack.io" }
}

dependencies {
    compile 'com.github.lzyzsd:jsbridge:1.0.4'
}
2. java 提供 Javascript 调用

webview 注册 “submitFromWeb” 方法名的 handler

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

Javascript 调用 submitFromWeb 对应的 handler

 WebViewJavascriptBridge.callHandler(
        'submitFromWeb'
        , {'param': str1}
        , function(responseData) {
            document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
        }
    );
3. Javascript 提供 java 调用

Javascript 注册 Handler

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

java 端调用 Handler

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

        }
    });

三:原理分析

tips:下面内容是根据 JsBridge 的具体源码流程来进行分析的,需要先阅读源码,否则会云里雾里不知道讲啥。

准备工作
  1. WebView 初始化
    WebViewClient中的 onPageFinished() 回调触发时,webView 加载本地的WebViewJavascriptBridge.js 文件。

  2. WebViewJavascriptBridge 初始化
    在该 JS 文件中,首先会创建俩个 iframe,然后 docuemnt.createEvent("Events") 创建并发送一个 WebViewJavascriptBridgeReady 的事件,表明 WebViewJavascriptBridge.js 文件已加载完毕。

  3. demo.html 所做的工作
    该页面加载时会进行连接 WebViewJavascriptBridge 的任务。该任务下会利用 document.addEventListener 监听 WebViewJavascriptBridgeReady 的事件,当该事件到来时,取出其中的 webViewJavascriptBridge 对象,调用 bridge.init() 与 bridge.registerHandler方法。

  4. bridge.init
    该方法是设置默认的 messageHandler (消息处理函数),将其保存在
    webViewJavascriptBridge._messageHandler 中。

  5. bridge.registerHandler 方法则是 Web 端设置可供Java 端调用指定方法的处理函数

java 调用 JavaScript 流程.
JsBridge.png

上面是 Java 调用 Javascript 的处理方法的流程图,接下来根据序号一一介绍

流程1
BridgeWebView 中使用 callHandler(String handlername, String data, CallBackFunction callback).方法调用 JS 中的处理函数。 其中 callback 函数会返回 JS 调用的结果。

1.1 doSend 处理
callHandller 实现中调用 doSend(handlerName, data, callback)函数,函数中将方法参数包装成 Message 对象,设置调用 JS 方法名 message.handleName属性;设置传递数据 message.data。

为了在 JS 内容返回时,准确找到回调函数,这里为回调函数分配一个回调 id 标识该回调函数。 回调 id 的组成由 “JAVA_CALLBACK” + uniqueId +当前时间戳。同时使用
HashMap 将 回调 id 与 回调函数联系起来。

1.2 queueMessage(Message m)
queueMessage 中又调用了 dispatchMessage(m)

1.3 dispatchmessage(Message m)
将 Message 对象 json 格式化,然后拼接 url ,使用 webView.loadUrl 执行 JS 代码。这种的 url 格式为 javascript:WebViewJavascriptBridge._handleMessageFromNative('%s')其中 %s 为 message json 格式化后的数据。

流程2
2.1 _handleMessageFromNative(messageJSON);
使用 console.log(messageJSON); 输出 logcat
接着调用 _dispatchMessageFromNative(messageJSON)

2.2 _dispatchMessageFromNative(messageJSON)
解析 messageJSON 反序列化为 Message 对象,
此时 Message 对象的 callbackId 不为空,构造 responseCallback。查看 messageHandlers 数组中是否有 message.handlerName对象的处理方法。有的话即调用对象函数。当返回结果为 WebView时,调用该 responseCallback,将结果返回给 JAVA 端。 该回调中调用
_doSend({responseId : callbackResponseId, responseData: responseData}) 函数。

流程3
3.1 _doSend(message, responseCallback)
这里将发送的消息保存到 sendMessageQueue.push(message)
messagingIFrame.src = YY://QUEUE_MESSAGE/
iframe.src 方法会刷新当前网页

流程4
4.1 WebViewClient.shouldOverrideUrlLoading(WebView view)
这时候该方法会回调,执行到 webView.flushMessageQueue();

流程5
5.1 webView.flushMessageQueue()
执行 loadUrl('javascript:WebViewJavascriptBridge._fetchQueue();', CallBackFunction);

5.2 loadUrl
获取url 中要调用 JS 端的方法名,将方法名与回调函数保存到 responseCallback

流程6
_fecthQueue()
获取 JS 端调用 JAVA 端的消息队列。

流程7
JSON 格式化之后,调用 bizMEssageIframe.src = YY://return/_fetchQueue/encodeURIConponent(messageQueueString)
该消息队列的内容都是 JS 端要返回给 WebView的消息

流程8
webView.handlerReturnData(url)
同样 shouldOverrideUrlLoading 方法会回调会拦截,根据“YY://return”得知这是 JS 端返回的内容,即调用 webView.handlerReturnData(url)

该函数内首先获取函数名 _fetchQueue,获取之前保存在 responseCallbacks 中的 回调处理函数。调用函数,将 JS 端调用JAVA 端的消息队列传递过去。

流程9
fetch_queue 的回调函数为
获取消息队列,遍历Message。 其中 messages 中 包含之前 JAVA 端调用 JS 端,JS处理之后的返回给 JAVA端的回调函数,故其中的 responseId 不为空。 由于 responseId 与 Java 端调用JS 时 设置的
CallbackId 是一一对应的。 取出其JAVA 设置的 回调函数,调用该函数,将 responseData 返回,至此 JAVA 端 调用 JS 端流程结束。

你可能感兴趣的:(Android JsBridge 原理解析)