文档章节

Objc与JS间相互调用

fengwenjie
 fengwenjie
发布于 2015/06/26 10:21
字数 1050
阅读 1015
收藏 3
点赞 0
评论 0

过去3、4年都在进行跨平台的混合应用开发,但一直没有系统梳理跨平台技术的底层原理,趁新工作未正式入职,这里整理一下。

跨平台的一种实现是基于webview。所谓webview,实质是在原生app中打开一个内嵌浏览器,具体到iOS平台就是使用UIWebView这个控件。然后就很容易理解了,我们相当于开发一个webapp(网页应用),然后通过原生应用作为用户入口(而非原生浏览器),用户会访问到远程服务器的网页内容。从用户感知上,似乎是在使用一个App,但实际上是在访问一个网页。

以上,只是跨平台基于webview实现的工作原理,而更重要的是如何桥接webview的js和app的objc,使得webapp也可用使用原生的功能api,如调用摄像头等,而app又可以调用webview里的js,即向双通信。

Apple开放了一个叫做JavascriptCore的框架,此框架最早在OSX10.2就存在,但到了2013年在OSX10.9上才发布其调用的API,而后又在iOS7上公开,由此我们可用名正言顺地使用了。

JavascriptCore提供了以下几个API,实现跨平台通信:

  • JavascriptCore/API/JSContext.h
  • JavascriptCore/API/JSExport.h
  • JavascriptCore/API/JSValue.h

JSContext是JavascriptCore的主入口,它代表了JS的运行时环境,在其中可以定义对象、方法等,这些实体(对象、方法)的生命周期在JSContext被释放的时候才结束。而且可用指定的JSVirtualMachine来创建JSContext,每个JSVM都会独立运行在一个线程上。

我们可用通过JSContext的evaluateScript方法来定义我们的JS方法,而且是通过字符串定义代码,当然可以通过读取外边js文件来实现。看下面的例子:

        // getting a JSContext
        JSContext *context = [JSContext new];
    
        // defining a JavaScript function
        NSString *jsFunctionText =
        @"var isValidNumber = function(phone) {"
        "    var phonePattern = /^[0-9]{3}[ ][0-9]{3}[-][0-9]{4}$/;"
        "    return phone.match(phonePattern) ? true : false;"
        "}";
        [context evaluateScript:jsFunctionText];

这里,相当于在objc层,向JSContext注入了一个isValidNumber的js方法。

正如上文所述,JSContext代表了一个JS运行环境,而我们的示例代码都是单独创建这个JSContext运行环境的,实际上UIWebView实例也有它自己的JSContext运行环境。为了修改web上的内容,我们需要访问UIWebView的JSContext。

但Apple就是一个闷骚男,虽然已经公开了JavascriptCore的API,但又不提供直接访问UIWebView’s JSContext的方法。

幸好“key-value”把我们救了回来:

// get JSContext from UIWebView instance
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

然后,我们可以通过JSValue来获取JSContext中js方法的引用和执行的结果:

        // 获取isValidNumber方法的引用
        JSValue *jsFunction = context[@"isValidNumber"];
        // 通过callWithArguments方法调用js方法
        JSValue *value = [jsFunction callWithArguments:@[ phone ]];

JavascriptCore会自动转换JSValue的对象类型,比如这里isValidNumber返回的boolean,同时还支持NSString, NSDate, NSDictionary, NSArray等。

另外,我们还可以增加异常捕捉

[context setExceptionHandler:^(JSContext *context, JSValue *value) {
        NSLog(@"%@", value);
}];

再有,通过JSExport可以将objc的方法暴露给JS。

  @protocol BNRContactAppJS <JSExport>
    
    - (void)addContact:(BNRContact *)contact;
    
    @end
    
    @interface BNRContactApp : NSObject <BNRContactAppJS>
    ...
    @end

addContact这个方法是在BNRContactAppJS协议中声明的,BNRContactAppJS又源自于JSExport,所以addContact方法将会暴露给JS环境,而其他方法则对JS环境而言是隐藏的。

最后,我们看一个完整的例子

