在 Worklight 架构下,纯 web 模式虽然可以像本地应用一样被安装在手机上,但是所提供的服务和传统的网页相比,几乎没有任何的区别,并且因为在分辨率和性能上与台式机相比有明显的差距,所以整体的体验相对于台式机会逊色不少。那么 web 模式应该如何扩展才能体现作为手机应用的优势呢?首先在完成基本的网页显示内容的情况下,需要能够扩展功能,使之可以调用手机本地的功能,诸如相机、定位系统、电话本等等;其次不但可以调用手机的本地功能,当手机和外接设备联系时,还能调用外接设备,比如条形码的检测;最后不同的手机操作系统在实现相同功能时因为策略的不同可能带来不同的效果,比如在打开网页的 PDF 文件上,iPhone 采用直接显示的策略,但是 Android 采取下载的策略,统一的解决这些问题,并且提供良好的提示机制也是手机客户端开发需要考虑的问题。以上这些方向都可以改善纯 web 模式的客户体验,提高手机应用的适用范围。
基于上面的描述,Worklight 中引入了一个定义,称之为混合同化模式(hybrid app mix)。在标准的网页程序中 JavaScript 语言只是负责对页面的元素进行操作,修改网页的树状结构。在 Worklight 混合模式的定义中,又可以按照使用技术的不同分为两类:web 模式下基于 Worklight 客户端 API 的 JavaScript 函数负责从远程的服务器中获得信息,以及同各种不同的后端数据库交互,进行诸如身份验证、数据读取等工作;在 mix 模式下,应用中的 JavaScript 不但可以从事前面的所有工作,还能通过它调用本地的 Java 或者 Object-C 代码,从而完成网页和本地功能的交互工作。在目前提供的技术中,开发者可以使用三种方式来完成这项工作:第一、通过 Worklight 中的 API,调用本地的页面,从而实现本地页面支持的 Java 或者 Object-C 方法,实现对本地功能的调用;第二、使用开源的 Cordova(也就是原有的 PhoneGap)框架,调用已经集成完毕的本地功能;第三、调用或者开发 Cordova 的插件,按照 Cordova 规定的模板,用户可以自行集成本地代码和 JavaScript 文件到 Cordova 框架中,用于调用本地功能。
本文将逐一介绍这三种方式,并且分析各自的利弊。但是需要指出的是,基于 Worklight 进行本地功能的开发,只能对客户端应用起到一定的辅助作用,如果大量的使用自行编辑的本地代码,不但丧失了原来的优势(利用 web 技术提高了开发的效率),反而会因为调用层次的增加带来运行效率低下的问题。所以在开发本地功能时,需要权衡利弊,作出适当的选择。
在 Worklight 的客户端 API 中,有一个函数可以调用本地已经编写好的页面,通过它可以方便的在不同页面间(网页和本地页面)进行数据的交换。它的函数形式如清单 1 所示:
WL.NativePage.show(className, callback, data)
|
函数一共包括三个参数,其定义如下
网页数据传入 | 利用 JSON 对象将数据传入到 data 中,并且根据 className 确定需要调用的类名称 | |
本地页面创建 | 必须继承 Object-C 中 UIViewController 类,然后通过 init 方法实现类的初始化。 | 必须继承 Activity 类,并且需要在 AndroidManifest.xm 文件中声明这个类。 |
本地页面数据接收 | 必须实现 setDataFromWebView 方法,然后将 JSON 对象转化为 NSDictionary 对象,读取其中的 NSString 数据 | 利用系统定义的 Intent 对象,通过 getExtra 方法获取相应的内容,可以是各种基本类型和它们的数组形式 |
本地页面数据输出 | 通过调用 showWebView 方法,将需要传输的数据放入 NSDictionary 对象中,然后返回到页面 | 通过创建的 Intent 对象,将返回的数据放置在 Intent 中,调用 Activity 的 finish 方法,将数据返回到网页上 |
网页数据接收 | 利用回调函数,将得到的 JSON 对象进行解析,获取其中的数据 | |
页面转换效果 | 利用 onBeforeShow 和 onAfterShow 来实现页面从网页到本地转换的效果;用 showWebView 来实现页面从本地向网页转换的效果 | 利用 OverridePendingTransition 函数,通过指定不同的参数完成页面由网页到本地页面以及返回的转换效果 |
Worklight 提供的方法虽然比较灵活,但是需要进行的编码工作比较多。比如需要定义相应的本地类,定义相互的数据接口,并且进行测试,这些工作会延长产品的开发周期,也容易引入错误,从而引起产品的不稳定。如果可以直接调用第三方的开源库,这样就可以减少本地功能的开发周期,并且获得稳定的代码表现和良好的代码维护。Cordova 作为 PhoneGap 的后续开源框架就提供了相应的功能。
Worklight 本身集成了 Cordova 的相应功能,在使用时需要注意的问题是,系统本身集成了 Cordova 的 1.6.1 版本,所以参考相应的 API 文档说明时,需要确定具体的版本。
Cordova 支持的功能可以分为十三类,对于常用的手机功能均有涉及,
从 Cordova 提供的内容看,对于普通的需求(企业级和非企业级),此框架均有良好的支持,开发者可以通过这个框架,完成系统要求的各种内容而不开发本地代码。
但是,Cordova 框架的内容毕竟有限,对于各种不同的客户端,框架不可能满足全部的要求。所以按照 Cordova 创建通用功能的方法,开发者可以自己创建 Cordova 插件,也可以在开源库中寻找其他开发者开发的插件系统,完成各种跨平台的本地功能。
Cordova 框架从本质上说就是利用网页上的 JavaScript 语言调用本地的代码,只要符合架构的模板,很多的本地功能都可以被网页调用,而不需要使用本地页面。Cordova 的插件系统由以下几部分组成:
从上面的描述可以看到,Cordova 插件机制增加了整个框架的灵活性。其实,Cordova 本身提供的十三项功能也是利用插件形式完成的。在这种架构下,框架的扩展性相当强,耦合度也很低,是一种良好的开发模式。
在日常的开发中,Cordova 提供的一些功能已经有官方网站的详细介绍,本文的余下内容将着重演示其余两种方式:通过 Worklight 提供的模式调用本地功能,以及通过 Cordova 插件的方式调用本地功能。
本文使用的实例测试环境如下:Windows XP 系统,Worklight 插件版本 5.0.3,IDE 版本为 Eclipse Indigo,Android 版本为 2.2 及以上。应用将首先利用 Worklight 提供的模式完成数据在网页和本地页面之间的交互。然后利用 Cordova 的插件直接获取本地代码的值显示在网页上。
第一个应用的流程如下:用户在网页中输入字符串信息,传递给本地页面;本地页面显示信息,并且让用户输入反馈信息,用户点击确认后,返回到网页,在网页中显示输入的反馈信息。本地代码的关键内容如清单 2 所示:
public class ExampleNative extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get content
String value = getIntent().getStringExtra("keyName");
// return content
String rvalue = "the content returned";
Intent rIntent = new Intent();
rIntent.putExtra("rvalue", rvalue);
setResult(RESULT_OK, rIntent);
finish();
}
}
|
从源代码中可以看到,源文件需要继承 Activity 类(关于 Activity 类的说明,可以查看 链接),并且通过 get 方法获得由网页传输过来的内容。在 Android 系统中,Intent 类是一个容器,可以将需要传递的数据按照键值对的方式放入其中,然后在 setResult 时将 Intent 作为参数放入,再调用 finish 后就可以将数据传递给其他页面了。在实例中本地的数据就是通过这种方式返回到网页的。完成源代码后,需要在 AndroidManifest 文件中添加配置信息,只有这样系统才能访问到相应的类文件。内容如清单 3 所示:
<application android:label="@string/app_name" android:icon="@drawable/icon" >
<activity android:name="wang.test.ExampleNative" />
|
完成了本地代码之后,网页端的代码按照如下方式完成:页面的内容在 common 文件夹下完成,然后在 common 和 environment(例如 android,iPhone 等)的 js 文件夹下实现相同的 JavaScript 方法,不同的是 common 环境下将直接显示提示信息,environment 环境下将实现具体的功能。相应的代码如清单 4 所示:
清单 4. common 和 environment 代码内容
common 代码
function openNative () {
alert('Don't support on web page');
}
Environment 代码
function openNative () {
var params = {keyName : $('#field').val()};
WL.NativePage.show('wang.test.ExampleNative', callback, params);
}
|
从代码中可以看到,调用的方式十分直接,并且不管在什么环境下,用户总能得到一定的提示信息。在 environment 下,回调函数完成的功能也很简单,就是将从本地代码中获取的数据显示在 alert 框中。具体的代码如清单 5 所示:
function callback(data) {
alert("Received value is: " + data.rvalue);
}
|
这样 Worklight 提供的对本地功能的访问模式就完成了,不过从实质上讲它的调用是基于本地页面的,如果应用只是希望获得本地的数据(比如联系人的电话),这种实现方式就显得拖沓。
Cordova 插件的方法与上面不同之处就在于,系统可以直接调用具体的本地方法,直接获取需要的数据,这样就不需要相应的本地页面了。在接下来的示例中应用将实现 Cordova 插件的开发,具体的流程是根据用户输入的名称信息,返回相应的欢迎信息。根据插件系统的介绍,首先要完成一个本地代码,代码的内容如清单 6 所示:
public class ExamplePlugin extends Plugin {
@Override
public PluginResult execute(String action, JSONArray arguments,
String callbackId) {
PluginResult result = null;
if (action.equals("test")){
String rText = "Input content is: ";
try {
rText+= ", " + arguments.getString(0);
result = new PluginResult(PluginResult.Status.OK, rText);
} catch (JSONException e) {
// ....
}
} else {
result = new PluginResult(PluginResult.Status.INVALID_ACTION);
}
return result;
}
}
|
从代码中可以看到,插件代码需要继承的是一个 Plugin 类,并且实现相应的 execute 方法。在方法中三个参数分别是:执行的动作名称(action)、动作的参数数组(arguments)和回调函数的 ID(在 JavaScript 中使用)。系统通过 action 的名称和传入参数完成一个 PluginResult 的构建,最后将该结果返回给调用网页。
配置文件的位置是 res 下的一个 plugins.xml 文件(相应的内容可以查看 Phonegap 的 开始文档),清单 7 就是 XML 文件修改后的内容:
<plugin name="ExamplePlugin" value="wang.test.ExamplePlugin" />
</plugins>
|
在网页端调用插件时,需要完成三个函数:插件 JavaScript 类的创建,插件的注册以及插件的调用。具体的内容如清单 8 所示:
function ExamplePlugin(){
}
ExamplePlugin.prototype.test = function(onTestSuccess, onTestFailure, para){
cordova.exec(onTestSucc, onTestFail, "ExamplePlugin", "test", [para]);
};
cordova.addConstructor(function() {
cordova.addPlugin("examplePlugin", new ExamplePlugin());
});
function callPlugin(){
window.plugins.examplePlugin.test(testSuccess, testFailure, "parameter");
}
|
从代码中可以看到,JavaScript 先初始化了一个对象,然后实现了它的 test 函数。在 Cordova 支持的插件调用函数中定义了五个参数,分别是:成功回调函数,失败回调函数,调用的类和动作,以及传入的参数。通过 Cordova 提供的函数将插件放入 Cordova 中,被上层函数调用时只需要调用事先定义的原型(prototype)函数就可。
在完成了页面集成和插件编写后,需要分析一下这三种模式,在企业级应用中的取舍。从使用的稳定度和难易度来看,使用 Cordova 是最好的,它不但有完整的说明文档和详细的例子,并且框架的版本不断更新,可以期望有更好的功能加入。相应的插件也是一个不错的选择,虽然自己编写插件需要一定的本地代码知识,但是开源网站上的公开插件使得开发者可以先寻找合适的插件,再考虑自己编写的问题,这样就可以获得和 Cordova 原始版本类似的体验。
Worklight 提供的方式虽然灵活,但是需要很多的本地代码知识,而且必须借助一个本地页面进行操作,这样就大大降低了模式的适用性,而且手机端操作,用户总是希望操作的步骤要尽量简单,强制加入的本地页面使得用户的操作步骤增加了,减少了应用的友好性。
在比较插件模式和 Worklight 模式时,两者的最大区别在于后者需要自行创建的一个本地页面。所以 Worklight 模式可能用到的地方就是必须需要本地页面的环境下:比如用 3D 函数来呈现绚丽的页面,编辑拍摄的照片和设置音频视频等等,这些功能在企业级应用中情况较少。所以,在企业级应用开发中参考的方案应该按照如下排列:首先考虑 Cordova 框架和一些开源的 Cordova 插件,然后再考虑自行编制 Cordova 插件,当上面的方式均不行时最后才考虑 Worklight 的模式。
提供良好的本地功能是开发专业的企业级手机应用的有效手段,好的应用不但应该可以查看企业的各种信息,还应该允许用户进行丰富的操作,完成企业规定的流程。对于适应各种可能存在的需求,本地功能开发提供了良好的帮助。但是需要注意的是,本地功能不能成为 web 模式手机应用开发的主要形式,如果应用中需要大量用到本地代码,应该重新考虑设计方案,直接使用本地代码来完成应用系统。
熟悉 Cordova 的架构,并且了解在开源框架中成熟的插件产品,这样才能在有需求时减少本地代码的开发量,保持利用 web 模式开发手机应用的较高效率。