jsbridge是客户端和H5沟通的桥梁,经过它,咱们能够获取部分原生能力,同时客户端也能够使用咱们提供的一些方法。实现双向通讯。javascript
客户端能够经过webview里面注入一些javascript的上下文,能够理解为在window对象上挂载了一些方法,而后H5经过特定的对象能够获取到这个方法,反过来也是同样,js挂载了一些方法到window对象上,客户端也就能够调用js的某些方法。前端
JSContext *context = [uiWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
context[@"postBridgeMessage"] = ^(NSArray<NSArray *> *calls) {
// Native 逻辑
};
复制代码
H5使用window.postBridgeMessage(message)
调用java
@interface WKWebVIewVC ()<WKScriptMessageHandler>
@implementation WKWebVIewVC
- (void)viewDidLoad {
[super viewDidLoad];
WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [[WKUserContentController alloc] init];
WKUserContentController *userCC = configuration.userContentController;
// 注入对象,前端调用其方法时,Native 能够捕获到
[userCC addScriptMessageHandler:self name:@"nativeBridge"];
WKWebView wkWebView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
// TODO 显示 WebView
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name isEqualToString:@"nativeBridge"]) {
NSLog(@"前端传递的数据 %@: ",message.body);
// Native 逻辑
}
}
复制代码
H5使用window.webkit.messageHandlers.nativeBridge.postMessage(message)
方式调用。android
来看一下H5端具体实现ios
假设咱们须要一个getUserInfo
方法,用于H5获取当前APP登陆用户的信息。web
那么咱们js能够这样:promise
import registerCallback from '../registerCallback';
export default function getUserInfo() {
return new Promise((resolve, reject) => {
try {
window.webkit.messageHandlers.getUserInfo.postMessage({
callback: registerCallback(resolve),
});
} catch (e) {
reject(e);
}
});
}
复制代码
咱们定义一个getUserInfo
方法,这个方法会去调用window.webkit.messageHandlers.getUserInfo.postMessage
方法,这是客户端写入的方法,而后传入一个callback方法。客户端会经过callback方法把咱们须要的信息返回给咱们。bash
咱们能够再看下registerCallback
这个方法是什么:微信
window.knCallbacks = {};
function makeRandomId(func) {
return `${func.name || 'anonymous'}_${Date.now()}`;
}
export default function registerCallback(callback, keepAlive) {
if (!callback) {
return null;
}
const callbackId = makeRandomId(callback);
window.knCallbacks[callbackId] = (data) => {
let result;
if (typeof data === 'object') {
result = data;
} else if (typeof data === 'string') {
try {
result = JSON.parse(data);
} catch (e) {
result = data;
}
}
callback(result);
if (!keepAlive) {
delete window.knCallbacks[callbackId];
}
};
return `knCallbacks.${callbackId}`;
}
复制代码
在这个方法里面,咱们会为promise的resolve方法生成一个随机函数名,避免出现window可能会有同名函数致使将其覆盖的问题。 而后咱们根据生成的函数名重写一个函数,将这个函数最后返回给外部的callback,传递给了客户端。 这里的data就是客户端调用callback传递过来的数据。 咱们在这里对数据作一些处理,而后给到callback,也就是promise的resolve。dom
客户端实现:
publicclassJavaScriptInterfaceDemoActivityextendsActivity{
private WebView Wv;
@Override
publicvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Wv = (WebView)findViewById(R.id.webView);
final JavaScriptInterface myJavaScriptInterface = new JavaScriptInterface(this);
Wv.getSettings().setJavaScriptEnabled(true);
Wv.addJavascriptInterface(myJavaScriptInterface, "knJSBridge");
// TODO 显示 WebView
}
publicclassJavaScriptInterface{
Context mContext;
JavaScriptInterface(Context c) {
mContext = c;
}
publicvoidpostMessage(String webMessage){
// Native 逻辑
}
}
}
复制代码
H5实现
import registerCallback from '../registerCallback';
export default function getUserInfo() {
return new Promise((resolve, reject) => {
try {
window.knJSBridge.getUserInfo(JSON.stringify({
callback: registerCallback(resolve),
}));
} catch (e) {
reject(e);
}
});
}
复制代码
这里咱们其实能够看到,knJSBridge实际上是咱们和客户端约定的一个字段。而后咱们经过window.knJSBridge
就能够调用客户端的方法了。 registerCallback和IOS的是同样的。
根据上面咱们H5这里最后能够实现这样一个结构的jsbridge
jsbridge
- index.js
- ios/
- index.js
- getUserInfo.js
- android/
- index.js
- getUserInfo.js
复制代码
// jsbridge/index.js
import iosBridge from './iOs';
import androidBridge from './android';
export default class Bridge {
constructor() {
super();
if (isAndroid) {
this.jsbridge = androidBridge;
} else {
this.jsbridge = iosBridge;
}
}
getUserInfo = (...args) => this.jsbridge.getUserInfo(...args);
}
// ios/index.js
import getUserInfo from './getUserInfo';
export {
getUserInfo
}
// ios/getUserInfo.js
import registerCallback from '../registerCallback';
export default function getUserInfo() {
return new Promise((resolve, reject) => {
try {
window.webkit.messageHandlers.getUserInfo.postMessage({
callback: registerCallback(resolve),
});
} catch (e) {
reject(e);
}
});
}
// android/index.js
import getUserInfo from './getUserInfo';
export {
getUserInfo
}
// android/getUserInfo.js
import registerCallback from '../registerCallback';
export default function getUserInfo() {
return new Promise((resolve, reject) => {
try {
window.webkit.messageHandlers.getUserInfo.postMessage({
callback: registerCallback(resolve),
});
} catch (e) {
reject(e);
}
});
}
复制代码
这种方案就是H5构造一个iframe,经过给iframe设置src发起请求,而后客户端拦截请求实现。
通常会将url设为一个特殊的字符串,好比https://__bridge__
这个样子,而后后面跟上咱们须要传递的数据,包括一个callback函数名,客户拦截这种请求,而后经过callback将数据传递回来。