- (void)webViewDidFinishLoad:(UIWebView *)webView
    {
        // get JSContext from UIWebView instance
        JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    
        // enable error logging
        [context setExceptionHandler:^(JSContext *context, JSValue *value) {
            NSLog(@"WEB JS: %@", value);
        }];
    
        // give JS a handle to our BNRContactApp instance
        context[@"myApp"] = self.app;
    
        // register BNRContact class
        context[@"BNRContact"] = [BNRContact class];
    
        // add function for processing form submission
        NSString *addContactText =
        @"var contactForm = document.forms[0];"
         "var addContact = function() {"
         "    var name = contactForm.name.value;"
         "    var phone = contactForm.phone.value;"
         "    var address = contactForm.address.value;"
         "    var contact = BNRContact.contactWithNamePhoneAddress(name, phone, address);"
         "    myApp.addContact(contact);"
         "};"
         "contactForm.addEventListener('submit', addContact);";
        [context evaluateScript:addContactText];
    }

最终跨平台调用就在这一句:

myApp.addContact(contact);

在JS环境调用了objc的方法。

总结一下:

  • ==从objc调用js:JSContext的evaluateScriptf方法和JSValue的callWithArguments方法;==
  • ==捕捉JS执行的异常;==
  • ==从WebView实例获取JSContext;==
  • ==通过JSExport将objc方法暴露给js调用。==

最后啰嗦一下,iOS7以前,并没有JavascriptCore,所以多使用 stringByEvaluatingJavaScriptFromString。

Titanium 就是使用了JavascriptCore的方式。

© 著作权归作者所有

共有 人打赏支持
fengwenjie
粉丝 6
博文 24
码字总数 20311
作品 0
白云
iOS WKWebView和JS交互的两种方式

本文介绍两种方式实现iOS WKWebView和JS交互 WKWebViewConfiguration注入WKScriptMessageHandler UIWebViewDelegate回调方法中处理 WKWebViewConfiguration注入WKScriptMessageHandler 网页很......

aron1992 ⋅ 2017/02/18 ⋅ 0

使用Kotlin:让Android与JS交互的详解

先来说说什么是JS交互: 说的俗一点就是通过我们项目中的控件来调用HTML里的JS代码,也可以通过JS来调用项目中的代码。 Android与JS之间的桥梁就是WebView了,我们是通过WebView来实现他们的...

富江___ ⋅ 06/11 ⋅ 0

Lynx技术分析-JS引擎扩展设计

JS Binding 技术 Lynx(一个高效的跨平台框架) 的 JS Binding 技术最主要的目的是搭建一个高效的与 JS 引擎解耦的通信桥梁,同时具备 JS 引擎切换的能力。该技术经历了多次迭代,最终通过抽...

hxxft ⋅ 05/15 ⋅ 0

基于JS的高级脚本语言 - Sara

Sara-基于JS的高级脚本语言 欢迎使用Sara,Sara是一款基于JavaScript的全新的高级脚本语言! Sara不像我们工作室上一款编程语言作品-Ginit一样,他属于更高级的语言 Sara全面支持高级函数,并...

Skyogo ⋅ 04/15 ⋅ 0

iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge

iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge 转载:原地址 https://www.jianshu.com/p/e951af9e5e74 上一篇文章介绍了UIWebView 如何通过WebViewJavascriptBridge 来实现......

法斗斗 ⋅ 05/11 ⋅ 0

WebAssembly 时代,Rust 也想成为 Web 语言

目前 Mozilla 正在基于 WebAssembly 可移植代码格式研发 JavaScript 和 Rust 之间的桥梁——wasm-bindgen,意义是提高 JavaScript 和 Rust 之间的互操作性。Mozilla 这么做是想让 Rust 成为类...

开源中国 ⋅ 04/10 ⋅ 0

JS与OC-WebView交互总结

OC与JS交互有两种方式,WKWebView和UIWebView,由于UIWebView比较耗内存,性能上不太好,所以苹果在iOS 8中推出了WKWebView。 一、与UIWebView交互 在UIWebView与JS交互中最简单的办法就是拦截...

乔兰伊雪 ⋅ 05/29 ⋅ 0

