Android Html5开发(二)-Cordova
本文介绍Android Html的开发框架Cordova
目录
一.Cordova简介
二.Cordova开发环境搭建
三.添加Cordova插件
四.添加自定义插件
五.JavaScript调用Android代码的过程
六.Android 和 Html5混合开发的性能
一.Cordova简介
介绍Cordova前需要先介绍下PhoneGap.
PhoneGap是一个用基于HTML,CSS和JavaScript的,创建移动跨平台移动应用程序
的快速开发平台。它使开发者能够利用iPhone,Android,Palm,Symbian,WP7,
WP8,Bada和Blackberry智能手机的核心功能——包括地理定位,加速器,联系人,声
音和振动等,此外PhoneGap拥有丰富的插件,可以调用。
业界很多主流的移动开发框架均源于PhoneGap。较著名的有Worklight、appMobi、
WeX5等;其中WeX5为国内打造,完全Apache开源,在融合Phonegap的基础上,
做了深度优化,具备接近Native app的性能,同时开发便捷性也较好。
PhoneGap和Cordova的关系:
在2011年10月,Adobe收购了Nitobi Software和它的PhoneGap产品,然后宣布这个移动Web开发框架将会继续开源,并把它提交到Apache Incubator,以便完全接受ASF的管治。当然,由于Adobe拥有了PhoneGap商标,所以开源组织的这个PhoneGap v2.0版产品就更名为Apache Cordova.Cordova是PhoneGap贡献给Apache后的开源项目,是从PhoneGap中抽离出的核心代码,是驱动PhoneGap的核心引擎.
也就是说PhoneGap是Adobe的商业产品,Cordova是Apache的开源项目,我们在使用时应当使用Cordova.
二.安装Cordova
Cordova 官网
安装Cordova开发环境
http://cordova.apache.org/#getstarted
在安装Cordova前需要先安装Node.js 和NPM
安装命令
npm install -g cordova
创建你的第一个应用
http://cordova.apache.org/docs/en/6.x/guide/cli/index.html
1.
cordova create hello com.example.hello HelloWorld
2.
cd hello
3.
cordova
platform add android --save
添加Android platfrom之后可以用Eclipse 导入hello/platforms/android下的工程
一个是CordovaLib ,一个是MainActivity.
CordovaLib 时Cordova框架的源码.
如图:
CordovaLib提供了JavaScript和Android之间相互调用的框架.
在第一个CordovaHelloWorld程序中,MainActivity代码如下
加载的launchUrl,是assets/www/index.html.
Cordova是基于插件开发的,Android提供给JavaScript调用的API应该已插件的形式提供.Cordova本身已经拥有丰富的插件.下面介绍如果添加Cordova 插件
三.添加Cordova插件
这里以添加contacts
插件为例
执行
:cordova
plugin add cordova-plugin-contacts
就可以添加
cordova-plugin-contacts
插件
Android
部分
添加之后会在
MainActiviy
里面增加如下代码
:
这部分就是通讯录插件Android端所有的代码.其中ContactManager继承了
CordovaPlugin,并重写了下面方法.
publicboolean
execute(String action,
CordovaArgs args,
CallbackContext callbackContext)
/res/xml/config.xml文件中增加了
<featurename="Contacts">
<paramname="android-package"value="org.apache.cordova.contacts.ContactManager"/>
</feature>
用于注册Contacts插件
featurename="Contacts"是定义js通过下面方法调用Android接口时传入的
Theplugin's JavaScript interface uses the cordova
.
exec
methodas follows:
exec
(<
successFunction
>,
<failFunction>
,
<service>
,
<action>
,
[<
args
>]);
service名
<paramname="android-package"value="org.apache.cordova.contacts.ContactManager"/>
是ContactManager类的全名
JavaScript部分:
在/assets/www/plugins目录下增加了一个cordova-plugin-contacts文件夹存放Contacts插件所有的js代码
在/assets/www/cordova_plugins.js里面注册该模块
其中
"clobbers":[
"navigator.contacts"
]
声明了
contacts.js里面定义的contacts对象可以通过navigator.contacts来访问.
使用示例:
在/assets/www/js/index.js的onDeviceReady方法里面加入如下代码,调用
查找所有联系人的方法.
functiononSuccess(contacts) {
for(vari = 0; i < contacts.length; i++) {
console.log(contacts[i].displayName);
}
};
functiononError(contactError) {
alert('onError!');
};
//find all contacts
varoptions = newContactFindOptions();
options.filter= "";
options.multiple= true;
varfilter = ["displayName","addresses"];
navigator.contacts.find(filter,onSuccess, onError, options);
参考:
http://cordova.apache.org/docs/en/latest/cordova-plugin-contacts/index.html
四.添加自定义插件
这里以添加一个EchoPlugin插件为例
Android端:
1.编写EchoPlugin继承CordovaPlugin并重写execute方法
publicclass
EchoPlugin extends
CordovaPlugin {
privatestaticfinal
String TAG
= EchoPlugin.class.getSimpleName();
@Override
publicvoid
initialize(CordovaInterface cordova,
CordovaWebView webView)
{
super.initialize(cordova,webView);
//your initcode here
Log.e(TAG,"initialize");
}
@Override
publicboolean
execute(String action,
JSONArray args,
CallbackContext callbackContext)
throws
JSONException {
Log.e(TAG,"action== "+ action);
Log.e(TAG,"args== "+ args);
Log.e(TAG,"callbackContext== "+ callbackContext);
if
("echo".equals(action))
{
callbackContext.success(args+ "from android ");
returntrue;
}
returnfalse; //Returning false results in a "MethodNotFound" error.
}
}
2.在/res/xml/config.xml文件中增加:
<featurename="Echo">
<paramname="android-package"value="com.example.echo.EchoPlugin"/>
</feature>
注册EchoPlugin插件,这样就完成了Android端的插件开发
JavaScript端:
如果只想调用EchoPlugin提供的方法,执行下面代码即可
cordova.exec(function(msg){
console.log(msg);
},function(err){
console.log("Nothingto echo.'"+ err);
},"Echo","echo",[]);
service名为Echo,action为echo
但实际使用中我们应该在js端也实现模块化.
1.在/assets/www/plugins目录下新建
com-example-echo目录,并新建一个echo.js文件
里面的代码如下:
//定义一个id为com-example-echo.echo的模块
//模块注册,加载参照 http://rensanning.iteye.com/blog/2047324
cordova.define("com-example-echo.echo",function(require,exports, module) {
//加载cordova/exec模块
varexec = require('cordova/exec');
//定义一个Echo对象
varEcho = {
//为Echo对象定义一个echo方法
echo:function(msg){
console.log("ech0");
exec(function(msg){
console.log(msg);
},function(err){
console.log("Nothingto echo.'"+ err);
},"Echo","echo",[msg]);
}
};
//模块的输出为Echo对象
module.exports= Echo;
});
这段代码的含义是定义一个id为”com-example-echo.echo”的模块,模块的输出时一个Echo的对象.
然后在/assets/www/cordova_plugins.js里面注册该模块
{
"file":"plugins/com-example-echo/echo.js",
"id":"com-example-echo.echo",
"pluginId":"com-example-echo",
"clobbers":[
"navigator.Echo"
]
}
这样就可以通过navigator.Echo来调用该方法.
调用示例如下:
在/assets/www/js/index.js的onDeviceReady方法里面加入
navigator.Echo.echo('helloword');
即可.
参考
http://cordova.apache.org/docs/en/latest/guide/platforms/android/plugin.html
五.JavaScript调用AndroidAPI的内部执行过程
这里就以调用navigator.Echo.echo('helloword')方法为例,首先对代码进行一下剖析
1)调用navigator.Echo.echo('helloword')方法,实际执行的是下面代码
cordova.exec(function(msg){
console.log(msg);
},function(err){
console.log("Nothingto echo.'"+ err);
},"Echo","echo",[]);
查看cordova.js源码,可以看到
modulemapper.clobbers('cordova/exec','cordova.exec');
modulemapper.clobbers('cordova/exec','Cordova.exec');
cordova.exec的定义如下
define("cordova/exec",function(require,exports, module) {
…/
module.exports= androidExec;
});
也就是说cordova.exec指向的是androidExec方法,
androidExec的定义如下:
functionandroidExec(success, fail, service, action, args)
参数说明:
success:成功回调
fail:失败回调
service:插件对应的service,对应EchoPlugin中的Echo,用于从插件列表中找到EchoPlugin这个插件
action:执行的哪个方法,对应EchoPlugin中的echo;
args:参数,是一个JSONArray;
从该方法可以看出JS调用AndroidNative接口都是通过回调异步调用的.
androidExec执行时调用的是
varmsgs = nativeApiProvider.get().exec(bridgeSecret, service, action,callbackId, argsJson);
nativeApiProvider.get()指向的是下面方法
define("cordova/android/promptbasednativeapi",function(require,exports, module) {
/**
*Implements the API of ExposedJsApi.java, but uses prompt() tocommunicate.
*This is used pre-JellyBean, where addJavascriptInterface() isdisabled.
*/
module.exports= {
exec:function(bridgeSecret,service, action, callbackId, argsJson) {
returnprompt(argsJson, 'gap:'+ JSON.stringify([bridgeSecret, service, action, callbackId]));
},
setNativeToJsBridgeMode:function(bridgeSecret,value) {
prompt(value,'gap_bridge_mode:'+ bridgeSecret);
},
retrieveJsMessages:function(bridgeSecret,fromOnlineEvent) {
returnprompt(+fromOnlineEvent, 'gap_poll:'+ bridgeSecret);
}
};
});
最终调用的是:
prompt(argsJson,'gap:'+ JSON.stringify([bridgeSecret, service, action, callbackId]));
prompt是Window对象的一个方法,用于显示可提示用户输入的对话框。
在Android端调用该方法会回调WebView的WebChromeClient的
publicboolean onJsPrompt(WebView view, String origin, String message, String defaultValue, final JsPromptResult result);
方法.
查看CordovaLib源码,可以看到在SystemWebChromeClient里面对该方法进行了重写
publicboolean
onJsPrompt(WebView view,
String origin,
String message,
String defaultValue,
final
JsPromptResult result)
{
//Unlike the @JavascriptInterface bridge, this method is always calledon the UI thread.
Log.d(TAG,"onJsPrompt"+ origin+ "
message == "
+ message);
StringhandledRet= parentEngine.bridge.promptOnJsPrompt(origin,message,defaultValue);
if
(handledRet
!= null){
result.confirm(handledRet);
}
else
{
dialogsHelper.showPrompt(message,defaultValue,
new
CordovaDialogsHelper.Result() {
@Override
publicvoid
gotResult(booleansuccess,String value){
if
(success)
{
result.confirm(value);
}
else
{
result.cancel();
}
}
});
}
returntrue;
}
执行调用的关键代码是
StringhandledRet= parentEngine.bridge.promptOnJsPrompt(origin,message,defaultValue);
promptOnJsPrompt中关键代码是
Stringr= jsExec(bridgeSecret,service,action,
callbackId,
message);
jsExec中关键代码是
pluginManager.exec(service,action,callbackId,arguments);
exec中
//根据service获取对应插件
CordovaPluginplugin= getPlugin(service);
//执行对应插件的execute方法
booleanwasValidAction= plugin.execute(action,rawArgs,callbackContext);
这也就是为什么编写插件时需要继承CordovaPlugin,并重写execute方法.
@Override
publicboolean
execute(String action,
JSONArray args,
CallbackContext callbackContext)
throws
JSONException {
Log.e(TAG,"action== "+ action);
Log.e(TAG,"args== "+ args);
Log.e(TAG,"callbackContext== "+ callbackContext);
if
("echo".equals(action))
{
callbackContext.success(args+ "from android ");
//if fail
callbackContext.error("err");
returntrue;
}
returnfalse; //Returning false results in a "MethodNotFound" error.
}
在execute方法执行完后,通过callbackContext.success()或者callbackContext.error()将结果回调给JS端.
查看源码,可以知道,callbackContext最终是通过loadUrl的方式,将结果回调给JS的.
Stringjs= queue.popAndEncodeAsJs();
if
(js
!= null){
engine.loadUrl("javascript:"+ js,
false);
}
六.Android 和 Html5混合开发的性能
这里只截取运行一个Android Hello word程序和Android +HTML5混合开发时,程序运行后的线程截图
1.Android Hello word,程序运行后会有18个线程
2.不使用cordova,直接使用webiew时,程序允许后会有31个线程
3.使用cordova框架,程序允许后会有36个线程