导读
写这篇的博客起因是由于公司app的H5 Hybrid项目引发的本文的撰写!由于公司内部JSBridge方案有些庞大,并且端上开发人力紧张等这些客观因素,简单的基于js2native的原理实现了一版简陋版JSBridge SDK,不妥之处还请大家批评指正!!!
JSBridge起源
首先我们讲下Bridge 的起源。JSBridge 是一种 JS 实现的 Bridge,连接着桥两端的 Native 和 H5。它在 APP 内方便地让 Native 调用 JS,JS 调用 Native ,是双向通信的通道。JSBridge 主要提供了 JS 调用 Native 代码的能力,实现原生功能如查看本地相册、打开摄像头、指纹支付等。
JSBridge 的双向通信原理
JS调用Native
JS 调用 Native 的实现方式较多,目前主流采用是拦截URL Scheme 、重写prompt 、注入API方法。
基于我们的业务需求,我们仅需在鸿蒙平台下使用JSBridge,所以我们采用了拦截URL Scheme来实现,今天我们的分享也主要以这个为主!
URL Scheme
web端采用创建隐藏的iframe进行scheme请求,端上采用拦截协议上的参数实现调用对应的native方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| String urlScheme = "xxx"; String callBackID = "xxx"; String data = "xxx"; boolean isSuccess = true; if(urlScheme == 'xxx') { webview.executeJs("javascript:window.CallJSBridge( ," + isSuccess "," + data + "," + ")", new AsyncCallback<String>() {
@Override
public void onReceive(String msg) { } }); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| createIframeRequest(url: string) { try { if (!iframeNode) { iframeNode = documentcreateElement("iframe"); iframeNode.style.display = "none"; document.documentElement.appendChild(iframeNode); let timerRemoveIframe: NodeJS.Timeout | null = setTimeout(() => { document.documentElement.removeChild( iframeNode as HTMLIFrameElement ); clearTimeout(timerRemoveIframe as NodeJS.Timeout); timerRemoveIframe = null; }, 0); } iframeNode.src = url; } catch (error) { } }
invoke(event: string, data?: any) { return new Promise((resolve, reject) => { if (this.isBridgeReady) { reject(); throw new Error("Bridge not ready!"); } const callback: string = nanoid(); const dataObj = { event, callback, }; const url = `${this.baseSchema}?${qs.stringify(dataObj)}`;
window.callbackObj[callback] = { url, call_time: +new Date(), success(data) { resolve(data); }, error(error) { reject(error); }, };
this.createIframeRequest(url);
setTimeout(() => { if ( window.__BridgeHandler && window.__BridgeHandler.call ) { let { callback, isSuccess, data: res, } = window.__BridgeHandler.call( JSON.stringify({ event, data, call_time: +new Date(), }) );
if (Object.prototype.toString.call(res) === "[object Object]") { res = JSON.stringify(res); } CallJSBridge(callback, isSuccess, res); } }, 0); }); }
|
- web同学在一进入页面前注入向window中注入代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function CallJSBridge(callback, isSuccess, data) { var handler = window.callbackObj[callback] if(handler) { throw new Error("handler error") return; }
try { isSuccess && handler.success && handler.success(data) !isSuccess && handler.error && handler.error(data) } catch(error) { console.error(error) } }
|
Native调用JS
Native 调用 JS 比较简单,只要 H5 将 JS 方法暴露在 Window 上给 Native 调用即可。
Android 和 鸿蒙OS 中主要有两种方式实现。在 4.4 以前,通过 loadUrl 方法,执行一段 JS 代码来实现。在 4.4 以后,可以使用 evaluateJavascript 方法实现。loadUrl 方法使用起来方便简洁,但是效率低无法获得返回结果且调用的时候会刷新 WebView 。evaluateJavascript 方法效率高获取返回值方便,调用时候不刷新 WebView,但是只支持 Android 4.4+。相关代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
webView.loadUrl("javascript:" + javaScriptString);
webView.evaluateJavascript(javaScriptString, new ValueCallback<String>() { @Override public void onReceiveValue(String value){ xxx } });
|
文档参考