Hybrid技术使用记录

本次记录的是关于在项目开发中使用Hybrid技术的实践记录。主要使用的是WKWebview,关于UIWebView只有一些简单的了解,当然做了一些简单的对比后,在新建的项目中还是建议使用WKWebview

前言

在最近的项目中很多页面都是前端开发然后和iOS端进行交互。理由当然是前端的更新方便快速。在项目中并没有使用 WebViewJavascriptBridge 和 cordova 这些比较著名的Hybrid技术框架。大部分使用的还是WKWebview提供的原生Api。对于我的项目也是够用。

实现方案


JS 调用 Native 的几种通信方案

  1. 拦截跳转请求
  2. 弹窗拦截
  3. JS上下文注入

Native 调用 JS 的几种通信方案

由于项目中主要使用的是WKWebview这里使用的主要是WKWebView官方提供的Api

  1. evaluateJavaScript

实现方案分析


JS 调用 Native

  1. 拦截跳转请求

在WKWebview还未问世的时候,苹果提供的是UIWebview,那个时候最常见的通讯方式就是拦截跳转请求,然后从指定的URL中获取需要的信息

//普通的Http地址
https://www.baidu.com/xxxx?xx=xx
//假的请求通信地址
milan://milan123/action?param=obj

一般我们用于通讯的URL地址都是假的跳转链接,目的是为了传递参数。我们可以分析上面的URL

  • 协议:http/https/file等,包括自己定义的milan
  • 域名:www.baidu.com,milan123
  • 路径:action?,xxxx?
  • 参数:xx=xx,param=obj

我们通过webview提供的Api拦截到特定的URL后获得传递的参数,就能做相应的动作

1.UIWebview拦截URL

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    //1 根据url,判断是否是所需要的拦截的调用 判断协议/域名
    if (是){
      //2 取出路径,确认要发起的native调用的指令是什么
      //3 取出参数,拿到JS传过来的数据
      //4 根据指令调用对应的native方法,传递数据
      return NO;
      //确认拦截,拒绝WebView继续发起请求
    }    
    return YES;
}

2.WKWebView拦截URL

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    //1 根据url,判断是否是所需要的拦截的调用 判断协议/域名
    if (是){
      //2 取出路径,确认要发起的native调用的指令是什么
      //3 取出参数,拿到JS传过来的数据
      //4 根据指令调用对应的native方法,传递数据

      //确认拦截,拒绝WebView继续发起请求
        decisionHandler(WKNavigationActionPolicyCancel);
    }else{
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    return YES;
}

弹窗拦截

在webview中分为alert/confirm/prompt三种弹窗。由js发起弹窗,然后移动端拦截弹窗请求,这里的例子主要是prompt

  1. WKWebView拦截prompt弹窗
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler{
    //1 根据传来的字符串反解出数据,判断是否是所需要的拦截而非常规H5弹框
    if (是){
        //2 取出指令参数,确认要发起的native调用的指令是什么
        //3 取出数据参数,拿到JS传过来的数据
        //4 根据指令调用对应的native方法,传递数据
        //直接返回JS空字符串
        completionHandler(@"");
    }else{
        //直接返回JS空字符串
        completionHandler(@"");
    }
}

2.UIWebView从我了解到的信息中不支持拦截弹窗。

JS上下文注入

  1. UIWebView
    由于本次使用的是WKWebview,对于UIWebView中的实现方法只做简单的介绍
  • 通过KVC获取当前webview的js上下文documentView.webView.mainFrame.javaScriptContext
  • 通过拿到的JSContext进行js注入

2.WKWebView

//配置对象注入
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"nativeObject"];
//移除对象注入
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"nativeObject"];

scriptMessageHandler的使用需要记得在页面销毁前进行释放,否则会造成内存泄漏

window.webkit.messageHandlers.nativeObject.postMessage(data);

JS代码中只需要使用上面的方法就能发送信息给客户端

-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    //1 解读JS传过来的JSValue  data数据
    NSDictionary *msgBody = message.body;
    //2 取出指令参数,确认要发起的native调用的指令是什么
    //3 取出数据参数,拿到JS传过来的数据
    //4 根据指令调用对应的native方法,传递数据
}

