Cordova的设计概念,是在APP上透过Web控件来呈现Web页面,让Web开发人员可以操作熟悉的语言、工具来开发APP。使用Web页面来呈现功能内容,的确可以满足大部分的功能需求,但是因为APP的使用情景毕竟有别于Web,某些APP的功能需求像是:拨打电话、扫描条形码...等等,无法单纯使用Web开发技术就能实现。
为了让Web页面能够满足更多的APP功能需求,Cordova提供了Plugin机制,让Web页面能够挂载并调用Native开发技术所开发的功能模块。当开发人员遇到Web开发技术无法实现的功能需求时,可以到 Cordova官网(https://cordova.apache.org/plugins/) ,下载并使用官方提供的各种通用Plugin功能模块。而就算遇到了特殊的功能需求,Cordova也提供了方法,让开发人员可以自行开发专属于自己的客制Plugin功能模块。
本篇文章介绍如何建立一个Cordova Plugin功能模块,让开发人员能够使用Native开发技术,在Cordova里开发Plugin功能模块给Web页面使用,让Web页面能够满足更多的APP功能需求。主要是为自己留个纪录,也希望能帮助到有需要的开发人员。
Cordova Plugin功能模块会以文件夹的形式,存放于本机文件系统或是远程Git服务器。使用Visual Studio开发Cordova项目的时候,只要输入档案路径或是服务器路径,就可以自动完成挂载Plugin功能模块的动作。
挂载Plugin功能模块的时候,Visual Studio会读取文件夹下的「plugin.xml」。这个XML档案中定义了Plugin功能模块的相关设定项目,这些设定项目里用于描述Plugin功能模块的,主要项目为:
<?xml version="1.0" encoding="UTF-8"?> <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" id="clk-cordova-sample" version="1.0.0"> <!-- metadata --> <name>CLK Cordova Sample</name> <description>CLK Cordova Sample的说明</description> <license>Apache 2.0</license> <!-- javascript --> <js-module>...</js-module> <js-module>...</js-module> <!-- android --> <platform name="android">...</platform> <!-- ios --> <platform name="ios">...</platform> </plugin>
Cordova在编译Android Platform Plugin的时候,会先建立编译用的Android项目,并且读取plugin.xml里被标注为 android 的platform设定区块,做为编译时期的编译参数。
<!-- android --> <platform name="android"> <!-- config --> <config-file target="res/xml/config.xml" parent="/*"> <feature name="NotificationService"> <param name="android-package" value="com.clk.cordova.sample.NotificationService"/> </feature> </config-file> <!-- source --> <source-file src="src/android/NotificationService.java" target-dir="src/com/clk/cordova/sample/" /> </platform>
当Cordova读取到android platform的设定区块的source-file设定区块时,会将每一个source-file设定区块里src所指定的档案,复制到Android项目里target-dir所指定文件夹,用以进行后续的编译工作。在本篇的范例里,source-file设定区块所代表的意义是:将Plugin里src/android/NotificationService.java的这个档案,复制到Android项目的src/com/clk/cordova/sample/文件夹里面来进行编译。
<!-- source --> <source-file src="src/android/NotificationService.java" target-dir="src/com/clk/cordova/sample/" />
android platform的设定区块的config-file设定区块比较特别,这个设定区块是用来,定义提供给Web页面调用的类别。在这其中feature name代表的是这个Plugin提供给Web页面使用的类别别名,而param value里定义的则是实际提供服务的Native类别名称,剩余其他设定参数则是编译所需的固定参数。在本篇范例里,config-file设定区块所代表的意义是:Web页面可以使用NotificationService这个类别别名,来调用Android项目里面的com.clk.cordova.sample.NotificationService这个Native类别。
<!-- config --> <config-file target="res/xml/config.xml" parent="/*"> <feature name="NotificationService"> <param name="android-package" value="com.clk.cordova.sample.NotificationService"/> </feature> </config-file>
android platform plugin里,使用Native技术所开发的Native类别,必须先继承org.apache.cordova.CordovaPlugin这个类别,再经由source-file设定加入项目编译,后续才可以透过config-file的定义提供Web页面调用。而Web页面调用Native类别时,Cordova会执行Native类别的execute方法,并且提供下列内容:
package com.clk.cordova.sample; import org.apache.cordova.*; import org.json.*; import android.widget.Toast; public class NotificationService extends CordovaPlugin { // methods public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { // show if(action.equals("show")) { // arguments String message = args.getString(0); // execute Toast.makeText(this.cordova.getActivity(), message, Toast.LENGTH_LONG).show(); // return return true; } // default return false; } }
完成上述Android Platform Plugin的相关开发工作之后,在挂载这个Cordova Plugin的Android执行平台里,Web页面就可以使用统一的cordova.exec方法,来调用Native开发的类别。这个cordova.exec接口会执行Native类别的execute方法,并且带入下列内容:
cordova.exec(success, error, feature, action, args);
Cordova提供统一的cordova.exec方法,让Web页面调用Native开发的Plugin功能。但是,cordova.exec方法需要带入的五个输入参数、加上success、error这两个Callback函式所隐含的回传参数、再加上多参数需要包装成JSON格式...这些琐碎的工作,会大量增加Web开发人员使用Plugin功能模块的负担。
为了减少Web开发人员使用Plugin功能模块的负担,Plugin功能模块的开发人员,可以使用Cordova提供的JavaScript Module挂载机制,在Plugin功能模块里面加入JavaScript程序代码来封装cordova.exec方法,用以提供更简洁、更易懂的使用接口让Web开发人员使用。
Before
cordova.exec(null, null, "NotificationService", "show", [message]);
After
clk.cordova.sample.NotificationService.show("Clark");
Cordova在编译每个执行平台的时候,会读取plugin.xml里所有的js-module设定区块,并且将每一个js-module设定区块里src所指定的js-module原始码打包备用。当应用程序执行的时候,Cordova核心会将这些js-module加载来提供Web页面使用。
<!-- javascript --> <js-module name="NotificationService" src="www/clk.cordova.sample.NotificationService.js" > ... </js-module>
在每个js-module原始码的内部,可以透过Cordova所提供的exports对象,来附加使用JavaScript所撰写的功能函式,这个exports对象是用来封装js-module功能的对象。在本篇的范例里,js-module原始码在exports对象上,附加了一个show函式用来简化cordova.exec的使用方式,提供给Web页面使用。
clk.cordova.sample.NotificationService.js
// methods exports.show = function (message) { cordova.exec(null, null, "NotificationService", "show", [message]); };
Web页面需要使用js-module的时候,可以使用cordova.require方法来取得js-module。这个cordova.require方法,使用js-module设定区块里name所设定的名称、加上Plugin metadata里所定的id标识符做为索引,来识别并取得Cordova核心中的js-module。在本篇的范例里,Web页面使用cordova.require取得Plugin id:clk-cordova-sample、js-module name:NotificationService,封装js-module的exports对象,并且调用这个exports对象所附加的show函式。
index.html
var notificationService = cordova.require("clk-cordova-sample.NotificationService"); notificationService.show("Clark");
为了更进一步简化取得js-module的方式,Cordova另外在plugin.xml的js-module设定区块上,提供了clobbers设定项目。当应用程序执行、Cordova核心加载js-module提供Web页面使用的时候,如果定义了clobbers所提供的target设定参数,Cordova核心会自动将封装js-module的exports对象,存放到target所定义的对象上,让Web页面可以直接调用。
在本篇的范例里,js-module定义了一个clobbers的target,Cordova核心会在载入clk.cordova.sample.NotificationService.js之后,将封装js-module的exports对象,存放到clk.cordova.sample.NotificationService这个物件上。后续Web页面,只要直接使用clk.cordova.sample.NotificationService.show就可以调用这个exports对象所附加的show函式。
clobbers
<!-- javascript --> <js-module name="NotificationService" src="www/clk.cordova.sample.NotificationService.js" > <clobbers target="clk.cordova.sample.NotificationService" /> </js-module>
index.html
clk.cordova.sample.NotificationService.show("Clark");
范例程序代码: 下载地址