热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【转】使用JavaScriptCore在JS和OC间通信

http:www.cocoachina.comios2016062316796.htmliOS开发中,我们时不时的需要加载一些Web页面,一些需求使用Web页面来实现可以更可控,

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,方法内部将标识作为 keyhandler 作为值保存。

- (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 by Example

    • JavascriptCore初探

    • WebViewJavascriptBridge


推荐阅读
  • 使用CocoaPods管理依赖库
    这篇内容将介绍Mac和iOS开发中必备的一个依赖库管理工具CocoaPods。在iOS开发中势必会用到一些第三方依赖库,比如大家都熟悉的ASIHttpRequest、AFNetwo ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文介绍了django中视图函数的使用方法,包括如何接收Web请求并返回Web响应,以及如何处理GET请求和POST请求。同时还介绍了urls.py和views.py文件的配置方式。 ... [详细]
  • 网络请求模块选择——axios框架的基本使用和封装
    本文介绍了选择网络请求模块axios的原因,以及axios框架的基本使用和封装方法。包括发送并发请求的演示,全局配置的设置,创建axios实例的方法,拦截器的使用,以及如何封装和请求响应劫持等内容。 ... [详细]
  • 本文介绍了Java后台Jsonp处理方法及其应用场景。首先解释了Jsonp是一个非官方的协议,它允许在服务器端通过Script tags返回至客户端,并通过javascript callback的形式实现跨域访问。然后介绍了JSON系统开发方法,它是一种面向数据结构的分析和设计方法,以活动为中心,将一连串的活动顺序组合成一个完整的工作进程。接着给出了一个客户端示例代码,使用了jQuery的ajax方法请求一个Jsonp数据。 ... [详细]
  • ①页面初始化----------收到客户端的请求,产生相应页面的Page对象,通过Page_Init事件进行page对象及其控件的初始化.②加载视图状态-------ViewSta ... [详细]
  • 渗透测试基础bypass绕过阻挡我们的WAF(下)
    渗透测试基础-bypass ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • POCOCLibraies属于功能广泛、轻量级别的开源框架库,它拥有媲美Boost库的功能以及较小的体积广泛应用在物联网平台、工业自动化等领域。POCOCLibrai ... [详细]
  • Imdevelopinganappwhichneedstogetmusicfilebystreamingforplayinglive.我正在开发一个应用程序,需要通过流 ... [详细]
author-avatar
致力于流浪动物救助量
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有