iOS与HTML5交互方法总结

根据网上的文章和最近使用的经历,做了下面一篇关于iOS与HTML5交互方法的分析,不过多描述具体代码。
大概有以下几种方式:

1. 利用WKWebView进行交互(系统API) -ios 8以后
2. 利用UIWebView进行交互(系统API) -ios 7前常用
3. 苹果的javascriptcore.framework框架; -ios 7以后
4. 跨平台cordova框架;
5. 第三方WebViewJavascriptBridge(作者N久没有更新,有提交issuse未解决,用的人最多)
6. 第三方DSBridge等

先说下最后的实现方案是:WKWebView+JSBridge

历史

iOS7之前兼容 -URL拦截

在iOS7之前,实现js与oc的交互,方式为向UIWebView发送stringByEvaluatingJavaScriptFromString:消息来执行一段JavaScript的脚本。

而且如果想用JavaScript来调用Objective-C的方法,必须打开一个自定义的URL(例如:Nanshanyi://),然后在UIWebView的delegate方法webView:shouldStartLoadWithRequest:navigationType中进行处理 实质就是上述的利用UIWebView进行交互(系统API)

这种JS调用OC的方法的缺点十分明显,需要繁琐地解释字符串得到相应的方法名和传值,且调用的方法也不能传递返回值;
优点是:不需要等待页面加载完才触发,当相应的代码被运行就能调用OC的方法(相比 JavaScriptCore而言)

发展

ios7以后-JavaScriptCore

JavaScriptCore这个框架是iOS7之后苹果推出的,方便了开发者的使用,让web页面和iOS本地原生应用交互起来更加简单。
JavaScriptCore是webkit的一个重要组成部分,主要是对JS进行解析和提供执行环境。
代码是开源的:https://github.com/phoboslab/JavaScriptCore-iOS

JavaScriptCore这里有个坑,就是oc调用js,必须是html加载完成之后才可以。
UIWebVIew支持,WKWebView不能够利用javaScriptCore交互。
使用方法百度很多,不具体描述。

代码示例:

Objective-C 调用 JavaScript

self.context = [[JSContext alloc] init];

NSString *js = @"function add(a,b) {return a+b}";

[self.context evaluateScript:js];

JSValue *n = [self.context[@"add"] callWithArguments:@[@2, @3]];

步骤很简单,创建一个JSContext对象,然后将JS代码加载到context里面,最后取到这个函数对象,调用callWithArguments这个方法进行参数传值。(JS里面函数也是对象)

JavaScript 调用 Objective-C

self.context = [[JSContext alloc] init];
self.context[@"add"] = ^(NSInteger a, NSInteger b) {
    NSLog(@"---%@", @(a + b));
};
[self.context evaluateScript:@"add(2,3)"];

1.WKWebView(系统API)
题外话:WKWebView优点:

  • 更多的支持HTML5的特性
  • 官方宣称的高达60fps的滚动刷新率以及内置手势
  • Safari相同的JavaScript引擎
  • 将UIWebViewDelegate与UIWebView拆分成了14类与3个协议(官方文档说明)
  • 另外用的比较多的,增加加载进度属性:estimatedProgress

缺点:

WKWebView 不支持二级页面缓存 和 NSURLProtocol 拦截了

因为在js发送消息给native的时候,有时候需要通过回调来获取相应的信息,仅仅靠上面两个方法是没有办法满足的,也可能会有小伙伴说,先通过上面方法1发送消息个native然后,再使用方法2发送消息给js不就好了么,不行的,这样的话,js调用native方法时,和native发送消息时候并没有时间先后的约定,不能保证,js获取相关返回值的时候,一定能拿到值。

-iOS端添加

[_webView configuration].userContentController =  [[WKUserContentController alloc]init];

//添加监听

[[_webView configuration].userContentController addScriptMessageHandler:self name:@"sendNativeAction"];

//WKScriptMessageHandler协议方法

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
  if ([message.name isEqualToString:@"sendNativeAction"]){
    [self anlyicesResp:message.body];
  }
}

