shmily-iOS/Mac

『ios』如何完成Hybrid-App交互层插件化?

2019-01-25  本文已影响29人  butterflyer
5ab5c9ea15ce36d3046a368b32f33a87e950b11b.jpg

Hybrid App就是h5跟原生混合开发,拿有些h5不好实现的功能,让原生来实现,两者相互配合也就是Hybrid App。

公司的项目有很大一部分都是拿混合开发的,下面从几方面来说下自己的心得吧。

首先是base基类,针对适配8.0之前的UIwebview和8.0之后的wkwebview,基类选的好真的可以省很多事

这里选用RSWebView来同时适配8.0之前跟之后的版本。
然后以RxWebViewController为基类进行封装自己的webviewController。

h5跟native层的交互,这里选择WebViewJavascriptBridge

对于交互方面,这里有些好的建议。

  1. 不管怎么改交互方式,我们必须要在原生里注册好可以供原生调用的方法,如果是一个方法两个方法,我们可以提前注册,然后供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端
}

上面只是一个业务层的例子,如果有好多业务层,我们必须确保代码的可复用性,清晰性,易于维护性,我相信插件化是一个很好的解决方案。

上一篇下一篇

猜你喜欢

热点阅读