【转】使用JavaScriptCore在JS和OC间通信
http://www.cocoachina.com/ios/20160623/16796.html
iOS 开发中,我们时不时的需要加载一些 Web 页面,一些需求使用 Web 页面来实现可以更可控,如上线后也可以发布更新,修改 UI 布局,或者修复 bug,这些 Web 页面的作用不止是展示,很大一部分是需要和原生代码实现的 UI 和业务逻辑发生交互的,那么不可避免的,就需要用一些方法来实现 Web 页面(主要是 JavaScript)和原生代码之间的通信,在 JavaScriptCore 出现之前,很多项目都在用 WebViewJavascriptBridge 作为 Web 页面和原生代码之间的一个桥梁(bridge),来传输一些数据和方法的调用,如 Facebook Messenger,Facebook Paper 等。
WebViewJavascriptBridge 原理简述
WebViewJavascriptBridge 的原理是通过自定义 scheme,在加载一个特定标识的URL( wvjbscheme://__BRIDGE_LOADED__)时在 UIWebView 的代理方法 webView:shouldStartLoadWithRequest:navigationType: 中拦截 URL 并通过 UIWebView 的 stringByEvaluatingJavaScriptFromString: 方法执行一段 JS,这个 JS 文件中声明了一些变量和方法,在通讯中作为一个桥梁,那么怎么通讯呢?
JS 中调用 OC 的方法
在 OC 中,实例化一个 WebViewJavascriptBridge 并调用 registerHandler:handler: 注册并监听一下事件,第一个参数是一个字符串,用来标识一个特定的事件,handler 是一个 block,方法内部将标识作为 key,handler 作为值保存。
- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
_base.messageHandlers[handlerName] = [handler copy];
}
当 JS 中需要调用 OC 的方法时,组装一个类似结构的数据,一个字符串作为标识,将需要传输的数据作为值并保存在一个全局数组中
var sendMessageQueue = [];
function _doSend(message, responseCallback) {
if (responseCallback) {
var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
message['callbackId'] = callbackId;
}
// 主要就是这一行,将 message 保存到全局数组,供待会儿查询
sendMessageQueue.push(message);
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}
并触发一个特定的 URL(wvjbscheme://__WVJB_QUEUE_MESSAGE__),UIWebView 则在 webView:shouldStartLoadWithRequest:navigationType: 中拦截这个 URL,并执行一段 JS(WebViewJavascriptBridge._fetchQueue();)
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
return messageQueueString;
}
查询 JS 中全局数组中的值,并转成 JSON 字符串返回,OC 中拿到 JSON 字符串,并解析,得到一个数组,遍历数组,根据数组中每个对象的 handlerName 查询 OC 中是否有注册这个事件,如果有注册,则根据 handlerName 取出保存在字典中的 block,并执行这个 block,block 可以接收一个 id 类型的参数,将 JS 全局数组中根据 handlerName 取出来的数据作为参数传入 block。这样就实现了从 JS 到 OC 中的数据传输。
OC 中调用 JS 的方法
OC 中调用 JS 的方法相对简单,因为 UIWebView 可以主动执行 JS,JS 中可以将需要监听的事件注册,同样是字符串作为标识,一个函数作为值,保存到一个全局对象中,在 OC 中主动执行特定的 JS 方法时,将数据封装成 JSON 字符串,传入标识符和数据,并遍历 JS 中保存 handler 的全局对象,看有没有注册相应的事件,如果有,根据 事件的名字得到一个函数并执行。实现了 OC 调用 JS 中的方法并向 JS 中传输数据。
JavaScriptCore 时代的通讯
iOS 7 开始,苹果提供了一个叫作 JavaScriptCore 的框架,使用 JavaScriptCore 框架可以实现 OC 和 JS 的互相调用,而不需要依赖「桥」来实现,怎么通讯呢?
JavaScriptCore 中 OC 调用 JS 方法
在 JS 中定义一个方法
function alertFunc() {
window.alert("这是一个JS中的弹框!")
}
在 webViewDidFinishLoad: 代理方法中,获取到 JSContext 对象
- (void)webViewDidFinishLoad:(UIWebView *)webView {
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
[context setExceptionHandler:^(JSContext *ctx, JSValue *expectValue) {
NSLog(@"%@", expectValue);
}];
self.context = context;
}
在一个 button 的点击事件中可以根据 JS 定义的方法的名字获得一个 JSValue 类型对象,这个对象就是在 JS 中定义的方法,JSValue 对象通过调用 callWithArguments: 方法,执行这个 JS 方法。
- (IBAction)buttonClick:(UIButton *)sender {
if (!self.context) {
return;
}
JSValue *funcValue = self.context[@"alertFunc"];
[funcValue callWithArguments:nil];
}
点击按钮时,效果如下。

实现了 OC 中调用 JS 的方法。
JS 调用 OC 中的方法
在 OC 中,通过给 JSContext 的一个 key 赋值,值为一个 block,key 是 JS 中调用的方法的名字,代码如下:
self.context[@"ocAlert"] = ^{
// block 异步执行,如果涉及到 UI 的操作需要回到主线程操作
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:@"这是OC中的弹框!" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[alert dismissViewControllerAnimated:YES completion:^{
}];
}]];
[strongSelf.navigationController presentViewController:alert animated:YES completion:nil];
});
};
在 Web 页面中创建一个 button 并设置 button 的 onClick 事件调用 ocAlert 方法
点击这里
点击 Web 页面上的 button 按钮,效果如下

