本文詳細講述瞭如何使用 JSBridge 在 H5 和原生 Android、IOS之間進行互動。IOS 端包含 OC 和 Swift 的版本,Android 端包含 Java 和 Kotlin 版本。
一、寫在前面
本文主要是通過程式碼講述瞭如何使用 JSBridge 在 H5 和 原生之間進行通訊。文中包含 H5、IOS、Android 三部分的程式碼。 IOS 中使用 OC 和 Swift 分別進行了程式碼實現。Android 中使用 Java 和 Kotlin 分別進行了程式碼實現。
Demo 地址:jsbridge-example
JSBridgeH5
:H5
端程式碼實現JSBridgeIOSOC
:原生IOS
端OC
程式碼實現JSBridgeIOSSwift
:原生IOS
端Swift
程式碼實現JSBridgeAndroidJava
:原生Android
端Java
程式碼實現JSBridgeAndroidKotlin
:原生IOS
端Kotlin
程式碼實現
本文沒有講解關於原理的部分,只是詳細使用程式碼介紹了 JSBridge 的使用。想要了解原理的朋友,可以另行搜尋關於原理的部落格。
二、H5 端程式碼
- 初始化
WebViewJavascriptBridge
,方式程式碼如下 - 註冊供原生呼叫的事件函式:
window.setupWebViewJavascriptBridge(bridge => bridge.registerHandler('fnName', function) )
- 呼叫原生事件函式:
window.setupWebViewJavascriptBridge(bridge => bridge.callHandler('fnName', data, callback) )
1、初始化 WebViewJavascriptBridge
在專案入口檔案或者根 js
檔案下,新增以下程式碼:
// 這裡根據移動端原生的 userAgent 來判斷當前是 Android 還是 ios
const u = navigator.userAgent;
// Android終端
const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1;
// IOS 終端
const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
/**
* 配合 IOS 使用時的初始化方法
*/
const iosFunction = (callback) => {
if (window.WebViewJavascriptBridge) { return callback(window.WebViewJavascriptBridge) }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback) }
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function(){
document.documentElement.removeChild(WVJBIframe);
}, 0);
}
/**
* 配合 Android 使用時的初始化方法
*/
const androidFunction = (callback) => {
if (window.WebViewJavascriptBridge) {
callback(window.WebViewJavascriptBridge);
} else {
document.addEventListener('WebViewJavascriptBridgeReady', function () {
callback(window.WebViewJavascriptBridge);
}, false)
}
}
window.setupWebViewJavascriptBridge = isAndroid ? androidFunction : iosFunction;
isAndroid && window.setupWebViewJavascriptBridge(function (bridge) {
// 註冊 H5 介面的預設接收函式(與安卓互動時,安卓端可以不呼叫函式名,直接 send 資料過來,就能夠在這裡接收到資料)
bridge.init(function (msg, responseCallback) {
console.log(msg);
responseCallback("JS 返回給原生的訊息內容");
})
});
複製程式碼
2、註冊與原生互動的事件函式
/*
window.setupWebViewJavascriptBridge(bridge => {
bridge.registerHandler('事件函式名',fun 執行函式);
})
*/
window.setupWebViewJavascriptBridge(bridge => {
/**
* data:原生傳過來的資料
* fn: 原生傳過來的回撥函式
*/
bridge.registerHandler("H5Function", (data, fn) => {
console.log(data);
fn && fn();
});
});
複製程式碼
3、呼叫原生註冊的事件函式
呼叫原生註冊的時間函式時使用如下的程式碼:
/*
window.setupWebViewJavascriptBridge(bridge => {
bridge.callHandler('安卓端函式名', "傳給原生端的資料", callback 回撥函式);
})
*/
window.setupWebViewJavascriptBridge(bridge => {
bridge.callHandler('changeData', data, (result) => {
console.log(result);
});
})
複製程式碼
三、IOS 端程式碼
-
初始化 WebViewJavascriptBridge:
+ (instancetype)bridgeForWebView:(id)webView; + (instancetype)bridge:(id)webView; 複製程式碼
-
註冊與
H5
端互動的事件函式:- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
-
呼叫
H5
端事件函式:- (void)callHandler:(NSString*)handlerName; - (void)callHandler:(NSString*)handlerName data:(id)data; - (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback; 複製程式碼
1、引入 WebViewJavascriptBridge
直接使用方式
-
找到
WebViewJavascriptBridge
資料夾,直接拖入到 XCode 專案中,在提示的彈窗中選擇Copy items if needed
和Create groups
,如下圖: -
在
ViewController.h
標頭檔案中引入#import "WebViewJavascriptBridge.h"
即可
Cocopad 使用方式
如必須使用這種方式請自行 Google。
2、初始化 WebViewJavascriptBridge
// 啟用 WebViewJavascriptBridge Log
[WebViewJavascriptBridge enableLogging];
// 初始化 WKWebViewConfiguration 物件
self.webConfig = [[WKWebViewConfiguration alloc] init];
// 設定偏好設定
_webConfig.preferences = [[WKPreferences alloc] init];
// 預設為0
_webConfig.preferences.minimumFontSize = 10;
// 預設認為YES
_webConfig.preferences.javaScriptEnabled = YES;
// 在iOS上預設為NO,表示不能自動通過視窗開啟
_webConfig.preferences.javaScriptCanOpenWindowsAutomatically = NO;
// TODO: 請替換成頁面的 url 地址
NSString *URLSTR = @"http://xxx.xxx.xxx.xx:xxxx";
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:_webConfig];
// 設定 UserAgent 字尾
_webView.customUserAgent = [NSString stringWithFormat:self.webView.customUserAgent, @"app"];
_webView.UIDelegate = self;
_webView.navigationDelegate = self;
NSURL *url = [NSURL URLWithString:URLSTR];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[_webView loadRequest:urlRequest];
[self.view addSubview:_webView];
self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
[_bridge setWebViewDelegate:self];
複製程式碼
3、註冊與 H5 端互動的事件函式
// 例:註冊修改 User 名稱的 changeUser 函式
[self.bridge registerHandler:@"changeUser" handler:^(id data, WVJBResponseCallback responseCallback) {
// 在這裡處理邏輯
NSLog(@"JS 傳過來的資料%@", data);
if (responseCallback) {
// 執行回撥函式
responseCallback(@"返回給 JS 的資料");
}
}];
複製程式碼
4、呼叫 H5 端事件函式
// 呼叫 H5 介面的 changeName 事件函式
[self.bridge callHandler:@"changeName" data:name responseCallback:^(id responseData) {
NSLog(@"JS 呼叫 OC 回撥函式返回的值:%@", responseData);
}];
複製程式碼
四、Android 端程式碼
- 註冊與
H5
互動的事件函式:public void registerHandler(String handlerName, BridgeHandler handler) { if (handler != null) { messageHandlers.put(handlerName, handler); } } 複製程式碼
- 呼叫
H5
端事件函式public void callHandler(String handlerName, String data, CallBackFunction callBack) { doSend(handlerName, data, callBack); } 複製程式碼
- 註冊與
H5
互動的預設事件,即H5
端不呼叫函式名,直接使用send
函式傳遞資料,安卓端也可以在這個事件中接收到資料// 設定預設接收函式 public void setDefaultHandler(BridgeHandler handler) { this.defaultHandler = handler; } 複製程式碼
- 呼叫 H5 端註冊的預設事件函式
@Override public void send(String data, CallBackFunction responseCallback) { doSend(null, data, responseCallback); } 複製程式碼
1、引入 BridgeWebView
- 在專案的 build.gradle 檔案中新增如下程式碼:
buildTypes { // ... repositories { // ... maven { url "https://jitpack.io" } } } 複製程式碼
- 新增依賴:
implementation 'com.github.lzyzsd:jsbridge:1.0.4'
2、初始化 BridgeWebView
在 activity_main.xml
檔案中新增布局
<com.github.lzyzsd.jsbridge.BridgeWebView
android:id="@+id/main_wv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.github.lzyzsd.jsbridge.BridgeWebView>
複製程式碼
在 MainActivity 中初始化 BridgeWebView
mWebView = findViewById(R.id.main_wv);
mWebView.getSettings().setAllowFileAccess(true);
mWebView.getSettings().setAppCacheEnabled(true);
mWebView.getSettings().setDatabaseEnabled(true);
// 開啟 localStorage
mWebView.getSettings().setDomStorageEnabled(true);
// 設定支援javascript
mWebView.getSettings().setJavaScriptEnabled(true);
// 進行縮放
mWebView.getSettings().setBuiltInZoomControls(true);
// 設定UserAgent
mWebView.getSettings().setUserAgentString(mWebView.getSettings().getUserAgentString() + "app");
// 設定不用系統瀏覽器開啟,直接顯示在當前WebView
mWebView.setWebChromeClient(new WebChromeClient());
mWebView.setWebViewClient(new MyWebViewClient(mWebView));
mWebView.loadUrl(URL);
複製程式碼
3、註冊與 H5 互動的事件函式
// 預設事件函式
mWebView.setDefaultHandler(new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
Toast.makeText(MainActivity.this, data, Toast.LENGTH_LONG).show();
function.onCallBack("安卓返回給 JS 的訊息內容");
}
});
// 普通事件函式
mWebView.registerHandler("reloadUrl", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
mWebView.reload();
Toast.makeText(MainActivity.this, "重新整理成功~", Toast.LENGTH_SHORT).show();
function.onCallBack("");
}
});
複製程式碼
4、呼叫 H5 端事件函式
// 呼叫 H5 端預設事件函式
mWebView.send("安卓傳遞給 JS 的訊息", new CallBackFunction() {
@Override
public void onCallBack(String data) {
Toast.makeText(MainActivity.this, data, Toast.LENGTH_LONG).show();
}
});
// 呼叫 H5 端普通事件函式
mWebView.callHandler("changeName", mEditName.getText().toString(), new CallBackFunction() {
@Override
public void onCallBack(String data) {
Toast.makeText(MainActivity.this, "name 修改成功", Toast.LENGTH_SHORT).show();
mEditName.setText("");
}
});
複製程式碼
5、新增網路許可權
這一步是必須的,否則的話, WebView 載入不出來,手機介面會提示 Webpage not available
。
-
在
AndroidManifest.xml
清單檔案中新增:<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> 複製程式碼
-
新增了許可權之後,網頁可能還是載入不出來,可能是因為對未加密的流量不信任,在
AndroidManifest.xml
的application
中新增一個屬性:android:usesCleartextTraffic="true"
。如下:<?xml version="1.0" encoding="utf-8"?> <manifest ...> <application ... android:usesCleartextTraffic="true"> ... </application> </manifest> 複製程式碼
五、Tips
1、在 Swift 中使用 WebViewJavascriptBridge
和在 OC 中使用類似,直接將下載好的 WebViewJavascriptBridge
資料夾拖到 Swift
專案中。
但此時還不能直接使用,因為 WebViewJavascriptBridge
中使用 OC
編寫的。所以需要先建立一個標頭檔案,名為:專案名-Bridging-Header.h,將需要用到的 OC
的標頭檔案引入進去。
這裡我建議不要手動建立,可以讓 XCode
自動幫忙建立。建立方式:在 Swift
專案中建立一個 OC
檔案:
之後 XCode 會自動提示建立 Bridging Header
:
建立之後,在資料夾中引入 WebViewJavascriptBridge.h
標頭檔案:
最後,就可以在 Swift
程式碼中正常使用 OC
編寫的方法了:
2、執行 XCode 專案報錯,控制檯提示:Unknown class ViewController in Interface Builder file.
解決辦法:
開啟 Main.storyboard
檔案,按照下圖所示,找到箭頭所指輸入框中的 ViewController
,刪除掉,之後再重新輸入,找到新的 ViewController
,填進去即可:
六、參考連結
七、Demo 地址
jsbridge-example
如果有所幫助,歡迎 Star!