开启成长之旅!这是我参加「日新计划 12 月更文应战」的第3天,点击查看活动详情
前沿
写这篇文章的首要意图是对 App 的 JSBridge 做一个全面的介绍,同时根据不同的运用场景总结出一份 App 完成 JSBridge 的最佳计划。关于没有触摸过 App 的同学能够对 JSBridge 有个大致的概念,关于做过 App 的 JSBridge 开发的同学也能有更体系的知道,也是自己关于相关知识点的概括总结。
一、概念
什么是 JSBridge ?
JSBridge 的全称:JavaScript Bridge,中文名 JS桥
或 JS桥接器
。
JSBridge 是一种用于在 Android 和 iOS 运用与 H5 之间进行通讯的技能。它答应运用开发者在原生代码中调用 JavaScript 函数,以及 在JavaScript 中调用原生代码函数。其一般用于移动运用开发中,能够运用 JSBridge 技能在原生运用中嵌入网页,并在网页与原生运用之间进行交互。
二、原理
JSBridge 经过在 WebView 中注册 JavaScript 函数来完成通讯。WebView 是一种在运用中嵌入网页的组件,能够在运用中显现网页内容。JSBridge 经过在 WebView 中注册 JavaScript 函数,并在原生代码中调用这些函数来完成通讯。
例如,下面是一个运用 JSBridge 完成通讯的示例代码:
/* Android 端完成 */
// 在WebView中注册JavaScript函数
webView.loadUrl("javascript:function myFunction() { /* JavaScript code here */ }");
// 在原生代码中调用JavaScript函数
webView.loadUrl("javascript:myFunction()");
/* iOS 端完成 */
// 在WebView中注册JavaScript函数
[self.webView stringByEvaluatingJavaScriptFromString:@"function myFunction() { /* JavaScript code here */ }"];
// 在原生代码中调用JavaScript函数
[self.webView stringByEvaluatingJavaScriptFromString:@"myFunction()"];
上面的代码经过在 WebView 中注册 JavaScript 函数 myFunction
,并在原生代码中调用这个函数来完成通讯。
在实践开发中,咱们一般是创立一个 JSBridge 目标,然后经过 WebView 的 addJavascriptInterface
办法进行注册。
// WebView 的 addJavascriptInterface 办法源码
public void addJavascriptInterface(Object object, String name) {
checkThread();
if (object == null) {
throw new NullPointerException("Cannot add a null object");
}
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("Invalid name");
}
mJavascriptInterfaces.put(name, object);
}
该办法首要查看当时线程是否是 UI 线程,以保证添加桥接目标的操作是在 UI 线程中进行的。接着,该办法会查看桥接目标和称号的有效性,保证它们都不为空。最终,该办法会把桥接目标与称号关联起来,并存储到 WebView 的 mJavascriptInterfaces
目标中。
当网页加载完成后,WebView 会把桥接目标的办法注入到网页中,使得网页能够调用这些办法。当网页中的 JavaScript 代码调用桥接目标的办法时,WebView 会把该办法调用映射到原生代码中,从而完成网页与原生运用之间的交互。
addJavascriptInterface
办法的首要作用是把桥接目标的办法注入到网页中,使得网页能够调用这些办法。它的详细完成办法可能会因渠道而异,但是它的基本原理是共同的。
三、原生完成
以 H5 获取 App 的版别号为例。Android相关源码
要完成一个获取 App 版别号的 JSBridge,需要在 H5 中编写 JavaScript 代码,并在 Android 原生代码中完成对应的原生办法。
首要,需要在 H5 中编写 JavaScript 代码,用于调用 Android 的原生办法。例如,能够在 H5 中界说一个函数,用于调用 Android 的原生办法:
// assets/index.html
function getAppVersion() {
// 经过JSBridge调用Android的原生办法
JSBridge.getAppVersion(function(version) {
// 在这儿处理获取到的Android版别号
});
}
然后,需要在 Android 的原生代码中完成对应的原生办法。例如,能够完成一个名为 getAppVersion
的办法,用于在 H5 中调用:
// com.fitem.webviewdemo.AppJSBridge
@JavascriptInterface
public String getAppVersion() {
// 获取App版别号
String version = BuildConfig.VERSION_NAME;
// 将App版别号回来给H5
return version;
}
最终经过 Webview 注入界说的 JavascriptInterface 办法的目标,在 H5 生成 window.jsBridge 目标进行调用。
// com.fitem.webviewdemo.MainActivity.kt
webView.addJavascriptInterface(jsBridge, "jsBridge")
iOS 的完成和 Android 相似:
- (void)getIOSVersion:(WVJBResponseCallback)callback {
// 获取App版别号
let version = Bundle.main.object(forInfoDictionaryKey:
"CFBundleShortVersionString") as! String
// 将App版别号回来给H5
callback(version);
}
// 在网页加载完成后设置JSBridge
- (void)webViewDidFinishLoad:(UIWebView *)webView {
// 设置JSBridge
[WebViewJavascriptBridge enableLogging];
self.bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
[self.bridge setWebViewDelegate:self];
}
四、跨渠道(Flutter)
1. JSBridge 完成
Flutter 完成 JSBridge 功能的插件有很多,但基本上大多数都是基于原生的 JSBridge 才能完成。这儿首要介绍官方的 webview_flutter
插件。
webview_flutter
插件完成 App 与 H5 之前的通讯分为:App 发送音讯到 H5 和 H5 发送音讯到 APP 两部分。
H5 发送音讯到 APP。首要在 Flutter 运用中添加 WebView
组件,并设置 JavascriptChannel
:
WebView(
initialUrl: 'https://www.example.com',
javascriptMode: JavascriptMode.unrestricted,
javascriptChannels: {
// 设置JavascriptChannel
JavascriptChannel(
name: 'JSBridge',
onMessageReceived: (JavascriptMessage message) {
// 在这儿处理来自H5的音讯
},
),
},
),
在H5中,能够经过 JSBridge
目标来调用原生办法:
// 经过JSBridge调用原生办法
window.jsBridge.postMessage('Hello, world!');
App 发送音讯到 H5。 在 Flutter 中,经过 WebViewController 的 runJavascrip
调用 H5 中 window 目标的办法
controller.runJavascript("receiveMessage(${json.encode(res)})")
在 H5 中,能够经过 onmessage
事件来接收来自原生的音讯:
// 接收来自原生的音讯
window.receiveMessage = function receiveMessage(message) {
console.log(message);
};
2. 局限性
webview_flutter
最大的局限在于 App 端与 H5 端之间的通讯只支撑单向通讯,无法经过一次调用直接获取另一端的回来值。
五、App 完成 JSBridge 的最佳计划
1. 完成目标
-
H5 兼容原生老版别 JSBridge。
-
支撑两端双向通讯。针对
webview_flutter
的单向通讯的局限性进行改造优化,使其能支撑回来值的回调。
2. NativeBridge 插件开发
NativeBridge 本质上是对 webview_flutter 的单向通讯才能进行扩展
和封装
。
NativeBridge 插件的运用和完成原理,请阅读之前的文章《Flutter插件之NativeBridge》和《NativeBridge完成原理解析》。
3. 完成效果
- H5 支撑原生老版别 JSBridge 兼容。
// 获取app版别号 回来String
async getVersionCode() {
// 是否是新的JSBridge
if (this.isNewJSBridge()) {
return await window.jsBridgeHelper.sendMessage('getVersionCode', null)
} else {
return window.iLotJsBridge.getVersionCode()
}
}
- 支撑两端双向通讯。
// H5 获取 App 的值
const versionNo = await jsBridge.getVersionCode()
// App 获取 H5 的值
var isHome = await NativeBridgeHelper.sendMessage("isHome", null, webViewController).future ?? false;
- 新增超时连接机制
就像网络恳求相同,咱们不能让代码履行一向阻塞在获取回来值的方位上。因为单向发送音讯是不可靠的,可能存在音讯丢掉,或者另一端不响应音讯的状况。因而咱们需要相似网络恳求相同,添加超时回调机制。
// 添加回调反常容错机制,避免音讯丢掉导致一向阻塞
Future.delayed(const Duration(milliseconds: 100), (){
var completer = _popCallback(callbackId);
completer?.complete(Future.value(null));
});
总结
咱们首要介绍了 JSBridge 的概念和原理,然后经过在 Android 、iOS 和 Flutter 中完成 JSBridge 来理解原生和 Flutter 之前的差异,最终总结了在 App 中完成 JSBridge 的最佳计划,计划包含支撑原生和 Flutter 的兼容,并优化 webview_flutter
只支撑单向通讯的局限性和添加超时回调机制。
相关文章:《Flutter插件之NativeBridge》和《NativeBridge完成原理解析》
源码:Android原生完成、Flutter完成源码