iOS原生和H5的交互调研笔记

第一部分原理解析

UIWebView

UIWebView,苹果在iOS2.0推出。

** UIWebView与JS交互原理:**

H5->通讯协议->原生

原生->回调函数->H5

** 理解:**

  1. 交互之前,制定好H5和原生的交互通讯协议:类似于Http协议;
  2. H5通过触发能被原生监听并捕获截拦的H5行为,原生通过Web代理拦截,获取通讯协议,解析匹配后会路由到具体处理方法,执行原生能力逻辑,比如业务逻辑处理,或者界面跳转。
  3. 原生在处理完数据的时机把数据通过API回调传递给H5,让H5做相应的展示。

     能被原生监听并捕获截拦的H5行为: console.log、alert、confirm、prompt、location.href 等。
    
     举例:location.href ='jsbridge://method?a=2&b=3#h5MethodTag'
    	
     协议名:jsbridge://,app 自定义的协议名,用于H5触发行为的监控捕获;
    	
     接口路径:method,原生具体能力路径,不同原生能力路径不同;
    	
     参数:a=2&b=3
    	
     回调:h5MethodTag,原生回调H5的方法标识;
    

** 第一部分:H5->原生 **

通过shouldStartLoadWithRequest代理方法拦截H5事件,解析协议路由到具体方法。

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

** 第二部分:原生->H5 **

在webViewDidFinishLoad的时机,通过stringByEvaluatingJavaScriptFromString回调JS函数,传递,或者更改H5界面元素。

- (void)webViewDidFinishLoad:(UIWebView *)webView;//UIWebView代理方法
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;//UIwebView的实例方法,可以获取H5界面元素,调用原有的JS方法,也可注入新的JS代码,调用新注入的代码

关于如何解藕H5,实现更好的跨平台,查阅文章 解耦—Hybrid H5跨平台性思考

WKWebView

iOS8以后,苹果推出了新框架Wekkit,提供了替换UIWebView的组件WKWebView。

** 第一部分:H5->原生 **

JavaScript调用原生

	    window.webkit.messageHandlers.jsCallOC.postMessage(dict);

原生响应调用,需要原生先通过WKUserContentController注册方法。WKUserContentController还有一个功能注入js。

WKUserContentController *userContentController = [[WKUserContentController alloc] init];

[userContentController addScriptMessageHandler:self name:@"jsCallOC"];

原生遵循WKScriptMessageHandler代理处理路由

#pragma mark - WKScriptMessageHandler代理处理JS调用OC
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSLog(@"方法名:%@", message.name);
    NSLog(@"参数:%@", message.body);
        // 方法名
    NSString *methods = [NSString stringWithFormat:@"%@:", message.name];
    SEL selector = NSSelectorFromString(methods);
        // 调用方法
    if ([self respondsToSelector:selector]) {
        [self performSelector:selector withObject:message.body];
    } else {
        NSLog(@"未实行方法:%@", methods);
    }

    
}

** 第二部分:原生->H5 **

原生调用JS,javaScriptString是js的函数名,evaluateJavaScript是WKWebView的实例方法。

- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

原生还提供监听Prompt,alert,Confirm的代理方法

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler {
    
}

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
    
}

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    
}

上面两个webView的单单从与H5交互这个角度进行对比,很多功能UIWebView和WKWebView都可以实现,UIWebView需要处理更多的细节,WKWebView的封装性更好,而且提供了更多的方法可以调用。

上面都是通过iOS原生控件直接加载web界面,使用控件的原生方法和H5进行交互,下面将使用iOS原生框架*JavaScriptCore*框架,和第三方框架*WebViewJavascriptBridge*来实现与H5的交互
WebViewJavascriptBridge

WebViewJavascriptBridge提供了对UIWebView和WKWebView两个原生控件的支持。只需要根据控件选择对应的bridge即可,交互API基本不变。

** 第一部分:H5->原生 ** H5通过bridge.callHandler 或者 bridge.send触发事件调用原生,原生通过下面代码中注册的方法,响应H5的调用。

