『ios』如何完成Hybrid-App交互层插件化?
Hybrid App就是h5跟原生混合开发,拿有些h5不好实现的功能,让原生来实现,两者相互配合也就是Hybrid App。
公司的项目有很大一部分都是拿混合开发的,下面从几方面来说下自己的心得吧。
首先是base基类,针对适配8.0之前的UIwebview和8.0之后的wkwebview,基类选的好真的可以省很多事
这里选用RSWebView来同时适配8.0之前跟之后的版本。
然后以RxWebViewController为基类进行封装自己的webviewController。
h5跟native层的交互,这里选择WebViewJavascriptBridge
对于交互方面,这里有些好的建议。
- 不管怎么改交互方式,我们必须要在原生里注册好可以供原生调用的方法,如果是一个方法两个方法,我们可以提前注册,然后供js端调用,但是对于大型Hybrid App来说,这样肯定是不符合规定。
解决方法:在全局提前注册一个方法 'callNativeToDo',所有需要调用的方法都走交互,在js端放到一个对象里,传过来,然后运用runtime来动态的调用方法。
{
action: 'close_h5',
data: {},
callback: 0
}
/**
* 注册 js 要调用的Native 功能
*/
- (void)registerNativeFunctions
{
__weak typeof(self) weakSelf = self;
[self.webView registerHandler:EXTERN_INTERFACE_ENTRANCE handler:^(id data, WVJBResponseCallback responseCallback) {
[weakSelf h5InvokeNativeWithOriginData:data handler4Callback2JS:responseCallback];
}];
}
// h5调用native方法
- (void)h5InvokeNativeWithOriginData:(id _Nonnull)originData
handler4Callback2JS:(WVJBResponseCallback _Nullable)handler4Callback2JS{
if (![originData isKindOfClass:[NSDictionary class]]) {
//如果不是字典类型,直接返回
return;
}
NSDictionary *dicData = (NSDictionary *)originData;
NSString *actionName = [dicData objectForKey:@"action"];
NSDictionary *argument = [dicData objectForKey:@"data"];
TVMLOG(@"actionname = %@",actionName);
if (!actionName) {
return;
}
//拼接函数签名参数
NSString *methodName = [NSString stringWithFormat:@"%@:callback:",[self.dataHandler.protocolDic objectForKey:actionName]];
SEL runMethod = NSSelectorFromString(methodName);
if ([self respondsToSelector:runMethod]) {
((void(*)(id,SEL, id,id))objc_msgSend)(self, runMethod, argument, handler4Callback2JS);
}
}
上面的self.dataHandler.protocolDic 中存放的就是每个js端传过来的方法,对应的键值对。
self.dataHandler.protocolDic = @{@"get_userinfo":@"invokeGetUserInfoWithParam",@"get_geo":@"invokeGetUserLocationWithParam"};
要想用runtime的方法,我们就要全局统一方法样式。比如如下的方法
// H5通知本地发送消息
- (void)invokeSendLocalMsgWithParam:(id _Nonnull)originData callback:(WVJBResponseCallback _Nullable)handler4Callback2JS;
// 通知H5是否点“返回”直接返回
- (void)invokeClickBackDirectBackWithParam:(id _Nonnull)originData callback:(WVJBResponseCallback _Nullable)handler4Callback2JS;
上面是native层的基本思路。
对于h5端。举个例子
对于WebViewJavascriptBridge的注册,文档都有,看下主要代码就可以
export function closeWindow () {
return appAction({
action: 'close_h5',
data: {},
callback: 0
})
}
function appAction (params) {
return new Promise((resolve, reject) => {
try {
let WebViewJavascriptBridge = getWebviewBridge()
WebViewJavascriptBridge.callHandler('callNativeToDo', params, function (res) {
if (res) {
console.log(res+'aaaa')
resolve(JSON.parse(res))
} else {
console.log(res+'bbb')
resolve(res)
}
console.log(res+'ccc')
})
} catch (e) {
console.error(e)
reject(e)
}
})
}
插件化hybird App?
这算是我理解的插件化的使用吧。
上面说的是交互方面的一些问题,我觉得是可以解决实际问题的,下面从代码设计层面来说下,自己对于Hybrid App插件化的理解。
如果一个app中有1000个native层同js端的交互方法,而且有些交互很复杂,比如下载图片,下载视频,支付等等,我们又该如何去做?
这样插件化就来了,首先我们可以用分类的方法,把交互方法都分发出去。这样还没完,我们还需要建立一个WebsiteDataHandler的类,来专门进行插件化的管理。
WebsiteDataHandler这个类,首先可以存放我们放交互方法的字典对象,然后就是作为插件的集合中心。
举个例子,比如这里有个绑定的业务层
// 绑定(手机号,微信)成功
- (void)invokeBindSuccessWithParam:(id _Nonnull)originData callback:(WVJBResponseCallback _Nullable)handler4Callback2JS{
if(!self.dataHandler.bindSuccessService){
self.dataHandler.bindSuccessService = [BindSuccessService new];
}
[self.dataHandler.bindSuccessService operateWithData:originData handler4CallbackJS:handler4Callback2JS];
}
self.dataHandler.bindSuccessService这里的bindSuccessService就是我口中所说的插件,把逻辑分发到这里面,来处理,
- (void)operateWithData:(NSDictionary *_Nullable)data
handler4CallbackJS:(WVJBResponseCallback _Nullable)handler4CallbackJS{
NSNumber *bind_type = [data objectForKey:@"bind_type"];
NSString *mobile_number = [data objectForKey:@"telephone_num"];
NSString *unionid = [data objectForKey:@"unionid"];
if (bind_type.integerValue == 1) {
// 微信绑定成功,更新用户信息
}else{
//手机号绑定成功
}
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@{@"state":@0} options:NSJSONWritingPrettyPrinted error:nil];
NSString *dictstr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
handler4CallbackJS(dictstr);//传给js端
}
上面只是一个业务层的例子,如果有好多业务层,我们必须确保代码的可复用性,清晰性,易于维护性,我相信插件化是一个很好的解决方案。