使用面向 iOS 的本机插件扩展 PhoneGap
本文细致探讨了 Xcode(以 iOS 设备为目标)中的 PhoneGap(也称为 Apache Cordova)应用程序本机插件。如果您刚开始接触 PhoneGap 或者需要回顾 PhoneGap 基础知识,请先阅读 Xcode for iOS 的 PhoneGap 入门,然后再继续阅读本文。
本文交替使用术语 Cordova 和 PhoneGap 指示同一开源应用程序平台,该平台可供您使用 HTML 和 JavaScript 创建本机安装的移动应用程序。PhoneGap 代码库已迁移至 Apache 软件基金会的开放资源中,名为 Cordova。Adobe 则仍以 PhoneGap 名称进行分发。有关更多信息,请参阅 Brian Leroux 发布的博客文章“PhoneGap、Cordova、名称有什么关系?”正如 Brian 在这篇文章中所说,“目前唯一的区别在于下载包名称的不同,并且这种情况仍将维持一段时间。
PhoneGap 不仅可让您利用 Web 技术为本机安装的移动应用程序构建用户界面,还能提供基于 JavaScript 的 API 供您与本机设备功能进行交互。默认情况下,PhoneGap 可访问设备摄像头、加速计、文件系统、GPS 位置及其他功能间的媒体重放。但是,PhoneGap 并未揭示供您在 JavaScript 应用程序内使用的每一个本机 API。如果您希望 PhoneGap 执行其默认功能集以外的操作,可以使用 PhoneGap 本机插件模型扩展核心 PhoneGap API 的功能。
PhoneGap 本机插件与桌面浏览器的插件不同;它们为您提供了一种插件自定义代码以增加 PhoneGap 应用程序框架功能的方式。PhoneGap 本机插件可让您以本机代码的形式创建全新的自定义功能,并通过 PhoneGap 的本机-JavaScript 桥将其显示给 PhoneGap 应用程序。这意味着,您可以显示所有本机库或框架,以便在基于 JavaScript 的 PhoneGap 应用程序内部使用。
在您开始编写 PhoneGap 本机插件之前,了解 PhoneGap 应用程序容器如何向基于 JavaScript 的应用程序显示本机操作系统功能将会有所帮助。
所有 Cordova API 均包含以下两个有关部件:一个可以在您的应用程序内部进行访问的基于 JavaScript 的界面,以及用于以本机代码执行操作的对应本机类。通常情况下,JavaScript 类和本机类具有相互镜像的 API,这样就能轻松地进行跟踪。JavaScript 类使用 Cordova.exec()
函数调用本机代码。当它调用 Cordova.exec
时,可将其传递至结果处理程序函数和错误处理程序函数,同时还会将一组参数传递至本机代码,并将引用传递至本机类名称和本机函数名称。Cordova 将负责管理 JavaScript 与本机之间的通信,您可以专心构建自己的应用程序。
要了解有关 PhoneGap 本机插件的更多信息,请登录 Cordova wiki 查看该核心 API 的源代码。整个 PhoneGap 框架均构建于同一模式之上,您可以在这里了解这一模式。
要开始构建您的首个 PhoneGap 插件,您需要按照 Xcode for iOS 的 PhoneGap 入门一文中所述的步骤创建一个新 PhoneGap 项目。我将自己的项目命名为 MyFirstPhoneGapNativePlugin。
JavaScript 类
在您设置完 Hello Xcode 项目后,即可准备为本机插件创建 JavaScript 界面。您需要使用函数(将要镜像通过本机代码显示的逻辑)创建类。在 www 文件夹下,创建一个名为 HelloPlugin.js 的 JavaScript 文件,其中包含如下所示的简单 JavaScript 类。
var HelloPlugin = { callNativeFunction: function (success, fail, resultType) { return Cordova.exec( success, fail, "com.tricedesigns.HelloPlugin", "nativeFunction", [resultType]); }};
HelloPlugin 类包含一个名为 callNativeFunction
的函数,它接收了一个成功回调函数、一个错误回调函数和一个 resultType
字符串参数。callNativeFunction
函数包含 Cordova.exec
函数,因而将会调用实际本机代码。此类中没有其他 JavaScript,但您可以根据自身需要在此处添加 JavaScript 代码。
调用 Cordova.exec
后,将会看到以下五个参数:
- 一个成功回调函数(本机代码层成功响应时调用的函数)引用
- 一个错误回调函数(本机代码层错误响应时调用的函数)
- 一个本机代码类字符串引用(我将会在下文进行详细介绍)
- 一个应当调用的函数名称字符串引用
- 一个将要传递至本机代码的参数数组
请记住,JavaScript 和本机代码层之间的代码执行操作并不同步,因此,在开发 PhoneGap 本机插件时需要使用回调函数和异步编码实践。
本机类
要创建本机代码层,请首先创建一个新的本机 Objective-C 类,它扩展核心 Cordova API 的 CDVPlugin 类:
- 右键单击 PhoneGap 项目内的 Plugins 目录,然后选择 New File(参见图 1)。
- 在 New File 向导中,选择 Objective-C 类模板,然后单击 Next(参见图 2)。
- 键入 HelloPlugin 作为新类的名称,并将该类纳为 CDVPlugin 的子类(参见图 3)。
CDVPlugin 类是所有 Cordova 类均必须扩展的父类。CDVPlugin 类通过 PhoneGAP API 封装本机 JavaScript 通信所需的所有必要逻辑。PhoneGap.exec
函数可让您调用该新类上的函数。CDVPlugin 类包含一个名为writeJavascript
的核心函数,可让您调用 PhoneGap 应用程序 Web 视图内的 JavaScript。本机到 Web JavaScript 这一方向的所有通信均必须使用 writeJavascript
函数完成。
- 单击 Next。
- 出现提示后,为新文件指定位置(最好指定在 Xcode 项目内的 Plugins 目录下),然后单击 Finish 继续操作。
您将会在 PhoneGap 项目内看到一个新的头文件 (.h) 和一个新的实现文件 (.m)(参见图 4)。
- 接下来,在您的 .h 文件中为
nativeFunction
函数添加定义;例如:
#import <Cordova/CDV.h>@interface HelloPlugin : CDVPlugin- (void) nativeFunction:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;@end
此定义包含以下两个参数:一个是 NSMutableArray
,其中包含从 JavaScript 层和选项字典(地图)接收到的参数。在本例中,您只需要关注参数数组。头文件只包含方法签名;您无需在 .h 文件中包含任何应用程序逻辑。
- 在您创建类签名后,将逻辑添加到 .m 文件的
nativeFunction
函数实例中。您将在下面看到此本机插件类内部使用的 Objective-C 函数示例。
#import "HelloPlugin.h"@implementation HelloPlugin- (void) nativeFunction:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options { //get the callback id NSString *callbackId = [arguments pop]; NSLog(@"Hello, this is a native function called from PhoneGap/Cordova!"); NSString *resultType = [arguments objectAtIndex:0]; CDVPluginResult *result; if ( [resultType isEqualToString:@"success"] ) { result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: @"Success :)"]; [self writeJavascript:[result toSuccessCallbackString:callbackId]]; } else { result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: @"Error :("]; [self writeJavascript:[result toErrorCallbackString:callbackId]]; }}@end
在 nativeFunction
方法内部,您首先需要获取一个 NSString callbackId
引用,核心 PhoneGap API 借此将此函数响应映射到调用此函数的原始 JavaScript。
接下来,此方法使用 NSLog
将消息写入 Xcode 调试控制台;这只是表明它目前正在执行本机代码。
写入到调试控制台后,该函数将会检查传递到该函数的 resultType
,然后创建相应的 CDVPluginResult
实例。resultType
值是一个简单字符串。如果 resultType
是 success"
,则该函数将创建 success 结果,并使用 [self writeJavascript]
函数将成功回调函数写入 JavaScript 层。任何其他 resultType
参数值均将生成 error 结果,并且该函数会将错误回调写入 JavaScript 层。
当您将成功或错误回调函数写回 JavaScript 时,始终使用 CDVPluginResult 实例。但是,您也可以使用writeJavascript
函数将任意 JavaScript 字符串传递回 JavaScript 层。这项技术甚至还可用于将数据从本机层实时推送至 JavaScript 层。
鉴于您已经创建插件,因而可以从 PhoneGap 应用程序内部进行调用。
- 首先,您需要向新插件的 JavaScript 接口类 (HelloPlugin.js) 添加一个引用。在您的 index.html 文件内添加一个新的
<script>
标记:
<script type="text/javascript" charset="utf-8" src="HelloPlugin.js"></script>
- 同样也是在
onDeviceReady()
函数后,添加 JavaScript 以调用本机插件及处理插件结果。添加名为callNativePlugin
、nativePluginResultHandler
和nativePluginErrorHandler
的 JavaScript 函数,如下所示:
function callNativePlugin( returnSuccess ) { HelloPlugin.callNativeFunction( nativePluginResultHandler, nativePluginErrorHandler, returnSuccess );} function nativePluginResultHandler (result) { alert("SUCCESS: \r\n"+result );}function nativePluginErrorHandler (error) { alert("ERROR: \r\n"+error );}
callNativePlugin
函数只需调用 JavaScript 的本机插件类接口。当其调用callNativeFunction
方法时,即会传递从本机代码层接收到的成功和错误状态回调函数。如果本机层成功返回回调函数,将会调用nativePluginResultHandler
函数,而当本机层传回错误回调时,则调用nativePluginErrorHandler
函数。
- 接下来,根据下列代码添加两个 JavaScript 按钮以调用该插件。
<body onload="onBodyLoad()"> <h1>Hey, it's Cordova!</h1> <button onclick="callNativePlugin('success');">Click to invoke the Native Plugin with an SUCCESS!</button> <button onclick="callNativePlugin('error');">Click to invoke the Native Plugin with an ERROR!</button></body>
单击第一个按钮将调用 callNativeFunction
方法,同时生成参数 "success"。PhoneGap 将于随后执行本机代码并在 JavaScript 层调用成功回调(它将会调用 nativePluginResultHandler
函数)。
当您单击第二个按钮时,将会调用 callNativeFunction
方法,同时生成参数 "error"。PhoneGap 将执行本机代码并在 JavaScript 层调用错误回调(它将会调用 nativePluginErrorHandler
函数)。
映射本机代码类
此时,您几乎已将所有事物串联起来并可准备执行操作,但您还必须再完成一个步骤,才能调用 JavaScript 中的本机代码。
您必须添加一个映射,以便 Cordova 识别您的本机代码类。还记得在调用 Cordova.exec
时用来识别本机类的字符串引用吗?您需要将该字符串映射到 Cordova.plist 文件中的实际类实例。Cordova.plist 文件包含当前 Cordova 项目的所有配置信息。
- 在项目导航器中,打开 Supporting Files 文件夹,然后单击名为 Cordova.plist 的文件。
- 在右侧向下滚动至 Plugins 条目并单击将其展开。
- 添加一个带有 code>com.tricedesigns.HelloPlugin 项和
"HelloPlugin"
值的条目(参见图 5),用贵公司的标识替换com.tricedesigns
。当调用Cordova.exec
时,您将利用此字符串引用识别第三个参数中的本机类。
key 是 PhoneGap.exec
映射至本机代码类所使用的唯一一个字符串引用。value是将要调用的实际本机类名称。
- 保存更改。
现在,您可以启动该应用程序并进行彻底检查。
要启动该应用程序,请单击 Run 按钮或选择 Product > Run。
在 iOS Simulator 中(或连接设备上)启动该应用程序后,您将会看到带有两个按钮的简单界面(参见图 6)。单击任一按钮调用本机插件的本机代码,无论调用成功回调还是错误回调,都会通过 JavaScript 显示一条警告消息。
当调用本机代码时,您还能够在 Xcode 调试控制台窗口中看到输出内容,反映调用本机插件 NSLog
生成的输出内容(参见图 7)。