JS端添加

1.代码需要判断安卓还是iOS移动端

1.  $(function(){  
2.  var u = navigator.userAgent, app = navigator.appVersion;   
3.    var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //android终端或者uc浏览器   
4.  var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端   
5.  alert('是否是Android:'+isAndroid);   
6.  alert('是否是iOS:'+isiOS);  
7.  if(isAndroid){  
8.  }  
9.  }); 
10.  window.webkit.messageHandlers.xxx.postMessage(JSON.stringify(json))
WebViewJavascriptBridge与JS交互

WebViewJavascriptBridge同时支持UIWeView和WKWebView,无论是JS调用OC还是OC调用JS,都可以正常传值和返回值;而且在页面加载时只要JS代码被运行就可以进行交互,上面遇到的缺点和坑在这里都被掩埋的,所以是现在处理交互的主流做法。

就是在OC环境和Javascript环境各自保存一个相互调用的bridge对象,每一个调用之间都有id和callbackid来找到两个环境对应的处理

WebViewJavascriptBridge是网上很火的一个交互库,使用的人较多,但是对于js基础较弱的小伙伴来说,底层不是太好理解。底层和easy-js一样都是通过创新一个隐藏的iframe通过截取url来进行交互。WebViewJavascriptBridge本身是有bug的,作者本身也停止了更新,网上有许多框架基于它的基础上做了优化。

 //设置能够进行桥接
[WebViewJavascriptBridge enableLogging];
//注册handler在Object-C,如果有self.webView.delegate = self。应注释掉,否则注册方法不执行
self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
//如果想执行UIWebView的代理方法,需设置
[self.bridge setWebViewDelegate:self];
JS调用OC

JS调用原生方法getUsername,可以使用如下方式注册。JS需要返回值的可以用responseCallback将返回值传过去。

[self.bridge registerHandler:@"getUsername" handler:^(id data,WVJBResponseCallback responseCallback) {
    NSLog(@"%@",data);
    responseCallback([self getUsername]);
}];
OC调用JS

OC调用JS可以使用如下方法实现,如果需要传参,可以写到参数data:里,如果没有参数就传nil

//OC调JS的方法

[self.bridge callHandler:@"testJavascriptHandler" data:nil   responseCallback:^(id responseData) {
    NSLog(@"ObjC received response: %@", responseData);
}];
JSBridge

JSBridge是偶然看到的一个Native和JS的通信框架,因为已经用了WebViewJavascriptBridge,这个框架作为了解,Native只通过一个固定的桥对象调用JS,JS也只通过固定的桥对象调用Native,基本原理是:

H5->通过某种方式触发一个url->Native捕获到url,进行分析->原生做处理->Native调用H5的JSBridge对象传递回调。
github地址:https://github.com/lzyzsd/JsBridge

  • android4.2以下,addJavascriptInterface方式有安全漏掉
  • iOS7以下,JS无法调用Native

JsBridge的核心

JsBridge之所以能实现Native与Js相互调用的功能,其核心实现其实就是:

拦截Url
load url("javascript:js_method()");

先说第二点,Native调用Js,通过加载以javascript:开头的url即可实现调用Js的方法。这个很好理解,在web中也是通过这种方式调用Js的方法的。
然后细说下第一点的实现:

向body中添加一个不可见的iframe元素。通过拦截url的方法来执行相应的操作,但是页面本身不能跳转,所以改变一个不可见的iframe的src就可以让webview拦截到url,而用户是无感知的。
拦截url。通过shouldOverrideUrlLoading来拦截约定规则的Url,再做具体操作。
Ps: 添加iframe是H5自身可实现的,但是如果H5来实现的话,需要每个页面实现,且耦合较高;因此放在库里,通过加载完成注入的方式,则会降低耦合

你可能感兴趣的:(iOS与HTML5交互方法总结)