客户端通过上面的代理方法就能接收信息

Native 调用 JS


evaluatingJavaScript 执行JS代码

function calljs(data){
    console.log(JSON.parse(data)) 
    //1 识别客户端传来的数据
    //2 对数据进行分析,从而调用或执行其他逻辑  
}

//不展开了,data是一个字典,把字典序列化
NSString *paramsString = [self _serializeMessageData:data];
NSString* javascriptCommand = [NSString stringWithFormat:@"calljs('%@');", paramsString];
//要求必须在主线程执行JS
if ([[NSThread currentThread] isMainThread]) {
    [self.webView evaluateJavaScript:javascriptCommand completionHandler:nil];
} else {
    __strong typeof(self)strongSelf = self;
    dispatch_sync(dispatch_get_main_queue(), ^{
        [strongSelf.webView evaluateJavaScript:javascriptCommand completionHandler:nil];
    });
}

上面的例子是使用WKWebview来执行js代码,我们首先在前端的js文件中先协定好一个js方法。我们在使用的时候只需要通过js方法的名字就能调用

WKUserScript 执行JS代码

//在loadurl之前使用
//time是一个时机参数,可选dom开始加载/dom加载完毕,2个时机进行执行JS
//构建userscript
WKUserScript *script = [[WKUserScript alloc]initWithSource:source injectionTime:time forMainFrameOnly:mainOnly];
WKUserContentController *userController = webView.userContentController;
//配置userscript
[userController addUserScript:script]

evaluatingJavaScript能够随时执行js方法。WKUserScript需要预先准备好js代码,在网页加载的时候再逐条执行可扩展性不高

综合对比和选择


  1. 跳转请求拦截

这种实现方式的优点是版本兼容性好,从iOS6开始支持。但是现在最低的版本兼容都从iOS8开始了,微信也从iOS9开始支持。这个方案最初是用在UIWebView上面,从iOS9开始WKWebview的使用明显更多。所以这个优点并不明显

然而他的缺点也比较明显:

  • 丢消息:当我们在同时发送多个请求的时候,部分请求我们是拦截不到的,webview会自动拦截掉后面的请求,因此我们可能会失去一些信息
  • URL长度限制:有时候我们一个请求含有的信息量比较大,所以URL的长度也会过长,webview中对URL的长度也会进行限制,这就导致某些请求无法进行
  1. 弹窗拦截

这种实现方式有明显的优点和缺点:

  • 优点:请求同步执行,前端能够接受到回调,就是说js调用了OC的代码后,OC能够返回一些信息。这样前端就能够确定了是否执行成功,并且能够返回一些参数
  • 缺点:他的缺点还是同步执行,这个弹窗返回的时候前端的整个加载都会停止。如果一些请求需要在页面加载的时候进行的话,就会影响前端页面的正常加载,用户体验十分差
  1. JS上下文注入
  • UIWebview的JSContext注入:上面已经说到项目中比较常用的是WKWebview,而JSContext只支持UIWebview,JSContext支持js的同步返回。这个功能点是比较牛逼的,但是考虑到WKWebview的性能优化,还是放弃了
  • WKWebView的scriptMessageHandler注入:这个是我目前采用的方式.直接支持json数据的传递。简单快捷
  1. evaluatingJavaScript 直接执行JS代码

就是Native主动调用JS的普遍方式缺点和优点就不说了

总结


由于我采用的是WKWebview,所以关于和js的交互一般使用

MessageHandler注入/Prompt弹框拦截(JSToNative) + evaluatingJavaScript (NativeToJS)

这个方案无论是实现方式和稳定性都是最好的,其中弹窗拦截的方式拥有同步返回数据的回调非常友好,倒是前面已经说了这个是同步执行的,甚至会阻碍前端的加载,因此在搭配上需要根据不同的功能进行选择

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,576评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,515评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,017评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,626评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,625评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,255评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,825评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,729评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,271评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,363评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,498评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,183评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,867评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,338评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,458评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,906评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,507评论 2 359

推荐阅读更多精彩内容