最近研究 WebViewJavaScriptBridge ,网上原理讲的大致都一样,结合自己看看源码,流程都弄清楚了,总结一下。
1 WebViewJavaScriptBridge 配合 WKWebView 使用的还是 拦截URL的方法 实现 JS 与 WebView的交互。简单的使用,先在页面加载完后,注入JS到页面,JS 方法调用时会将参数等转换使webview能拦截识别。内部使用 类似字典的机制,存储注册的方法名,和相应的处理。
2 官方文档给的注册方法的例子
[self.jsBridge registerHandler:jsName
handler:^(id data, WVJBResponseCallback responseCallback) {
//
}];
这种情况简单的还好,但是涉及到方法比较多的时候,会比较头疼。
我的解决方案是创建一个类,专门处理 JS 的调用。类中方法名字和 JS 调用的相同,通过 runtime 获取方法列表,然后向 self.jsBridge 注册方法。通过分发的方式调用方法。最后的样子是这样
//注册 JS
for (NSString *jsName in [self.helper.ymHelperMethodDic allKeys]) {
__weak typeof(self) weakSelf = self;
[self.jsBridge registerHandler:jsName
handler:^(id data, WVJBResponseCallback responseCallback) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf.helper ymHelperHandleName:jsName
data:data
callBack:responseCallback];
}];
}
具体的解决思路,先获取方法列表,和转换后的 JSName ,记得过滤掉不相干的方法。
/**
* 通过runtime 获取方法列表
*
* @return 方法名 和 js名 组成 的 字典
*/
- (NSDictionary *)ymHelperMethodListDic
{
unsigned int count;
Method *methods = class_copyMethodList([WebViewJSHandler class], &count);
NSMutableDictionary *methodDictionary = [NSMutableDictionary dictionaryWithCapacity:count];
for (int i = 0; i < count; i++)
{
Method method = methods[i];
SEL selector = method_getName(method);
NSString *name = NSStringFromSelector(selector);
/**
* 过滤掉私有的方法
*/
if (![name containsString:@"mHelper"] &&
![name containsString:@"."] &&
![name containsString:@"dealloc"]) {
NSString *jsName = [[name componentsSeparatedByString:@":"] firstObject];
NSLog(@"方法 名字 ==== %@",name);
NSLog(@"js 名字 ==== %@",jsName);
[methodDictionary setObject:name forKey:jsName];
}
}
return methodDictionary;
}
拿到字典成功一半。通过 JSName 转换 SEL ,然后再做转发。
/**
* 通过 methodName 做转发
*
* @param methodName 方法名称
* @param data 数据
* @param responseCallback 回调
*/
- (void)ymHelperHandleName:(NSString *)methodName
data:(id)data
callBack:(YMResponseCallback)responseCallback{
NSString *selString = [self ymHelperFindSELByJSName:methodName];
//转换 string to SEL
SEL sel = NSSelectorFromString(selString);
if ([self.ymHelperWebViewJSHandler respondsToSelector:sel]) {
//忽略警告
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.ymHelperWebViewJSHandler performSelector:sel withObject:data withObject:responseCallback];
#pragma clang diagnostic pop
}
}
然后通过 performSelector 的方式做转发。
在 web view里面 就通过这样就搞定了
//注册 JS
for (NSString *jsName in [self.helper.ymHelperMethodDic allKeys]) {
__weak typeof(self) weakSelf = self;
[self.jsBridge registerHandler:jsName
handler:^(id data, WVJBResponseCallback responseCallback) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf.helper ymHelperHandleName:jsName
data:data
callBack:responseCallback];
}];
}
不用在 一个 controller 里面写一堆注册方法的实现。