实现了 JS 调用 OC 中的方法。
是不是方便了很多?
写在后面
嗯 ,一篇文章应该有个写在后面的。
以上当然只是 JavaScriptCore 框架的一个很小的应用,使用 JavaSciptCore 框架结合 Objective-C 的动态性可以做很多事,比如著名的热修复框架 JSPatch 就是这两者的结合。这里只是演示了 JS 和 OC 之间的方法调用,并没有传输数据,JavaScriptCore 框架是很容易的实现两者之间的数据传输的。具体做法可以参考参考资料。
苹果添加的这些新特性可以给开发带来很多便利,就是不知道有坑没有,嗯,且爬且珍惜吧。
使用 JavaScriptCore 进制通讯的 demo 放到了 GitHub,地址如下:
https://github.com/cielpy/CPYJSCoreDemo
参考资料
【转】使用JavaScriptCore在JS和OC间通信的更多相关文章
- UIWebView的应用和其中的JS与OC间传值
现在有很多的应用已经采用了WebView和html语言结合的开发模式.html5一直很火因为一份代码可以在多个平台上运用啊,效果各不相同都很美观,也越来越有一些公司直接招后台程序员和html5程序员, ...
- JS和OC间的通信(使用JavaScriptCore)
JavaScriptCore 时代的通讯 iOS 7 开始,苹果提供了一个叫作 JavaScriptCore 的框架,使用 JavaScriptCore 框架可以实现 OC 和 JS 的互相调用,而不 ...
- iOS下JS与OC互相调用(四)--JavaScriptCore
前面讲完拦截URL的方式实现JS与OC互相调用,终于到JavaScriptCore了.它是从iOS7开始加入的,用 Objective-C 把 WebKit 的 JavaScript 引擎封装了一下, ...
- iOS中JS 与OC的交互(JavaScriptCore.framework)
iOS中实现js与oc的交互,目前网上也有不少流行的开源解决方案: 如:react native 当然一些轻量级的任务使用系统提供的UIWebView 以及JavaScriptCore.framewo ...
- iOS开发:JavaScriptCore.framework的简单使用--JS与OC的交互篇
iOS7之后苹果为众猿推出了JavaScriptCore.framework这个框架,这个框架为大家在与JS交互上提供了很大帮助,可以在html界面上调用OC方法并传参,也可以在OC上调用JS方法并传 ...
- 转载 【iOS开发】网页JS与OC交互(JavaScriptCore) OC ----->JS
目标 本文介绍利用苹果在iOS7时发布的JavaScriptCore.framework框架进行js与OC的交互.我们想要达到的目标是: OC调用网页上的js方法 网页js调用APP中的OC方法 ...
- JS调用OC方法并传值,OC调用JS方法并传值////////////////////////zz
iOS开发-基于原生JS与OC方法互相调用并传值(附HTML代码) 最近项目里面有有个商品活动界面,要与web端传值,将用户在网页点击的商品id 传给客户端,也就是js交互,其实再说明白一点 ...
- 史上最全的 UIWebview 的 JS 与 OC 交互
来源:伯乐在线 - 键盘风筝 链接:http://ios.jobbole.com/89330/ 点击 → 申请加入伯乐在线专栏作者 其实一直想给大家整理一下JS与OC的交互,但是没有合适的机会,今天借 ...
- JS 与 OC
做项目需要从网页点击跳转到app的一个页面上,并且需要获取参数. 当时后台给写的参数是这样的.自己打开浏览器看的源码 JavaScript:window.location.href= 这句话在js ...
随机推荐
- python3.4安装suds
使用suds访问webservice十分方便 python3.x安装suds会报错“No module named client” 在stackoverflow上找到了替代方法,安装suds-jurk ...
- 面试题_1_to_16_多线程、并发及线程的基础问题
多线程.并发及线程的基础问题 1)Java 中能创建 volatile 数组吗?能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组.我的意思是,如果改变引 ...
- jquery select操作和联动操作
(function(){ //select操作 $.fn.loadSelect = function(opt){ opt = $.extend({}, { required:false,//为真则添加 ...
- poj3207 Ikki’s Story IV – Panda’s Trick
2-SAT. tarjan缩点.强连通分量的点要选一起选. #include<cstdio> #include<algorithm> #include<cstring&g ...
- WEB-INF目录与META-INF目录的作用
/WEB-INF/web.xml Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则. /WEB-INF/classes/包含了站点所有用的 class 文件,包括 ser ...
- Asp.Net Web Api 与 Andriod 接口对接开发经验,给小伙伴分享一下!
最近一直急着在负责弄Asp.Net Web Api 与 Andriod 接口开发的对接工作! 刚听说要用Asp.Net Web Api去跟 Andriod 那端做接口对接工作,自己也是第一次接触Web ...
- LeetCode Swap Nodes in Pairs 交换结点对(单链表)
题意:给一个单链表,将其每两个结点交换,只改尾指针,不改元素值. 思路:迭代法和递归法都容易写,就写个递归的了. 4ms /** * Definition for singly-linked list ...
- UVA 1660 Cable TV Network 电视网络(无向图,点连通度,最大流)
题意:给一个无向图,求其点连通度?(注意输入问题) 思路: 如果只有1个点,那么输出“1”: 如果有0条边,那么输出“0”: 其他情况:用最大流解决.下面讲如何建图: 图的连通度问题是指:在图中删去部 ...
- Sourcetree add Submodule
LMXMN041:ximalaya will.wei$ git submodule add https://github.com/willbin/WeLib02.git Submodule Cloni ...
- JazzyViewPager开源项目的简析及使用
JazzyViewPager是一个重写的ViewPager,能是ViewPager滑动起来更加的炫酷. 开源地址:https://github.com/jfeinstein10/JazzyViewPa ...