Native JsBridge源码解析 深入理解JsBridge
Posted Vicent_9920
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Native JsBridge源码解析 深入理解JsBridge相关的知识,希望对你有一定的参考价值。
最近项目中使用了 HyBrid 框架,但是在使用过程中遇到了不少问题,因此花时间来研究了一下其中原理! 在平时开发过程中,不管是可复用性非常高,可以跨平台开发的 HyBrid ,还是半 Native 半 web 浅尝辄止的 HyBrid ,对 android 而言,陌生的就是其中的通信——Android 与 html 的互相通信。这里就不掉书包,直接阐明其中的使用方法。
- 引入JsBridge库
//at your porject gradle
repositories
// ...
maven url "https://jitpack.io"
//at you app gradle
dependencies
compile 'com.github.lzyzsd:jsbridge:1.0.4'
Android端收发消息
- 使用控件
- 向Html发送消息
- 接收Html发送的消息
//布局
<com.github.lzyzsd.jsbridge.BridgeWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</com.github.lzyzsd.jsbridge.BridgeWebView>
//控件
webView = (BridgeWebView) findViewById(R.id.webView);
/**
* 发送消息给html
* @param value 字符串
* @param data 数据
* @param function 回调方法
*/
webView.callHandler("value", "data", new CallBackFunction()
@Override
public void onCallBack(String response)
);
/**
* 发送消息给html
* @param data 字符串
*/
webView.send("hello");
/**
* register handler,so that javascript can call it
* 注册handler,以便javascript可以调用它
* @param handlerName
* @param handler
*/
webView.registerHandler("value", new BridgeHandler()
@Override
public void handler(String data, CallBackFunction function)
);
- Html端收发消息
//注册 WebViewJavascriptBridgeReady
function connectWebViewJavascriptBridge(callback)
if (window.WebViewJavascriptBridge)
callback(WebViewJavascriptBridge)
else
document.addEventListener(
'WebViewJavascriptBridgeReady'
, function()
callback(WebViewJavascriptBridge)
,
false
);
connectWebViewJavascriptBridge(function(bridge)
bridge.init(function(message, responseCallback)
console.log('JS got a message', message);
//默认返回值
var data =
'Javascript Responds': '测试中文!'
;
console.log('JS responding with', data);
responseCallback(data);
);
/**
* 接收消息
* "functionInJs" 字符串标签
* data 收到数据
* responseCallback 回调接口
*/
bridge.registerHandler("functionInJs", function(data, responseCallback)
document.getElementById("show").innerHTML = ("data from Java: = " + data);
var responseData = "Javascript Says Right back aka!";
responseCallback(responseData);
);
)
通过库文件demo可以仔细看到这些方法,还是比较容易理解的。但是具体是怎么一个原理呢?跟踪代码看看,其实非常简单:
webView.send("hello");
/**
* 发送消息给html
* @param value 字符串
* @param data 数据
* @param function 回调方法
*/
private void sendMessage(String value, String data, CallBackFunction function)
webView.callHandler(value, data, function);
接下来看看BridgeWebView的源码:
@Override
public void send(String data)
send(data, null);
@Override
public void send(String data, CallBackFunction responseCallback)
doSend(null, data, responseCallback);
/**
* call javascript registered handler
*
* @param handlerName
* @param data
* @param callBack
*/
public void callHandler(String handlerName, String data, CallBackFunction callBack)
doSend(handlerName, data, callBack);
接下来仔细研究一下doSend这个方法,源码如下:
private void doSend(String handlerName, String data, CallBackFunction responseCallback)
Message m = new Message();
if (!TextUtils.isEmpty(data))
m.setData(data);
if (responseCallback != null)
String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT, ++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));
responseCallbacks.put(callbackStr, responseCallback);
m.setCallbackId(callbackStr);
if (!TextUtils.isEmpty(handlerName))
m.setHandlerName(handlerName);
queueMessage(m);
上面的代码其实就是封装了一个 Message,然后将 Message 添加到添加到队列里,这个结构类似于 Handler,接下来看看队列消息里面怎么将消息发送到 Js?
private void queueMessage(Message m)
if (startupMessage != null)
startupMessage.add(m);
else
dispatchMessage(m);
void dispatchMessage(Message m)
String messageJson = m.toJson();
//escape special characters for json string
messageJson = messageJson.replaceAll("(\\\\\\\\)([^utrn])", "\\\\\\\\\\\\\\\\$1$2");
messageJson = messageJson.replaceAll("(?<=[^\\\\\\\\])(\\")", "\\\\\\\\\\"");
String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);
if (Thread.currentThread() == Looper.getMainLooper().getThread())
LUtil.e(javascriptCommand);
this.loadUrl(javascriptCommand);
startupMessage 其实就是一个消息队列,这个对象的初始值肯定不为 null,但是这里的 startupMessage 确实是被置空了,至于在什么地方置空的,稍候再研究,我们可以看到在分发消息的时候,直接将 Message 转成了 Json 字符串,然后对字符串经过一系列处理便加载了这个方法,通过日志,我们可以看到这时候输出的内容如下:
问题是我们注册的时候,并没有这个方法,那么这个方法在什么地方被声明的呢?通过查看 BridgeWebView 的相关方法,最后发现在 BridgeWebViewClient 的 onPageFinished 方法里,这里将 assets 文件夹下的 WebViewJavascriptBridge.js 读取出来发送到了 html,而 WebViewJavascriptBridge.js 内容是什么呢?看代码:
//发送消息内容:
view.loadUrl("javascript:" + jsContent);
//消息内容如下
//notation: js file can only use this kind of comments
//since comments will cause error when use in webview.loadurl,
//comments will be remove by java use regexp
(function()
if (window.WebViewJavascriptBridge)
return;
var messagingIframe;
var sendMessageQueue = [];
var receiveMessageQueue = [];
var messageHandlers = ;
var CUSTOM_PROTOCOL_SCHEME = 'yy';
var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';
var responseCallbacks = ;
var uniqueId = 1;
function _createQueueReadyIframe(doc)
messagingIframe = doc.createElement('iframe');
messagingIframe.style.display = 'none';
doc.documentElement.appendChild(messagingIframe);
//set default messageHandler
function init(messageHandler)
if (WebViewJavascriptBridge._messageHandler)
throw new Error('WebViewJavascriptBridge.init called twice');
WebViewJavascriptBridge._messageHandler = messageHandler;
var receivedMessages = receiveMessageQueue;
receiveMessageQueue = null;
for (var i = 0; i < receivedMessages.length; i++)
_dispatchMessageFromNative(receivedMessages[i]);
function send(data, responseCallback)
_doSend(
data: data
, responseCallback);
function registerHandler(handlerName, handler)
messageHandlers[handlerName] = handler;
function callHandler(handlerName, data, responseCallback)
_doSend(
handlerName: handlerName,
data: data
, responseCallback);
//sendMessage add message, 触发native处理 sendMessage
function _doSend(message, responseCallback)
if (responseCallback)
var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
message.callbackId = callbackId;
sendMessageQueue.push(message);
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
// 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容
function _fetchQueue()
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
//android can't read directly the return data, so we can reload iframe src to communicate with java
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
//提供给native使用,
function _dispatchMessageFromNative(messageJSON)
setTimeout(function()
var message = JSON.parse(messageJSON);
var responseCallback;
//java call finished, now need to call js callback function
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(
responseId: callbackResponseId,
responseData: responseData
);
;
var handler = WebViewJavascriptBridge._messageHandler;
if (message.handlerName)
handler = messageHandlers[message.handlerName];
//查找指定handler
try
handler(message.data, responseCallback);
catch (exception)
if (typeof console != 'undefined')
console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
);
//提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以
function _handleMessageFromNative(messageJSON)
console.log(messageJSON);
if (receiveMessageQueue && receiveMessageQueue.length > 0)
receiveMessageQueue.push(messageJSON);
else
_dispatchMessageFromNative(messageJSON);
var WebViewJavascriptBridge = window.WebViewJavascriptBridge =
init: init,
send: send,
registerHandler: registerHandler,
callHandler: callHandler,
_fetchQueue: _fetchQueue,
_handleMessageFromNative: _handleMessageFromNative
;
var doc = document;
_createQueueReadyIframe(doc);
var readyEvent = doc.createEvent('Events');
readyEvent.initEvent('WebViewJavascriptBridgeReady');
readyEvent.bridge = WebViewJavascriptBridge;
doc.dispatchEvent(readyEvent);
)();
前面讲的 startupMessage 置空,就是在上面的 onPageFinished 中被置空的。
//置空startupMessage
if (webView.getStartupMessage() != null)
for (Message m : webView.getStartupMessage())
webView.dispatchMessage(m);
webView.setStartupMessage(null);
接下来分析,BridgeWebView 端发送的消息是怎么在 JS 被执行的呢?先是通过 _handleMessageFromNative 方法调用 _dispatchMessageFromNative 方法,最后执行如下代码:
try
handler(message.data, responseCallback);
catch (exception)
if (typeof console != 'undefined')
console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
handler(message.data, responseCallback) 方法具体实现是在下面实现的( js 不大熟悉,整理代码发现逻辑大致如下):
/**
* 接收消息
* "functionInJs" 字符串标签
* data 收到数据
* responseCallback 回调接口
*/
bridge.registerHandler("functionInJs", function(data, responseCallback)
document.getElementById("show").innerHTML = ("data from Java: = " + data);
var responseData = "Javascript Says Right back aka!";
responseCallback(responseData);
);
接着我们看看 BridgeWebView 端发送消息的回调接口,是怎么实现的?通过代码我们发现 _dispatchMessageFromNative 方法下回调方法 _doSend 方法:
//sendMessage add message, 触发native处理 sendMessage
function _doSend(message, responseCallback)
if (responseCallback)
var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
message.callbackId = callbackId;
sendMessageQueue.push(message);
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
_doSend 方法请求 url:yy://QUEUE_MESSAGE/
当 BridgeWebView 的 shouldOverrideUrlLoading 方法收到 messagingIframe 请求,继续检查这个方法:
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
try
url = URLDecoder.decode(url, "UTF-8");
LUtil.e("shouldOverrideUrlLoading:"+url);
catch (UnsupportedEncodingException e)
e.printStackTrace();
if (url.startsWith(BridgeUtil.YY_RETURN_DATA))
webView.handlerReturnData(url);
return true;
else if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA)) //
webView.flushMessageQueue();
return true;
else
return super.shouldOverrideUrlLoading(view, url);
这次我们查看 BridgeWebView 的 flushMessageQueue 方法,直接看代码:
/**
* 刷新消息队列
*/
void flushMessageQueue()
if (Thread.currentThread() == Looper.getMainLooper().getThread())
loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction()
***
);
public void loadUrl(String jsUrl, CallBackFunction returnCallback)
this.loadUrl(jsUrl);
responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback);
其实这里很清楚了,就是当 JS 需要返回数据的时候,先将需要返回的数据保存下来,然后发送请求到 BridgeWebView ,告诉 BridgeWebView 我要发送回调数据了,然后 BridgeWebView 将请求和接口以键值对的形式保存下来,并请求 js 的方法—— javascript:WebViewJavascriptBridge._fetchQueue();JS 收到消息后就发送请求,然后 shouldOverrideUrlLoading 收到请求判断消息头以后调用 BridgeWebView 的方法,代码如下:
//返回消息头 yy://return/_fetchQueue/+返回数据
void handlerReturnData(String url)
String functionName = BridgeUtil.getFunctionFromReturnUrl(url);
CallBackFunction f = responseCallbacks.get(functionName);
String data = BridgeUtil.getDataFromReturnUrl(url);
if (f != null)
f.onCallBack(data);
responseCallbacks.remove(functionName);
return;
最后将这个过程整理如下:
- 安卓发送消息给 Js 过程
- 当 WebView 发送数据给 Js 时: WebView 请求 js 方法—— javascript:WebViewJavascriptBridge._handleMessageFromNative( gson 字符串);
- js 执行相关方法: _handleMessageFromNative——_dispatchMessageFromNative——handler(message.data, responseCallback);,
- Js 通过接口返回数据
- js 执行 _doSend 方法;
- BridgeWebView 拦截请求并判断消息头,如果消息头标签是—— yy://,则将 _fetchQueue 和接口再次以键值对的形式保存下来,并通过 BridgeWebView 请求 js 的 _fetchQueue() 方法;
- js 的 _fetchQueue() 方法将值发送给 BridgeWebView;
- BridgeWebView 再次判断请求的消息头,如果消息头标签是—— yy://return/ ,则用 _fetchQueue 取出的接口对象;
- 在接口对象中,根据返回的接口对象 Id —— responseId 取出 BridgeWebView 存储的待处理数据的接口对象,并将请求里面包含的数据取出来,执行待处理数据的接口。
接下来 Js 发送消息给 BridgeWebView 的过程又如何呢?首先咱们得与 Js 约定一个 Handler,然后将这个 Handler 保存下来,代码如下:
Map<String, BridgeHandler> messageHandlers = new HashMap<String, BridgeHandler>();
/**
* register handler,so that javascript can call it
*
* @param handlerName
* @param handler
*/
public void registerHandler(String handlerName, BridgeHandler handler)
if (handler != null)
messageHandlers.put(handlerName, handler);
然后咱们继续看 Js 是如何发送消息给 BridgeWebView,还是直接看源码:
//call native method
window.WebViewJavascriptBridge.callHandler(
'submitFromWeb'
, 'param': '中文测试'
, function(responseData)
document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
);
来来来,咱们理一下这个过程,首先 js 执行 callHandler 方法—— _doSend 方法,在这个方法 _doSend 方法中还是一样:将 Message 信息保存下来,并发送请求给 BridgeWebView,告诉他,我要给你发消息了,准备好!
然后过程就和上面类似了,过程如下:
- js 执行 _doSend 方法,保存消息并发送请求—— yy://QUEUE_MESSAGE/;
- BridgeWebView 拦截请求并判断消息头,如果消息头标签是—— yy://,则将 _fetchQueue 和接口以键值对的形式保存下来,并通过 BridgeWebView 请求 js 的 _fetchQueue() 方法;
- js 执行 _fetchQueue() 方法并发送请求——yy://return/_fetchQueue/+”消息”;
- BridgeWebView 在 shouldOverrideUrlLoading 方法拦截请求,如果消息头标签是—— yy://return/_fetchQueue/,则取出第2点保存的接口,并将请求包含的数据发送给接口;
- 在接口当中做两件事,第一件事是根据请求信息生成 Message 对象并保存下来(BridgeWebView的queueMessage方法),另一件事就是根据请求内容将 js 与 BridgeWebView 约定 BridgeHandler ,并执行 BridgeHandler 的 handler 方法;
- BridgeWebViewClient 在 onPageFinished 方法中分发消息(dispatchMessage方法)—— BridgeWebView 请求 js 的 _handleMessageFromNative 方法;
- Js 执行 _handleMessageFromNative 方法—— _dispatchMessageFromNative 方法,在方法中根据 responseId 找出接口,并执行该接口,完毕。
上述已经描述了 BridgeWebView 发送消息并回调、Js 发送消息并回调,整个过程我们已经很清楚了,那么这些个过程有没有需要优化的地方呢?比如 BridgeWebView 与 js 优化的地方有很多,那么能不能把接口改一下呢?咱们试一试!由于整个 JsBridge 代码文件不多,我们可以直接下载下来,这样方便我们修改,因此我们可以直接修改 BridgeHandler.java ,然后修改部分代码,说干就干:
public interface BridgeHandler
void handler(String name, String data, CallBackFunction function);
//修改DefaultHandler
public class DefaultHandler implements BridgeHandler
@Override
public void handler(String name,String data, CallBackFunction function)
if(function != null)
function.onCallBack("DefaultHandler response data");
//通过Js信息查找Handler
JsBridgeHandler handler;
if (!TextUtils.isEmpty(m.getHandlerName()))
handler = messageHandlers.get(m.getHandlerName());
else
handler = defaultHandler;
if (handler != null)
handler.handler(m.getHandlerName(),m.getData(), responseFunction);
//修改注册方法
@Override
public void handler(String name, String data, final CallBackFunction function)
switch (name)
case "open"://打开后台维护界面
break;
case "token"://获取token
break;
case "clientId"://获取clientId
break;
case "out"://退出账号
break;
以上已经分析完毕。JsBridge 的原理大家都知道了,接下来需要做的就是WebView 的优化,因为我们知道原生的 WebView 加载页面的时候渲染比较慢,一些诸如腾讯 X5、VasSonic 等据说渲染效果比 WebView 好很多,那么兼容通信和渲染效果良好的的 JsBridge 框架,应该怎么搭建呢?好好想一想?
腾讯 X5 WebView 接入
X5 WebView 比较简单,就是一个 jar 包,然后将原本的 android.webkit 包下的内容转为 com.tencent.smtt 下的相关类,并实现相关权限(运行时权限需要自己处理),具体如下:
系统内核 | SDK内核 |
---|---|
android.webkit.ConsoleMessage | com.tencent.smtt.export.external.interfaces.ConsoleMessage |
android.webkit.CacheManager | com.tencent.smtt.sdk.CacheManager(deprecated) |
android.webkit.CookieManager | com.tencent.smtt.sdk.CookieManager |
android.webkit.CookieSyncManager | com.tencent.smtt.sdk.CookieSyncManager |
android.webkit.CustomViewCallback | com.tencent.smtt.export.external.interfaces.IX5WebChromeClient.CustomViewCallback |
android.webkit.DownloadListener | com.tencent.smtt.sdk.DownloadListener |
android.webkit.GeolocationPermissions | com.tencent.smtt.export.external.interfaces.GeolocationPermissionsCallback |
android.webkit.HttpAuthHandler | com.tencent.smtt.export.external.interfaces.HttpAuthHandler |
android.webkit.JsPromptResult | com.tencent.smtt.export.external.interfaces.JsPromptResult |
android.webkit.JsResult | com.tencent.smtt.export.external.interfaces.JsResult |
android.webkit.SslErrorHandler | com.tencent.smtt.export.external.interfaces.SslErrorHandler |
android.webkit.ValueCallback | com.tencent.smtt.sdk.ValueCallback |
android.webkit.WebBackForwardList | com.tencent.smtt.sdk.WebBackForwardList |
android.webkit.WebChromeClient | com.tencent.smtt.sdk.WebChromeClient |
android.webkit.WebHistoryItem | com.tencent.smtt.sdk.WebHistoryItem |
android.webkit.WebIconDatabase | com.tencent.smtt.sdk.WebIconDatabase |
android.webkit.WebResourceResponse | com.tencent.smtt.export.external.interfaces.WebResourceResponse |
android.webkit.WebSettings | com.tencent.smtt.sdk.WebSettings |
android.webkit.WebSettings.LayoutAlgorithm | com.tencent.smtt.sdk.WebSettings.LayoutAlgorithm |
android.webkit.WebStorage | com.tencent.smtt.sdk.WebStorage |
android.webkit.WebView | com.tencent.smtt.sdk.WebView |
android.webkit.WebViewClient | com.tencent.smtt.sdk.WebViewClient |
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
- VasSonic-Android 接入
VasSonic接入比较简单,但是使用就比较复杂,接入如下:
//Add VasSonic gradle plugin as a dependency in your module's build.gradle
compile 'com.tencent.sonic:sdk:1.0.0'
使用前需要实现 SonicRuntime 和 SonicSessionClient 接口,具体参考 VasSonic
腾讯 X5 WebView 、VasSonic 、原生 WebView 对比
通过对onPageStarted方法、onPageFinished方法查看各个 WebView 加载页面所需时间,发现 X5 、VasSonic 均比原生 WebView 快了近一倍,所以 JsBridge 有必要将第三方WebView 加载到项目中,但是加载 X5 还是 VasSonic 呢?通过查看文档发现 VasSonic 更新需要和后台服务器交互判断是否更新数据?也就是说 VasSonic 需要和后台配合才能发挥最大效果。我倾向于选择前者,因为使用起来简单得多,而后者的库也配置好了,地址如下:https://github.com/Vicent9920/JsBridge
JsBridge 注意事项:
App 首次就可以加载 x5 内核
App 在启动后(例如在 Application 的 onCreate 中)立刻调用 QbSdk 的预加载接口 initX5Environment ,可参考接入示例,第一个参数传入 context,第二个参数传入 callback,不需要 callback 的可以传入 null,initX5Environment 内部会创建一个线程向后台查询当前可用内核版本号,这个函数内是异步执行所以不会阻塞 App 主线程,这个函数内是轻量级执行所以对 App 启动性能没有影响,当 App 后续创建 webview 时就可以首次加载 x5 内核了。(也可以像LitePal那样在Application初始化,但是需要文件清单配置或者继承自该Application,或者在该类传入一个静态方法初始化 JsBridge )
获取系统内核的WebView或者 x5内核的WebView的宽高
com.tencent.smtt.sdk.WebView webView = new com.tencent.smtt.sdk.WebView(this);
int width = webView.getView().getWidth();
- 避免输入法界面弹出后遮挡输入光标的问题
//方法一:在AndroidManifest.xml中设置
android:windowSoftInputMode="stateHidden|adjustResize"
//方法二:在代码中动态设置:
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
- 兼容视频播放
1)享受页面视频的完整播放体验需要做如下声明:
页面的Activity需要声明
android:configChanges="orientation|screenSize|keyboardHidden"
2)视频为了避免闪屏和透明问题,需要如下设置
a)网页中的视频,上屏幕的时候,可能出现闪烁的情况,需要如下设置:Activity在onCreate时需要设置:
getWindow().setFormat(PixelFormat.TRANSLUCENT);(这个对宿主没什么影响,建议声明)
在非硬绘手机和声明需要controller的网页上,视频切换全屏和全屏切换回页面内会出现视频窗口透明问题,需要如下设置
声明当前<item name="android:windowIsTranslucent">false为不透明。
特别说明:这个视各app情况所需,不强制需求,如果声明了,对体验更有利
c)以下接口禁止(直接或反射)调用,避免视频画面无法显示:
webview.setLayerType() webview.setDrawingCacheEnabled(true);
其它参考文档:X5 接入文档
WebView相关文章推荐:
腾讯浏览服务X5内核集成
Android中WebView的JavaScript代码和本地代码交互的三种方式
WebView详解与简单实现Android与H5互调
WebView写入数据到 localStorage总结
以上是关于Native JsBridge源码解析 深入理解JsBridge的主要内容,如果未能解决你的问题,请参考以下文章
Android EventBus源码解析, 带你深入理解EventBus
深入理解Android Handler机制(深入至native层)