XMWebView
一个轻量级的 Hybrid 框架
原生 -> JS
WKWebView 仅有一个 evaluateJavaScript:completionHandler:
方法来调用 JS,JS 调用原生需要两步
JS -> 原生
- 通过
WKUserContentController
的addScriptMessageHandler:
方法注册一个 MessageHandler - JS 通过
window.webkit.messageHandlers.XM_JS2Native.postMessage(parameters);
来调用原生。
该框架只是对这两个方法的封装,以更加统一的方式,来打通原生 - JS
JSAssembler
JS 组装器,用来组装原生调用 JS 字符串, XMWebViewJSDefaultAssembler
实现了 XMWebViewJSAssemblerProtocol
协议,提供了一个默认的组装器
XM_Native2JS({name: foo,
params:{
foo1: bar1,
foo2: bar2,
}});
可以实现 XMWebViewJSAssemblerProtocol
来定义自己的组装器
JSInvoker
JSInvoker 会将每次 JS 调用封装为 XMWebViewJSInvokeOperation
并放入队列中执行,队列有两个,invokJSQueueWithLoadingState
需要在 webView 加载完成后开始执行;而 invokJSQueueIgnoreLoadingState
则会立即执行。
XMWebViewJSDispatcher
XMWebViewJSDispatcher 是一个派发器,用于分发 JS 调用到 Native 方法。
JSParser
XMWebViewJSDefaultJSParser
实现 XMWebViewJSParserProtocol
协议,用于解析 JS 传递过来的数据,该解析器解析如下默认数据。
JS 调用 Native 的数据格式如下。
XM_JS2Native({name: foo,
params:{
foo1: bar1,
foo2: bar2,
}});
Native 收到的回调数据如下。
message.body = @{
name: foo
params: @{
foo1: bar1,
foo2: bar2,
}
}
XMWebViewJSDefaultJSParser
将解析出类名 XMWebViewFooHandler
,参数 @{@"foo1": bar1, @"foo2": bar2}
。
XMWebViewFooHandler
是继承自 XMWebViewJSDefaultHandlerOperation
的 operation
,这 operation 会被加入主队列中执行。
XMWebViewJSDefaultHandlerOperation
该类是处理业务逻辑的基类,应继承此类来处理自己的业务逻辑。该类已经处理好各种状态机和 JS 传递过来的参数的绑定,子类只需实现 -handleBusiness:
来处理自己的业务,处理完毕后,调用 -callJSBackWithParams:
回调给 JS。
JSCallBackAssembler
XMWebViewJSDefaultCallBackAssembler
实现 XMWebViewJSCallBackAssemblerProtocol
协议,用于构造一个 NSString 来执行 JS。用户可以自定义构造器来定义回调给 JS 的格式。
webView 启动时,Native 主动注入一段 JS,以便 JS 能够调用 Native,当 Native 回调时,将调用 window.XM_NativeCallBackNameMap.funcName(params)
来触发 MessageChannel 的 port1 发送消息,Promise 中的 port2 的 onmessage 将会被调用,然后执行 resolve。
var XM_JS2Native_MessageChannelMap = {};
function XM_JS2Native(parameters) {
var channel = new MessageChannel(); // 创建一个 MessageChannel
if(!window.XM_NativeCallBackNameMap) {
window.XM_NativeCallBackNameMap = {};
}
var id = parameters.name + "_" + Math.random();
parameters.id = id;
XM_JS2Native_MessageChannelMap[id] = channel;
window.XM_NativeCallBackNameMap[parameters.name] = function(nativeValue) {
if (nativeValue.id && XM_JS2Native_MessageChannelMap[nativeValue.id]) {
XM_JS2Native_MessageChannelMap[nativeValue.id].port1.postMessage(nativeValue);
delete XM_JS2Native_MessageChannelMap[nativeValue.id];
}
};
window.webkit.messageHandlers.XM_JS2Native.postMessage(parameters);
return new Promise(function(resolve, reject) {
channel.port2.onmessage = function(e) {
var data = e.data;
resolve(data);
channel = null;
};
});
}
使用方式
JS
XM_JS2Native({name: test, params:{foo1:bar1, foo2:bar2}}).then(nativeValue => {
// 当 Native 回调的时候,这里会被出发
})
Native
XMWebViewTestHandler.h
@interface XMWebViewTestHandler : XMWebViewJSDefaultHandlerOperation
@end
XMWebViewTestHandler.m
#import "XMWebViewTestHandler.h"
@implementation XMWebViewTestHandler
- (void)handleBusiness {
NSLog(@"param: %@", self.parameters);
[self callJSBackWithParams:@{@"arg1": @"foo", @"arg2": @"bar"}];
}
@end
如有疑问,欢迎提交 issue。