初学angular 看到网上有angular js 也有angular2 ,到angular官网发现最近版本是6了,那么现在大家说的angular js到底是什么啊?

初学angular 看到网上有angular js 也有angular2 ,到angular官网发现最近版本是6了,那么现在大家说的angular js到底是什么啊? angular2和现在官网的angular6 就是 angular js 只是版本不同...

Jordan裔 ⋅ 05/19 ⋅ 0

[译] JavaScript 如何工作:对引擎、运行时、调用堆栈的概述

原文地址: https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf PS: 好久没写东西了,最近一直在准备写一个自己的博客,最后一些技术方向已经敲定了,又可...

小烜同学 ⋅ 2017/11/12 ⋅ 0

React Native 与原生模块数据通信(一)(iOS)

(一)iOS日历模块封装演示 下面开始演示如何封装一个iOS日历原生模块,让JavaScript可以进行访问到iOS平台日历的功能。 在React Native中,原生模块就是一个Objective-C类,该实现了RCTBridge...

manofit ⋅ 05/24 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Confluence 6 从其他备份中恢复数据

一般来说,Confluence 数据库可以从 Administration Console 或者 Confluence Setup Wizard 中进行恢复。 如果你在恢复压缩的 XML 备份的时候遇到了问题,你还是可以对整个站点进行恢复的,如...

honeymose ⋅ 9分钟前 ⋅ 0

myeclipse10 快速搭建spring boot开发环境(入门)

1.创建一个maven的web项目 注意上面标红的部分记得选上 2.创建的maven目录结构,有缺失的目录可以自己建立目录补充 补充后 这时候一个maven的web项目创建完成 3.配置pom.xml配置文件 <proje...

小海bug ⋅ 21分钟前 ⋅ 0

nginx.conf

=========================================================================== nginx.conf =========================================================================== user nobody; #......

A__17 ⋅ 24分钟前 ⋅ 0

645. Set Mismatch - LeetCode

Question 645. Set Mismatch Solution 思路: 遍历每个数字,然后将其应该出现的位置上的数字变为其相反数,这样如果我们再变为其相反数之前已经成负数了,说明该数字是重复数,将其将入结果r...

yysue ⋅ 37分钟前 ⋅ 0

Python这么强?红包杀手、消息撤回也可以无视,手机App辅助!

论述 标题也许有点不好理解,其实就是一款利用Python实现的可以监控微信APP内的红包与消息撤回的助手。不得不说,这确实是一款大家钟意的神器。 消息撤回是一件很让人恶心的事,毕竟人都是有...

Python燕大侠 ⋅ 53分钟前 ⋅ 0

压缩打包介绍、gzip压缩工具、bzip2压缩工具、xz压缩工具

压缩打包介绍 压缩的好处不仅能节省磁盘空间而且在传输的时候节省传输时间和网络带宽 windows系统下文件带有 .rar .zip .7z 后缀的就是压缩文件 linux系统下则是 .zip, .gz, .bz2, .xz, ...

黄昏残影 ⋅ 58分钟前 ⋅ 0

观察者模式

1.利用java原生类进行操作 package observer;import java.util.Observable;import java.util.Observer;/** * @author shadow * @Date 2016年8月12日下午7:29:31 * @Fun 观察目标 **/......

Cobbage ⋅ 今天 ⋅ 0

Ubuntu打印服务器配置

参考:https://blog.csdn.net/gsls200808/article/details/50950586 https://blog.csdn.net/jiay2/article/details/80252369 https://wiki.gentoo.org/wiki/HPLIP 由于媳妇儿要大量打印资料,......

大熊猫 ⋅ 今天 ⋅ 0

面试的角度诠释Java工程师(二)

原文出处: locality 续言: 相信每一位简书的作者,都会有我这样的思考:怎么写好一篇文章?或者怎么写好一篇技术类的文章?我就先说说我的感悟吧,写文章其实和写程序是一样的。为什么我会...

颖伙虫 ⋅ 今天 ⋅ 0

github中SSH的Key

https://help.github.com/articles/connecting-to-github-with-ssh/ https://help.github.com/articles/testing-your-ssh-connection/ https://help.github.com/articles/adding-a-new-ssh-k......

whoisliang ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部