_bridge = [WebViewJavascriptBridge bridgeForWebView:_webView];
[_bridge registerHandler:@"handleName" handler:^(id data, WVJBResponseCallback responseCallback) {
        //JS调用原生,原生响应处理逻辑。
 }];

**第二部分:原生->H5 **

原生通过👇三个方法调用JS方法,handlerName可以是JS的函数名,data是参数

- (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
JavaScriptCore

JavaScriptCore框架是苹果在iOS7.0推出的iOS和JS交互的新方案,更加面向对象。UIWebView和WKWebKit都可使用JavaScriptCore框架。注意,** 听说 **WKWebView中鉴于一些线程和进程的问题有无法获取JSContext的现象。

** 第一部分:H5->原生 **

JS调用原生的方法

   <input type="button" value="Call ObjC system camera" onclick="OCModel.callSystemCamera()">
   <input type="button" value="Call ObjC system alert" onclick="OCModel.showAlertMsg('js title', 'js message')">

原生需要实现OCModel对象的绑定

  1. 声明一个继承JSExport协议的协议JavaScriptObjectiveCDelegate,并且声明让JS调用的方法
  2. 创建一个类遵循JavaScriptObjectiveCDelegate协议的对象

    @interface NLJsObjCModel : NSObject

    @property (nonatomic, weak) JSContext *jsContext;

    @property (nonatomic, weak) UIWebView *webView;

    @end

  3. 实现绑定

     NLJsObjCModel *model  = [[NLJsObjCModel alloc] init];
        
     self.jsContext[@"OCModel"] = model;
        
     model.jsContext = self.jsContext;
        
     model.webView = self.webView;
    

JavaScriptCore框架使用这部分代码来源于IOS 原生与HTML交互

** 第二部分:原生->H5 **

获取JSContext对象,对象调用evaluateScript:获取JSValued对象,最终实现与JS的交互。

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    JSContext *jsContext = [webView     valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    JSValue *jsValue = [jsContext evaluateScript:@"showAppAlertMsg"];
    [jsValue callWithArguments:@[@"这是app本地交互文案"]];
}

总结

这些方法都可以实现交互,UIWebView需要指定协议,处理更多细节;WKWebView和WebViewJavascriptBridge的使用模式很像,可以看出相比于UIWebView的交互流程已经简洁很多;WebViewJavascriptBridge提供了对UIWebView和WKWebView的支持,而且和JS的交互过程很简洁。JavaScriptCore是实现了面向协议的封装,看起来稍微复杂,但是代码的封装性会更好。

很多公司的老代码还是比较多的使用UIWebView的自定义协议拦截的方法,所以这方面的使用需要了解;对于新的代码,还是希望可以采用JavaScriptCore或者WebViewJavascriptBridge 结合WKWebView的方式。

最近的文章

智能设备数据传输调研

关注的智能产品:智能手环,电子体脂秤,智能门禁报警设备,智能摄像头。基于蓝牙数据传输的设备有智能手环,电子体脂秤,智能水杯等。设备本身会有蓝牙硬件,可以和移动设备进行数据传递。典型的交互模式是智能手环,手环通过硬件接收用户的身体状况信息,与移动设备(android手机应用,或者iPhone手机应用)进行数据传输。用户通过移动设备的应用可以设置手环,也可以从手环获取用户数据。手环可以给接收移动设备对手环的设置信息,也可以发送用户数据给移动设备。BLE的特点低功耗,带宽很低,传输的数据量小,一...…

继续阅读
更早的文章

Jenkins 持续集成

操作系统:Mac OSstep 1:[点击此处到jenkins官网下载Mac OS的安装包](https://jenkins.io/index.html)step 2:下载完成后,点击Jenkins的pkg安装包,完成安装引导步骤。会碰到的问题:How to “Unlock Jenkins”?解决方案:在终端执行👇的命令,可以获取密码。ps(路径需要更换成你的initialAdminPassword的提示路径)sudo more /Users/Shared/Jenkins/Home/sec...…

继续阅读