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 官网

http://cordova.apache.org/

安装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框架的源码.

如图:

android html5 性能 android html5开发_PhoneGap










 

CordovaLib提供了JavaScript和Android之间相互调用的框架.

在第一个CordovaHelloWorld程序中,MainActivity代码如下


android html5 性能 android html5开发_android_02


 

 

 

 

 

 

 

 

 

 

加载的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 html5 性能 android html5开发_PhoneGap_03



 

这部分就是通讯录插件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代码


android html5 性能 android html5开发_android_04






 

在/assets/www/cordova_plugins.js里面注册该模块

android html5 性能 android html5开发_PhoneGap_05




 

其中

"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个线程


 

android html5 性能 android html5开发_Android_06


 


 


 

 

 

 

 

 

 

2.不使用cordova,直接使用webiew时,程序允许后会有31个线程

 

 

android html5 性能 android html5开发_Android_07


 


 


 


 


 


 

3.使用cordova框架,程序允许后会有36个线程

 

android html5 性能 android html5开发_android html5 性能_08