Android平台指南
这个指南展示如何安装SDK环境以便可以部署Cordova App在Android设备上,以及如何选择使用以Android为中心命令行工具在你的开发工作流中。 不管你是使用以平台为中心的shell工具还是跨平台的Cordova命令行你都需要安装Android SDK。要比较两种开发路径,请参见概述。要获取CLI的详细介绍请参见Cordova CLI 参考.
要求和支持情况
Cordova支持Android需要Android SDK,它可以安装在OS X, Linux和Windows上。查看 Android SDK的 系统要求. Cordova最新的Android包支持的是AndroidAPI-级别 23。 最近几年 cordova-android支持的Android API-级别可以在下面这个表中看到:
cordova-android 版本 | 支持的 Android API-级别 |
---|---|
5.X.X | 14 - 23 |
4.1.X | 14 - 22 |
4.0.X | 10 - 22 |
3.7.X | 10 - 21 |
注意这里列出的版本是给Cordova Android包, cordova-android的, 而不是Cordova CLI的。要想知道你的Cordova项目中安装的Cordova Android包的版本,你可以在项目目录中运行cordova platform ls
。
作为一般规则,当Android版本在Google分布面板占比跌破5%Cordova就不会支持了。
安装要求
Java开发工具包(JDK)
安装Java Development Kit (JDK) 7或者最新的。
当在Windows上安装的时候需要根据JDK路径设置JAVA_HOME
环境变量(参见设置环境变量)
Android SDK
安装Android Stand-alone SDK或者Android Studio。如果你计划开发一个新的Cordova Android插件或者使用Android平台原生的工具去运行和调试,那么就使用Android Studio吧。否则,Android Stand-alone SDK Tools就足够构建和部署Android应用程序了。
详细的安装说明可以点击上面的安装链接查看。
添加SDK包
在安装完Android SDK后,你需要安装任何你希望的目标API级别的SDK包。建议你安装cordova-android(参见上面)支持的最高级别的SDK包。
打开Android SDK Manager (例如,在终端上运行`android),并确保下面已经安装:
- 目标Android版本的Android Platform SDK
- Android SDK build-tools,版本19.1.0或者之上
- Android Support Repository (在"Extras"查找)
参见 Android文档安装SDK包获得详细内容。
设置环境变量
为了使功能正常使用Cordova的CLI工具需要设置一些环境变量。CLI会尝试为你设置这些变量,但在某些情况下你需要手动设置。下面这些变量需要更新:
- 设置
JAVA_HOME
环境变量,指定为JDK安装路径 - 设置
ANDROID_HOME
环境变量,指定为Android SDK安装路径 - 同时也建议你添加Android SDK的
tools
和platform-tools
目录到你的PATH
OS X and Linux
在Mac或者Linux上面,你可以使用文本编辑器来创建或者修改文件~/.bash_profile
。为了设置一个环境变量,添加一行,使用export
像下面这样(用你本地安装路径替代路径):
export ANDROID_HOME=/Development/android-sdk/
为了更新PATH
,添加一行类似下面这样(路径替换为你本地的Android SDK安装路径):
export PATH=${PATH}:/Development/android-sdk/platform-tools:/Development/android-sdk/tools
重新启动终端或者运行下面命令来看变化带来的反应:
$ source ~/.bash_profile
Windows
这些步骤可能会因你安装的Windows版本而不同。在更改后,关闭并重新打开命令行提示符窗口,来看看他们的反应
点击桌面左下角的开始菜单
在搜索栏中,搜索环境变量并从出现的选项中选择编辑系统的环境变量
在出现的窗口中,点击环境变量按钮
创建一个新的环境变量:
- 点击新建... 并输入变量的名字和值
T设置你的PATH:
选择PATH变量并点击编辑。
添加条目到PATH相关的位置。 例如(用你本地的Android SDK安装路径替代路径):
C:\Development\android-sdk\platform-tools C:\Development\android-sdk\tools
项目配置
设置一个模拟器
如果你想运行你的Cordova应用在Android模拟器上面,首先你要创建一个Android虚拟设备(AVD)。查看Android文档管理AVD和配置模拟器和设置硬件加速的说明。
一旦你的AVD配置正确,在Cordova项目里面运行下面命令你应该可以可以看到他们:
$ cordova run --list
配置Gradle(一个构建工具)
自cordova-android@4.0.0起,Cordova为Android项目使用 Gradle构建。关于用Ant构建的说明,请参考老版本的文档。
设置Gradle属性
通过设置确定的Cordova暴露的Gradle属性 配置Gradle构建是可能的。下面的属性是可以被设置的:
属性 | 描述 |
---|---|
cdvBuildMultipleApks | 如果这个被设置了,那么多个APK文件将会被产生:每一个是由库项目(x86, ARM等)支持的本地平台。如果你的项目使用一个很大的本地库,这会显著提高所产生的APK的大小,这是很重要的。如果不设置,可以在所有设备上使用的单个APK将产生。 |
cdvVersionCode | 重写设置在AndroidManifest.xml 里面的versionCode |
cdvReleaseSigningPropertiesFile | 默认: release-signing.properties .properties文件的路径,这个文件包含发布版本的签名信息(参见Signing an App) |
cdvDebugSigningPropertiesFile | 默认: debug-signing.properties .properties文件的路径,这个文件包含调试版本的签名信息(参见Signing an App)。当你需要吧签名key分享给其他开发者这十分有用 |
cdvMinSdkVersion | 重写设置在AndroidManifest.xml 里minSdkVersion 的值。当基于SDK版本创建多个APK时是十分有用的 |
cdvBuildToolsVersion | 重写自动检测android.buildToolsVersion 的值 |
cdvCompileSdkVersion | 重写自动检测android.compileSdkVersion 的值 |
你可以设置这些属性通过下面四种方式之一:
像下面这样通过环境变量设置:
$ export ORG_GRADLE_PROJECT_cdvMinSdkVersion=20 $ cordova build android
通过使用
--gradleArg
标志在你的Cordovabuild
或者run
命令中:$ cordova run android -- --gradleArg=-PcdvMinSdkVersion=20
通过在你的Android平台目录(
<your-project>/platforms/android
)中放置一个名为gradle.properties
的文件,并在其中设置属性像这样:# In <your-project>/platforms/android/gradle.properties cdvMinSdkVersion=20
By extending 通过一个
build-extras.gradle
文件扩展build.gradle
,并设置属性像这样:// In <your-project>/platforms/android/build-extras.gradle ext.cdvMinSdkVersion = 20
后面两种方案都涉及包含了额外的文件在你的平台文件夹中。通常我们不鼓励你编辑这些文件夹中的内容,因为这些变化很容易丢失被重写。代替,这两个文件应该从另一个位置复制到这个文件夹,作为构建命令的一部分,通过使用before_build
钩子.
扩展build.gradle
如果你需要自定义build.gradle
,而不是直接编辑他,你要创建一个名为build-extras.gradle
的同级文件。这个文件将会包含在主build.gradle
里面,当他出现时。这个文件必须覆盖android平台目录(<your-project>/platforms/android
),所以建议通过绑定脚本到before_build
钩子来覆盖他。
这里有个例子:
// 例子build-extras.gradle
// 这个文件会包含在`build.gradle`开头
ext.cdvDebugSigningPropertiesFile = '../../android-debug-keys.properties'
// 当设置了,这个方法允许代码在`build.gradle`结尾处运行
ext.postBuildExtras = {
android.buildTypes.debug.applicationIdSuffix = '.debug'
}
注意插件可以包含在build-extras.gradle
文件:
<framework src="some.gradle" custom="true" type="gradleReference" />
设置Version Code(版本编码)
要改变你的应用程序的生成APK的version code,你可以设置应用程序config.xml文件的widget元素的android-versionCode
属性。如果android-versionCode
没有设置,版本编码将由version
属性决定。例子,如果版本是MAJOR.MINOR.PATCH
:
versionCode = MAJOR * 10000 + MINOR * 100 + PATCH
如果你的应用程序启动了cdvBuildMultipleApks
Gradle属性 (参见 设置Gradle属性),你应用的版本编码还得乘与10以便编码的最后一位可以用暗示apk的构建架构。不管你的版本编码来自android-versionCode
还是由version
生成,这个乘法都会发生。请注意一些插件添加到你的项目(包括 cordova-plugin-crosswalk-webview)可能会自动设置Gradle属性。
请注意: 当更新android-versionCode
属性时,从构建的APK递增版本编码是不明智的。代替的,你应该基于config.xml
文件里android-versionCode
属性来递增编码。这是因为cdvBuildMultipleApks
属性导致版本编码在构建的apk中被乘与10,所以使用这个值将会导致下一个版本编码是原始的一百倍,等等。
签名一个应用
首先你应该阅读Android应用签名所需。
使用标志
签名一个应用,你需要下面参数:
参数 | 标志 | 描述 |
---|---|---|
Keystore | --keystore | 用来存储一组key的二进制文件路径 |
Keystore Password | --storePassword | keystore存储密钥 |
Alias | --alias | 用来指定私有key用来签名 |
Password | --password | 私有key的密码 |
Keystore的类型 | --keystoreType | 默认: 自动检测基于文件扩展名 pkcs12或者jks |
这些参数可以通过上面的Cordova CLI build
或者 run
命令来指定命令行参数。
注意: 你应该使用两个中划线 --
来表示这些平台特定参数,例如:
cordova run android --release -- --keystore=../my-release-key.keystore --storePassword=password --alias=alias_name --password=password
.
使用build.json
或者,你可以在相同的命令中使用--buildConfig
参数传递构建配置文件(build.json
),并在其中指定他们。这里有个样例配置文件:
{
"android": {
"debug": {
"keystore": "../android.keystore",
"storePassword": "android",
"alias": "mykey1",
"password" : "password",
"keystoreType": ""
},
"release": {
"keystore": "../android.keystore",
"storePassword": "",
"alias": "mykey2",
"password" : "password",
"keystoreType": ""
}
}
}
对于发布签名,密码可以排除在外,构建系统将会发出提示要求输入密码。
这里同时也支持,命令行参数和build.json
参数混合。命令行中的参数优先。这是十分有用的在命令行中输入密码。
使用Gradle
你还可以通过包含一个.properties
文件指定签名属性,并指向他到cdvReleaseSigningPropertiesFile
和 cdvDebugSigningPropertiesFile
Gradle属性 (see 设置Gradle属性). 这个文件应该像这样:
storeFile=relative/path/to/keystore.p12
storePassword=SECRET1
storeType=pkcs12
keyAlias=DebugSigningKey
keyPassword=SECRET2
storePassword
和 keyPassword
是选的,如果省略了将会提示
调试
为了获取于AndroidSDK打包在一起调试工具的详细信息, 可以看 Android调试开发者文档。 另外, Android调试web应用程序开发者文档提供了你的应用程序员运行在Webview中这部分调试的介绍。
在Android Studio中打开一个项目
Cordova的Android项目可以被Android IDEAndroid Studio打开。如果你想使用Android Studio内置的Android调试/分析工具或者你要开发Android插件这是十分有用的。请注意当你在Android studio里打开你的项目,建议你不要编辑你的代码在IDE中。这会在 platforms
目录中编辑你的代码(而不是 www
),并且变化将会被重写。代替,编辑www
目录并通过运行cordova build
来拷贝过来你的变化。
Plugin开发者希望编辑原生代码在IED中应该是使用--link
标志,当他们通过 cordova plugin add
添加插件到项目。这会链接文件,以便在platforms
文件夹中的插件文件变化会映射到插件源文件夹(反之亦然)。
要在Android Studio中打开Cordova的Android项目:
启动 Android Studio.
选择 Import Project (Eclipse ADT, Gradle, etc).
选择你项目中的Android platform目录(
<your-project>/platforms/android
)。对于
Gradle Sync
问题你可以简单的回答 Yes.
一旦导入完成,你应该可以在Android Studio中直接构建和运行应用。 参见 Android Studio概述 和 从Android Studio中构建和运行活的详细信息。
平台为中心的工作流
cordova-android包含很多脚本,可以让你以不完全的Cordova CLI使用平台。这种开发路径给开发者很大选择在特定的环境而不是跨平台的cordova CLI。例如,你需要一个shell工具用来不熟自定义的Cordova WebView和本地组件一起。在使用这个开发路径之前,你任然需要配置Android SDK环境,如要求和支持情况上面描述的。
对于下面每一个讨论的脚本, 参考Cordova CLI参考手册获取更多关于参数和使用的信息。对于每一个脚本有一个于CLI命令对应的名字。比如 cordova-android/bin/create
对应于cordova create
。
为了开始, 载cordova-android包从npm 或者 Github。
为了创建一个项目使用下面包, 运行 create
脚本在bin
目录:
$ cordova-android/bin/create ...
创建的项目将会包含一个叫做 cordova
目录,在这里有针对特定项目的Cordova命令脚本(比如run
, build
等等)。另外,这个项目的结构将不同于普通的Cordova项目。值得注意的 /www
被移到/assets/www
。
为了安装插件在这个项目, 使用Cordova Plugman功能.
升级
参考这个 文章介绍了更新你的cordova-android
版本。
生命周期(Lifecycle)指南
Cordova和Android
原生的Android应用通常由一系列活动组成,用于与用户交互。活动可以被认为一个单独的屏幕,组成了一个应用程序。在应用中不同的任务通常拥有自己的活动。每个活动都有自己的生命周期,作为活动的进入和离开用户设备前景维护。
比较起来,Android平台的Cordova应用运行在一个嵌入单独Android活动中的Webview中。活动的生命周期通过文档事件触发暴漏给你的应用程序,事件不保证与Android的生命周期对齐,但可以提供保存和恢复状态的指导方针。这些事件大致与Android回调对应如下:
Cordova事件 | 粗略的Android等效 | 含义 |
---|---|---|
deviceready | onCreate() | 应用程序开始(不是从背景) |
pause | onPause() | 应用程序移动到背景 |
resume | onResume() | 应用程序返回到前景 |
大多数其他Cordova平台有类似的生命周期概念,当类似的动作发生在用户设备上的时候,应该触发同样的事件。然而,Android平台会有一些独有的事件会触发,这归功于原生活动周期。
什么使Android不同?
在Android设备中,操作系统可以选择在后台杀死活动来释放资源,如果当前设备运行程序的内存过低。不幸的是,当支持你的应用程序的活动被杀死,生存在Webview中的应用程序也会被销毁。这种情况任何应用程序维护的状态都会丢失。当用户重新导航到应用程序,活动和Webview将会由操作系统重新创建,但是在你的Cordova应用中状态不会自动恢复。由于这个原因,你的应用程序知道生命周期被触发并维持任何确保用户在离开应用程序用户上下文不丢失的状态,是必须的。
什么时候这会发生?
你的应用程序是很容易被操作系统销毁的,当它离开用户视野的时候,这里有种主要情况会发生。第一种是最显然的情况用户按home键或者切换到另外一个应用程序。
然而,这里有第二种情况(更加微妙),一个插件被引用。如上所述,Cordova应用通常被限定在一个包含Webview的活动中。这里有一些其他的活动的实例会被插件创建并暂时将Cordova活动放入背景。这些其他活动通常是为了执行特定的任务,通过使用安装在用户设备上的原生应用。例如,Apache camera插件启动任何一个原生安装在设备上的camera活动来获取照片。通过这种方式重复利用安装的camera应用,让用户尝试获取照片使用起来更像原生应用。不幸的是,当原生的活动将你的应用放入背景,会给操作系统一次kill掉他的机会。
为了更清晰的理解第二种情况,我们将走过一个使用camera插件的例子。想象一下你有一个应用程序需要获取用户头像资料。当一切都按照计划,应用中的事件流应该是这样子的:
- 用户与app交互并需要获得一张图片
- camera插件启动一个camera活动
- Cordova活动被推送入后台(pause事件触发)
- 用户获得一张图片
- camera活动结束
- Cordova活动被移动到前台(resume事件被触发)
- 用户返回离开的应用程序
然而,如果设备内存过低事件流会被打断。如果活动被操作系统杀死掉,上面的事件流序列会被下面代替:
- 用户与app交互并需要获得一张图片
- camera插件启动一个camera活动
- OS销毁Cordova活动(pause事件被触发)
- 用户获得一张图片
- camera活动结束
- 操作系统重新创建Cordova活动(deviceready和resume事件被触发)
- 用户是困惑的,因为他们突然返回到应用的登录界面
在这个实例里面,操作系统在背景中杀掉了应用程序并且应用程序不会作为生命周期的一部分来维护他的状态。当用户返回应用,Webview被重新创建并且应用从头开始重启(让用户更加困惑)。这个事件序列和用户按home键或者切换到其他应用是一样的。阻止上面体验的关键是订阅事件并合适的维护状态作为活动生命周期的一部分。
关于生命周期
在上面的例子,javascript事件的触发被标注成了斜体。这些事件有机会保存和恢复你的应用程序状态。你应该通过bindEvents
方法来注册应用程序回调来回应生命周期事件来保存状态。保存什么息和怎么保存信息由你决定,但是你要确保保存足够的信息,来精确的恢复到用户离开的地方,s当用户返回到应用时。
这里有一个额外的特性在上面的例子中,他仅仅应用与第二种讨论的情况(也就是一个插件启动了一个额外的活动)。不仅当用户获取完图片应用程序状态丢失,用户获取的图片也一样。通常那个图片会通过注册在插件上的一个回调传递给你的应用程序。然而,当Webview被摧毁,回调会被永远丢失。幸运的是,cordova-android 5.1.0及其以上提供了一个方法来获得插件的结果当你的应用恢复时。
检索插件回调结果(cordova-android 5.1.0+)
当操作系统销毁了由插件推送到背景的Cordova活动,任何添加的回调也丢失了同时。这意味着如果你传递了一个回调给插件,这个插件启动了一个新的活动(例子camera插件),这个回调不会触发当应用程序被重建。然而,从cordova-android 5.1.0开始,resume
事件有效载荷将会包含任何附加插件的结果从插件请求启动外部活动使优先级高于活动被销毁。
对于resume
事件的有效载荷我们坚持下面格式:
{
action: "resume",
pendingResult: {
pluginServiceName: string,
pluginStatus: string,
result: any
}
}
有效载荷的字段定义如下:
pluginServiceName
: 插件返回结果的名字(比如"Camera")。这个可以在插件plugin.xml文件中的<name>
标签中找到pluginStatus
: 插件调用状态(查看下面)result
: 任何插件调用的结果
pluginStatus
在pendingResult
中可能包含的值有:
"OK"
- 插件调用成功"No Result"
- 插件调用结束没有结果"Error"
- 插件调用结果一般的错误- 其他混杂的错误
"Class not found"
"Illegal access"
"Instantiation error"
"Malformed url"
"IO error"
"Invalid action"
"JSON error"
请注意插件决定什么包含在result
里面和pluginStatus
的含义。当你要使用的时候参考插件的API看看这个字段包含什么,以及如何使用这些值。
例子
下面是一个简单的例子使用resume
和 pause
事件管理状态。这里使用Apache camera插件做一个例子,展示如果检索插件调用结果从resume
有效载荷。代码处理resume
的event.pendingResult
对象部分需要cordova-android 5.1.0+
// 这个状态代表了应用程序的状态并且会在onResume()和onPause()中保存和恢复
var appState = {
takingPicture: true,
imageUri: ""
};
var APP_STORAGE_KEY = "exampleAppState";
var app = {
initialize: function() {
this.bindEvents();
},
bindEvents: function() {
// 这里我们注册我们关心的生命周期事件回调
document.addEventListener('deviceready', this.onDeviceReady, false);
document.addEventListener('pause', this.onPause, false);
document.addEventListener('resume', this.onResume, false);
},
onDeviceReady: function() {
document.getElementById("take-picture-button").addEventListener("click", function() {
//由于camera插件方法启动了一个外部活动
//这里有一次机会我们的应用程序被kill掉在回调被成功或者失败调用之前
// 在onPause()和onResume()那里我们保存和恢复状态,来处理这个事情
appState.takingPicture = true;
navigator.camera.getPicture(cameraSuccessCallback, cameraFailureCallback,
{
sourceType: Camera.PictureSourceType.CAMERA,
destinationType: Camera.DestinationType.FILE_URI,
targetWidth: 250,
targetHeight: 250
}
);
});
},
onPause: function() {
// 这里我们检测我们是否在获取图片,如果在,我们希望保存我们的状态以便onResume()
// 恢复的时候使用,如果我们获得了图片URI我们也要存储
if(appState.takingPicture || appState.imageUri) {
window.localStorage.setItem(APP_STORAGE_KEY, JSON.stringify(appState));
}
},
onResume: function(event) {
// 这里我们检差存储的状态,如果需要恢复他。由你跟踪任何添加的插件结果的来源
// (也就是说你代码的哪一步被调用),还有什么参数提供给插件如果相关
var storedState = window.localStorage.getItem(APP_STORAGE_KEY);
if(storedState) {
appState = JSON.parse(storedState);
}
// 检查如果我们需要恢复我们的图片
if(!appState.takingPicture && appState.imageUri) {
document.getElementById("get-picture-result").src = appState.imageUri;
}
// 现在我们可以检测如果插件结果在事件对象里面
// 这里需要cordova-android 5.1.0+
else if(appState.takingPicture && event.pendingResult) {
// 检测插件调用是否成功并调用相应的回调。对于camera插件,"OK"
//意味着成功其他意味着错误
if(event.pendingResult.pluginStatus === "OK") {
// camera放置同样的结果在resume对象,因为成功回调传递给了getPicture(),
// 因此我们可以传递同样的回调,返回一些其他东西。查询文档,了解怎么解释你使用
// 插件的结果字段
cameraSuccessCallback(event.pendingResult.result);
} else {
cameraFailureCallback(event.pendingResult.result);
}
}
}
}
// 这里是回调我们传入getPicture()
function cameraSuccessCallback(imageUri) {
appState.takingPicture = false;
appState.imageUri = imageUri;
document.getElementById("get-picture-result").src = imageUri;
}
function cameraFailureCallback(error) {
appState.takingPicture = false;
console.log(error);
}
app.initialize();
对应的html:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<link rel="stylesheet" type="text/css" href="css/index.css">
<title>Cordova Android Lifecycle Example</title>
</head>
<body>
<div class="app">
<div>
<img id="get-picture-result" />
</div>
<Button id="take-picture-button">Take Picture</button>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>
测试活动生命周期
Android提供了开发者设置测试活动被破坏在低内存的情况下。在你的设备或者模拟上模拟低内存场景通过设置 "Don't keep activities"在开发者选项菜单。你应该在这种情况下做大量测试来确保你的应用保持正确的状态。