Android 平台指南

本指南將協助您設定開發環境,以便在 Android 裝置上建置 Cordova 應用程式。此外,它還提供了將 Android 特定命令列工具整合到您的開發工作流程中的選項。

無論您打算使用 Android 特定命令列工具還是 Cordova CLI 命令,您都需要安裝並設定必要的環境需求。

Android API 級別支援

下表中列出了與 Cordova-Android 發布版本對應的支援的 Android API 級別(Android 版本)

cordova-android 版本 Android API 級別(Android 版本) 程式庫 & 工具版本
12.0.x 24 (7.0) - 33 (13.0)
  • 建置工具:^33.0.2
  • Kotlin:1.7.21
  • Gradle:7.6
  • Android Gradle 外掛:7.4.2
  • AndroidX 相容程式庫:1.6.1
  • AndroidX WebKit 程式庫:1.6.0
  • AndroidX 核心啟動畫面:1.0.0
  • Google 服務 Gradle 外掛:4.3.15
11.0.x 22 (5.1) - 32 (12L)
  • 建置工具:^32.0.0
  • Kotlin:1.7.21
  • Gradle:7.4.2
  • Android Gradle 外掛:7.2.1
  • AndroidX 相容程式庫:1.4.2
  • AndroidX WebKit 程式庫:1.4.0
  • AndroidX 核心啟動畫面:1.0.0-rc01
  • Google 服務 Gradle 外掛:4.3.10
10.1.x 22 (5.1) - 30 (11.0)
  • 建置工具:^30.0.3
  • Kotlin:1.5.21
  • Gradle:7.1.1
  • Android Gradle 外掛:4.2.2
  • AndroidX 相容程式庫:1.3.1
  • AndroidX WebKit 程式庫:1.4.0
  • Google 服務 Gradle 外掛:4.3.8
10.0.x 22 (5.1) - 30 (11.0)
  • 建置工具:^30.0.3
  • Kotlin:1.5.20
  • Gradle:7.1.1
  • Android Gradle 外掛:4.2.2
  • AndroidX 相容程式庫:1.3.0
  • AndroidX WebKit 程式庫:1.4.0
  • Google 服務 Gradle 外掛:4.3.5
9.X.X 22 (5.1) - 29 (10.0) -
8.X.X 19 (4.4) - 28 (9.0) -
7.X.X 19 (4.4) - 27 (8.1) -
6.X.X 16 (4.1) - 26 (8.0) -
5.X.X 14 (4.0) - 23 (6.0) -
4.1.X 14 (4.0) - 22 (5.1) -
4.0.X 10 (2.3.3) - 22 (5.1) -
3.7.X 10 (2.3.3) - 21 (5.0) -

注意: 上述列出的 cordova-android 版本並非 Cordova CLI 版本。

要找出您 Cordova 專案中安裝的 Cordova-Android 套件版本,請導覽至專案的根目錄並執行命令 cordova platform ls

Cordova 通常會停止支援在 Google 發布儀表板上低於 5% 的 Android 版本。您可以參考 Google 的 發布儀表板 以取得更多資訊。

系統需求

Cordova-Android 依賴 Android SDK,該 SDK 可以安裝在 macOS、Linux 或 Windows 作業系統上。

為了確保您的系統符合必要的環境需求,請參考 Google 提供的「安裝 Android Studio」指南。

必要的軟體 & 工具

Java 開發套件 (JDK)

如果您使用的是 cordova-android 10.0.0 或更高版本,請安裝 Java 開發套件 (JDK) 11

如果您使用的是 cordova-android 10.0.0 以下的任何版本,請安裝 Java 開發套件 (JDK) 8

必須根據您的 JDK 安裝路徑設定 JAVA_HOME 環境變數。請參閱 設定環境變數 章節,了解如何設定環境變數。或者,從 cordova-android 10.0.0 或更高版本開始,可以設定 CORDOVA_JAVA_HOME 來取代 JAVA_HOME,允許專門將 JDK 安裝用於 Cordova 開發。

Gradle

從 Cordova-Android 6.4.0 開始,需要安裝 Gradle

在 Windows 上安裝時,您需要將 Gradle 二進位目錄的路徑新增至您的 path 環境變數。請參閱 設定環境變數),了解如何設定系統環境變數。

注意: 這是系統的 Gradle 版本。系統的 Gradle 二進位檔將建立 Gradle Wrapper 檔案,該檔案宣告並取得建置 Android 應用程式所需的適當 Gradle 版本。系統層級和專案層級的 Gradle 版本可能不需且不必一致。專案層級的 Gradle 版本定義在 Cordova-Android 的套件中,並根據 Android 支援的版本設定。

Android Studio

下載並安裝 Android Studio。請按照連結的 Android 開發人員網站上的指示開始操作。

第一次開啟 Android Studio 將會引導您完成安裝 Android SDK 套件的過程。

SDK 套件

建議根據專案安裝的 Cordova-Android 版本安裝最新版本的 SDK 平台 & SDK 工具。請參閱 Android API 級別支援 章節,以找出根據 Cordova-Android 版本支援的版本。

安裝 SDK 平台
  1. 開啟 Android Studio 的
  2. 開啟 SDK 管理員 (工具 > SDK 管理員)
  3. 點擊 SDK 平台 標籤
  4. 選擇與 Android API 級別支援 中最高的支援 SDK 相符的 Android 版本
  5. 點擊「套用」

例如:如果專案已安裝 cordova-android@12.0.0,則最高的支援 SDK 為 33。在上述步驟 3 中,應該選擇「Android 13.0 (Tiramisu)」。

Android SDK Platform

安裝 Android SDK 建置工具
  1. 開啟 Android Studio 的
  2. 開啟 SDK 管理員 (工具 > SDK 管理員)
  3. 點擊 SDK 工具 標籤
  4. 勾選 顯示套件詳細資訊
  5. 展開 Android SDK 建置工具
  6. 根據 Android API 級別支援,勾選最高的支援建置工具。
  7. 點擊「套用」

例如:如果專案已安裝 cordova-android@12.0.0,則最高的支援 SDK 為 33。我們要選擇 33.x 的最高可用版本。在撰寫本文時,步驟 6 中應已選擇「33.0.2」。

Android SDK Build-Tools

安裝 SDK 命令列工具(最新)
  1. 開啟 Android Studio 的
  2. 開啟 SDK 管理員 (工具 > SDK 管理員)
  3. 點擊 SDK 工具 標籤
  4. 勾選 顯示套件詳細資訊
  5. 展開 Android SDK 命令列工具(最新)
  6. 勾選 Android SDK 命令列工具(最新)
  7. 點擊「套用」

SDK Command-line Tools (latest)

安裝 Android SDK 平台工具
  1. 開啟 Android Studio 的
  2. 開啟 SDK 管理員 (工具 > SDK 管理員)
  3. 點擊 SDK 工具 標籤
  4. 勾選 顯示套件詳細資訊
  5. 勾選 Android SDK 平台工具
  6. 點擊「套用」

Android SDK Platform-Tools

安裝 Android 模擬器
  1. 開啟 Android Studio 的
  2. 開啟 SDK 管理員 (工具 > SDK 管理員)
  3. 點擊 SDK 工具 標籤
  4. 勾選 顯示套件詳細資訊
  5. 勾選 Android 模擬器
  6. 點擊「套用」

Android Emulator

設定環境變數

Cordova 的 CLI 需要特定的環境變數才能正常運作。如果缺少環境變數,CLI 將嘗試暫時解析該變數。如果遺失的變數無法解析,則必須手動設定。

必須設定以下變數

  • JAVA_HOME - JDK 安裝位置的環境變數
  • ANDROID_HOME - Android SDK 安裝位置的環境變數

也建議更新 PATH 環境變數以包含以下目錄。

  • cmdline-tools/latest/bin
  • platform-tools
  • build-tools
  • emulator
    • 這是 apksignerzipalign 工具所必需的。

注意: 上述目錄通常位於 Android SDK ROOT 中。

macOS 和 Linux

在 Mac 或 Linux 上,使用文字編輯器建立或修改 ~/.bash_profile 檔案。

要設定環境變數,請新增一行使用 export 的程式碼,如下所示(將路徑替換為您的本機安裝位置)

export ANDROID_HOME=/Development/android-sdk/

若要更新您的 PATH,請新增一行類似以下的程式碼(將路徑替換為您的本機 Android SDK 安裝位置)

export PATH=$PATH:$ANDROID_HOME/platform-tools/
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin/
export PATH=$PATH:$ANDROID_HOME/build-tools
export PATH=$PATH:$ANDROID_HOME/emulator/

重新載入您的終端機以查看此變更的反映,或執行以下命令

source ~/.bash_profile

Windows

這些步驟可能會因您安裝的 Windows 版本而異。進行變更後,關閉並重新開啟任何命令提示字元視窗以查看其反映。

  1. 點擊 開始 功能表或按下 Windows 鍵 (Win)
  2. 在搜尋列中輸入 環境變數
  3. 選取 編輯系統環境變數 選項
  4. 點擊出現的視窗中的 環境變數... 按鈕。
要建立新的環境變數
  1. 點擊 新增... 按鈕
  2. 輸入 變數名稱
  3. 輸入 變數值
  4. 點擊 確定 按鈕
設定您的 PATH
  1. 從已定義的變數清單中選取 PATH
  2. 點擊 編輯... 按鈕
  3. 點擊 新增 按鈕
  4. 輸入相關位置。

重複步驟 3 和 4,直到新增所有路徑。

範例路徑(將路徑替換為您的本機 Android SDK 安裝位置)

C:\Users\[your user]\AppData\Local\Android\Sdk\platform-tools
C:\Users\[your user]\AppData\Local\Android\Sdk\cmdline-tools\latest\bin
C:\Users\[your user]\AppData\Local\Android\Sdk\build-tools
C:\Users\[your user]\AppData\Local\Android\Sdk\emulator

新增所有路徑後,點擊 確定 按鈕,直到所有用於設定和編輯環境變數的開啟視窗都關閉。

專案設定

設定模擬器

如果您希望在 Android 模擬器上執行您的 Cordova 應用程式,您首先需要建立一個 Android 虛擬裝置 (AVD)。

請參閱以下 Android 文件,以取得更多關於

一旦您的 AVD 設定正確,您應該可以透過執行以下命令將您的 Cordova 應用程式部署到模擬器

cordova run --emulator

設定 Gradle

Cordova-Android 專案是使用 Gradle 建置的。

設定 Gradle 屬性

可以透過設定 Cordova 公開的某些 Gradle 屬性的值來設定 Gradle 建置。

以下屬性可用

屬性 描述
cdvAndroidXAppCompatVersion 設定 androidx.appcompat:appcompat 函式庫的版本。
cdvAndroidXWebKitVersion 設定 androidx.webkit:webkit 函式庫的版本。
cdvBuildArch 覆寫應用程式建置的目標架構。預設值由 Cordova 的建置腳本自動偵測。
cdvBuildMultipleApks 如果設定此項,則會產生多個 APK 檔案:每個函式庫專案支援的原生平台 (x86、ARM 等) 各一個。如果您的專案使用大型原生函式庫,這點可能很重要,因為這樣會大幅增加產生的 APK 大小。如果未設定,則會產生單一 APK,可用於所有裝置
cdvBuildToolsVersion 覆寫自動偵測到的 android.buildToolsVersion
cdvCompileSdkVersion 設定應用程式編譯的框架 SDK 版本。此設定將覆寫對 android.compileSdkVersion 值的自動偵測。
cdvDebugSigningPropertiesFile 預設值:debug-signing.properties
指向 .properties 檔案的路徑,其中包含偵錯組建的簽署資訊 (請參閱簽署應用程式)。當您需要與其他開發人員共用簽署金鑰時很有用
cdvMaxSdkVersion 設定應用程式可執行的最大 API 等級
cdvMinSdkVersion 覆寫 AndroidManifest.xml 中設定的 minSdkVersion 值。當基於 SDK 版本建立多個 APK 時很有用
cdvReleaseSigningPropertiesFile 預設值:release-signing.properties
指向 .properties 檔案的路徑,其中包含發佈組建的簽署資訊 (請參閱簽署應用程式)
cdvSdkVersion 覆寫 targetSdkVersion 值。
cdvVersionCode 覆寫 AndroidManifest.xml 中設定的 versionCode
cdvVersionCodeForceAbiDigit 當只建置單一 APK 時,是否要在 versionCode 後面附加一個 0「abi 位數」。

您可以使用以下四種方式之一設定這些屬性

  • 使用環境變數

    範例

      export ORG_GRADLE_PROJECT_cdvMinSdkVersion=20
      cordova build android
    
  • --gradleArg 旗標與 Cordova buildrun 命令搭配使用

    範例

      cordova run android -- --gradleArg=-PcdvMinSdkVersion=20
    
  • 在專案的 Android 平台目錄中建立 gradle.properties

    <project-root>/platforms/android 目錄中建立名為 gradle.properties 的檔案,內容如下

    範例檔案內容

      cdvMinSdkVersion=20
    
  • 使用 `build-extras.gradle` 檔案擴充 build.gradle

    <project-root>/platforms/android/app 目錄中建立名為 build-extras.gradle 的檔案,內容如下

      ext.cdvMinSdkVersion = 20
    

後兩個選項都涉及在您的 Android 平台資料夾中包含額外的檔案。一般而言,不建議編輯此資料夾的內容,因為這些變更很容易遺失或被覆寫。相反地,這些檔案應該透過使用 before_build hook 腳本,作為建置命令的一部分複製到資料夾中。

擴充 build.gradle

如果您需要自訂 build.gradle 檔案,建議您建立一個名為 build-extras.gradle 的同級檔案,而不是直接編輯它。如果存在此檔案,則主 build.gradle 腳本會將其納入。此檔案必須放在 Android 平台目錄的 app 資料夾中 (<your-project>/platforms/android/app)。建議使用 before_build hook 腳本來複製此檔案。

以下是一個範例

// Example build-extras.gradle
// This file is included at the beginning of `build.gradle`

// special properties (see `build.gradle`) can be set and overwrite the defaults
ext.cdvDebugSigningPropertiesFile = '../../android-debug-keys.properties'

// normal `build.gradle` configuration can happen
android {
  defaultConfig {
    testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
  }
}
dependencies {
  androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2', {
    exclude group: 'com.android.support', module: 'support-annotations'
  }
}

// When set, this function `ext.postBuildExtras` allows code to run at the end of `build.gradle`
ext.postBuildExtras = {
    android.buildTypes.debug.applicationIdSuffix = '.debug'
}

請注意,外掛程式也可以透過以下方式包含 build-extras.gradle 檔案

<framework src="some.gradle" custom="true" type="gradleReference" />

設定 Gradle JVM 引數

若要變更 Gradle JVM 引數,可以使用 --jvmargs 旗標搭配 Cordova 的 buildrun 命令。這主要用於控制 gradle 在建置過程中允許使用的記憶體量。建議至少允許 2048 MB。

預設情況下,JVM 引數的值為 -Xmx2048m。若要增加允許的最大記憶體,請使用 -Xmx JVM 引數。以下提供範例

cordova build android -- --jvmargs='-Xmx4g'

支援以下單位

單位 範例
千位元組 k -Xmx2097152k
百萬位元組 m -Xmx2048m
十億位元組 g -Xmx2g

設定版本代碼

若要變更應用程式產生 APK 的 版本代碼,請在應用程式的 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 產生,都會進行此乘法運算。

注意:請注意,新增至專案的某些外掛程式可能會自動設定此 Gradle 屬性。

注意:更新 android-versionCode 屬性時,不建議遞增從已建置的 APK 中取得的版本代碼。建議根據 config.xml 檔案的 android-versionCode 屬性中的值遞增代碼。這是因為 cdvBuildMultipleApks 屬性會導致已建置的 APK 中的版本代碼乘以 10,因此使用該值會導致您的下一個版本代碼是原始版本的 100 倍,依此類推。

簽署應用程式

建議您先閱讀 Android 關於簽署您的應用程式的文件,因為其中包含建立簽署所需檔案的必要步驟。

使用旗標

若要簽署應用程式,您需要以下參數

參數 旗標 描述
金鑰儲存庫 --keystore 指向可保存一組金鑰的二進位檔案的路徑
金鑰儲存庫密碼 --storePassword 金鑰儲存庫的密碼
別名 --alias 指定用於簽署的私密金鑰的 ID
密碼 --password 所指定私密金鑰的密碼
金鑰儲存庫的類型 --keystoreType 預設值:根據檔案副檔名自動偵測
pkcs12 或 jks
套件類型 --packageType 預設值:偵錯使用 apk,發佈使用 bundle
指定要建置 APK 還是 AAB (Android App Bundle) 檔案。
可接受的值:apkbundle

上述參數可以在使用 Cordova CLI buildrun 命令時指定為引數。

注意:您應該使用雙 -- 來表示這些是平台特定的引數。

範例

cordova run android --release -- --keystore=../my-release-key.keystore --storePassword=password --alias=alias_name --password=password --packageType=bundle.

使用 build.json

或者,您可以在建置設定檔 (build.json) 中指定簽署參數。

預設情況下,如果 build.json 檔案存在於專案的根目錄中,則會自動偵測並使用它。如果檔案不在專案的根目錄中或有多個設定檔,則必須使用檔案的路徑提供命令列引數 --buildConfig

範例 build.json 設定檔

{
    "android": {
        "debug": {
            "keystore": "../android.keystore",
            "storePassword": "android",
            "alias": "mykey1",
            "password" : "password",
            "keystoreType": "",
            "packageType": "apk"
        },
        "release": {
            "keystore": "../android.keystore",
            "storePassword": "",
            "alias": "mykey2",
            "password" : "password",
            "keystoreType": "",
            "packageType": "bundle"
        }
    }
}

也支援混合搭配命令列引數和 build.json 中的參數。命令列引數中的值優先。這對於在命令列上指定密碼很有用。

使用 Gradle

您也可以透過包含 .properties 檔案並使用 cdvReleaseSigningPropertiesFilecdvDebugSigningPropertiesFile Gradle 屬性 (請參閱設定 Gradle 屬性) 指向該檔案來指定簽署屬性。

範例檔案內容

storeFile=relative/path/to/keystore.p12
storePassword=SECRET1
storeType=pkcs12
keyAlias=DebugSigningKey
keyPassword=SECRET2

自動簽署需要 storePasswordkeyPassword 屬性。

偵錯

有關 Android SDK 中隨附的偵錯工具的詳細資訊,請參閱 Android 關於偵錯的開發人員文件。此外,Android 關於偵錯 Web 應用程式的開發人員文件提供了關於偵錯在 Webview 中執行的應用程式部分的介紹。

在 Android Studio 中開啟專案

可以在 Android Studio 中開啟 Cordova-Android 專案。如果您希望使用 Android Studio 內建的 Android 偵錯和分析工具,或者您正在開發 Android 外掛程式,這會很有用。

注意:在 Android Studio 中開啟專案時,建議不要在 IDE 中編輯程式碼。在 Android Studio 中編輯會編輯位於您專案的 platforms 目錄中的程式碼。它不會更新專案根目錄 www) 目錄中的程式碼。這些變更可能會被覆寫。相反地,請編輯 www 資料夾,然後透過執行 cordova prepare 來複製您的變更。

希望在 Android Studio 中編輯其原生程式碼的外掛程式開發人員,應該在使用 cordova plugin add 將其外掛程式新增至專案時使用 --link 旗標。這會建立從外掛程式原始碼目錄到專案 platforms 目錄的外掛程式檔案的符號連結。

若要在 Android Studio 中開啟 Cordova-Android 專案

  1. 啟動 Android Studio
  2. 按一下 開啟 按鈕 歡迎使用 Android Studio
  3. 導覽至專案的 Android 平台目錄:(<project-root>/platforms/android)
  4. 按一下 開啟

一旦匯入完成,您應該可以直接從 Android Studio 建置並執行應用程式。

如需更多資源,請參閱

Hello Cordova MainActivity

升級

請參閱這篇文章,以了解升級 cordova-android 版本的說明。

生命週期指南

Cordova 和 Android

原生 Android 應用程式通常由一系列使用者互動的 活動 (activities) 所組成。活動可以被視為組成應用程式的個別畫面;應用程式中不同的任務通常會有各自的活動。每個活動都有其自身的生命週期,該生命週期會隨著活動進入和離開使用者裝置的前景而維護。

相反地,Android 平台上的 Cordova 應用程式是在嵌入於單一 Android 活動中的 Webview 內執行。此活動的生命週期會透過觸發的文件事件暴露給您的應用程式。這些事件不保證與 Android 的生命週期一致,但它們可以為您提供儲存和還原狀態的指南。這些事件大致上對應到 Android 的回呼,如下所示

Cordova 事件 大致對應的 Android 事件 意義
deviceready onCreate() 應用程式正在啟動 (非從背景)
pause onPause() 應用程式正在移至背景
resume onResume() 應用程式正在返回前景

大多數其他 Cordova 平台都有類似的生命週期概念,並且在使用者裝置上發生類似的操作時,應該會觸發相同的事件。然而,由於原生活動生命週期,Android 呈現出一些獨特的挑戰,有時會顯現出來。

Android 的獨特之處為何?

在 Android 中,如果裝置記憶體不足,作業系統可以選擇終止背景中的活動,以釋放資源。不幸的是,當持有您應用程式的活動被終止時,您的應用程式所在的 Webview 也會被摧毀。在這種情況下,您的應用程式正在維護的任何狀態都會遺失。當使用者導航回您的應用程式時,活動和 Webview 將由作業系統重新建立,但您的 Cordova 應用程式的狀態不會自動還原。因此,您的應用程式務必了解所觸發的生命週期事件,並維護任何適當的狀態,以確保使用者離開應用程式時,在您的應用程式中的上下文不會遺失。

什麼時候會發生這種情況?

當您的應用程式離開使用者視線時,隨時都有可能被作業系統摧毀。主要有兩種情況會發生這種情況。第一種也是最明顯的情況是使用者按下首頁按鈕或切換到另一個應用程式時。

然而,某些外掛程式可能會引入第二種 (而且更微妙) 的情況。如上所述,Cordova 應用程式通常僅限於包含 Webview 的單一活動。然而,在某些情況下,外掛程式可能會啟動其他活動,並暫時將 Cordova 活動推至背景。啟動這些其他活動通常是為了使用裝置上安裝的原生應用程式執行特定任務。例如,Cordova 相機外掛程式會啟動裝置上安裝的任何原生相機活動來拍照。當使用者嘗試拍照時,以這種方式重複使用已安裝的相機應用程式會讓您的應用程式感覺更像原生應用程式。不幸的是,當原生活動將您的應用程式推至背景時,作業系統有可能會終止它。

為了更清楚地了解第二種情況,讓我們以相機外掛程式為例。假設您的應用程式需要使用者拍攝個人資料照片。當一切按計畫進行時,應用程式中的事件流程會如下所示

  1. 使用者正在與您的應用程式互動,並且需要拍照
  2. 相機外掛程式會啟動原生相機活動
    • Cordova 活動會被推至背景 (觸發 pause 事件)
  3. 使用者拍照
  4. 相機活動完成
    • Cordova 活動移至前景 (觸發 resume 事件)
  5. 使用者返回到他們離開應用程式的位置

然而,如果裝置記憶體不足,此事件流程可能會中斷。如果活動被作業系統終止,上述事件順序將會改為如下

  1. 使用者正在與您的應用程式互動,並且需要拍照
  2. 相機外掛程式會啟動原生相機活動
    • 作業系統終止 Cordova 活動 (觸發 pause 事件)
  3. 使用者拍照
  4. 相機活動完成
    • 作業系統重新建立 Cordova 活動 (觸發 deviceready 和 resume 事件)
  5. 使用者會感到困惑,不明白為何突然回到應用程式的登入畫面

在此情況下,作業系統在背景中終止了應用程式,且應用程式沒有在生命週期中維護其狀態。當使用者返回應用程式時,Webview 會被重新建立,且應用程式似乎從頭開始重新啟動 (因此使用者感到困惑)。此事件順序等同於按下首頁按鈕或使用者切換應用程式時發生的情況。防止上述體驗的關鍵是訂閱事件,並在活動生命週期中正確維護狀態。

尊重生命週期

在上述範例中,觸發的 javascript 事件以斜體標示。這些事件是您儲存和還原應用程式狀態的機會。您應該在應用程式的 bindEvents 函數中註冊回呼,以透過儲存狀態來回應生命週期事件。您儲存哪些資訊以及如何儲存則由您自行決定,但您應該確保儲存足夠的資訊,以便在使用者返回應用程式時,將使用者還原到他們離開的位置。

在上述範例中,還有一個額外的因素僅適用於第二種討論的情況 (亦即當外掛程式啟動外部活動時)。不僅在使用者完成拍照後應用程式的狀態遺失,使用者拍攝的照片也遺失了。通常,該照片會透過註冊到相機外掛程式的回呼傳遞到您的應用程式。然而,當 Webview 被摧毀時,該回呼就會永遠遺失。幸好,cordova-android 5.1.0 及更高版本提供了一種在您的應用程式恢復時取得該外掛程式呼叫結果的方式。

擷取外掛程式回呼結果 (cordova-android 5.1.0+)

當作業系統終止被外掛程式推至背景的 Cordova 活動時,任何擱置的回呼也會遺失。這表示如果您將回呼傳遞給啟動新活動的外掛程式 (例如相機外掛程式),則當應用程式重新建立時,將不會觸發該回呼。然而,從 cordova-android 5.1.0 開始,resume 事件的 payload 將包含在活動被終止之前從啟動外部活動的外掛程式要求傳回的任何擱置外掛程式結果。

resume 事件的 payload 會遵循以下格式

{
    action: "resume",
    pendingResult: {
        pluginServiceName: string,
        pluginStatus: string,
        result: any
    }
}

該 payload 的欄位定義如下

  • pluginServiceName:傳回結果的外掛程式名稱 (例如「Camera」)。這可以在外掛程式的 plugin.xml 檔案的 <name> 標籤中找到
  • pluginStatus:外掛程式呼叫的狀態 (請參閱下方)
  • result:外掛程式呼叫的任何結果

pendingResult 欄位中的 pluginStatus 可能的值包括以下

  • "OK" - 外掛程式呼叫成功
  • "No Result" - 外掛程式呼叫結束時沒有結果
  • "Error" - 外掛程式呼叫導致某些一般錯誤
  • 其他雜項錯誤
    • "找不到類別"
    • "非法存取"
    • "執行個體化錯誤"
    • "格式錯誤的 URL"
    • "IO 錯誤"
    • "無效動作"
    • "JSON 錯誤"

注意:由外掛程式決定 result 欄位中包含的內容,以及傳回的 pluginStatus 的意義。請參閱外掛程式的 API 文件,以了解預期的結果以及如何使用這些值。

範例

以下是一個簡短的範例應用程式,使用 resumepause 事件來管理狀態。它使用 Apache 相機外掛程式作為如何從 resume 事件 payload 擷取外掛程式呼叫結果的範例。處理 resumeevent.pendingResult 物件的程式碼部分需要 cordova-android 5.1.0+

// This state represents the state of our application and will be saved and
// restored by onResume() and onPause()
var appState = {
    takingPicture: true,
    imageUri: ""
};

var APP_STORAGE_KEY = "exampleAppState";

var app = {
    initialize: function() {
        this.bindEvents();
    },
    bindEvents: function() {
        // Here we register our callbacks for the lifecycle events we care about
        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() {
            // Because the camera plugin method launches an external Activity,
            // there is a chance that our application will be killed before the
            // success or failure callbacks are called. See onPause() and
            // onResume() where we save and restore our state to handle this case
            appState.takingPicture = true;

            navigator.camera.getPicture(cameraSuccessCallback, cameraFailureCallback,
                {
                    sourceType: Camera.PictureSourceType.CAMERA,
                    destinationType: Camera.DestinationType.FILE_URI,
                    targetWidth: 250,
                    targetHeight: 250
                }
            );
        });
    },
    onPause: function() {
        // Here, we check to see if we are in the middle of taking a picture. If
        // so, we want to save our state so that we can properly retrieve the
        // plugin result in onResume(). We also save if we have already fetched
        // an image URI
        if(appState.takingPicture || appState.imageUri) {
            window.localStorage.setItem(APP_STORAGE_KEY, JSON.stringify(appState));
        }
    },
    onResume: function(event) {
        // Here we check for stored state and restore it if necessary. In your
        // application, it's up to you to keep track of where any pending plugin
        // results are coming from (i.e. what part of your code made the call)
        // and what arguments you provided to the plugin if relevant
        var storedState = window.localStorage.getItem(APP_STORAGE_KEY);

        if(storedState) {
            appState = JSON.parse(storedState);
        }

        // Check to see if we need to restore an image we took
        if(!appState.takingPicture && appState.imageUri) {
            document.getElementById("get-picture-result").src = appState.imageUri;
        }
        // Now we can check if there is a plugin result in the event object.
        // This requires cordova-android 5.1.0+
        else if(appState.takingPicture && event.pendingResult) {
            // Figure out whether or not the plugin call was successful and call
            // the relevant callback. For the camera plugin, "OK" means a
            // successful result and all other statuses mean error
            if(event.pendingResult.pluginStatus === "OK") {
                // The camera plugin places the same result in the resume object
                // as it passes to the success callback passed to getPicture(),
                // thus we can pass it to the same callback. Other plugins may
                // return something else. Consult the documentation for
                // whatever plugin you are using to learn how to interpret the
                // result field
                cameraSuccessCallback(event.pendingResult.result);
            } else {
                cameraFailureCallback(event.pendingResult.result);
            }
        }
    }
}

// Here are the callbacks we pass to 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 特性

Cordova-Android 平台中的預設 API 等級已升級。在 Android 9 裝置上,預設會停用明文通訊。

依預設,HTTP 和 FTP 等會拒絕應用程式使用明文流量的要求。避免明文流量的主要原因是缺乏機密性、真實性和防止篡改的保護;網路攻擊者可以竊聽傳輸的資料,也可以在不被偵測到的情況下修改資料。您可以在Android 開發人員文件中了解更多有關 android:usesCleartextTraffic 或任何其他 Android 應用程式元素設定的資訊。

若要再次允許明文通訊,請在 config.xml 檔案中將應用程式標籤上的 android:usesCleartextTraffic 設定為 true

<platform name="android">
  <edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
      <application android:usesCleartextTraffic="true" />
  </edit-config>
</platform>

而且您還需要在同一個 config.xml 中將 Android XML 名稱空間 xmlns:android="http://schemas.android.com/apk/res/android" 新增至您的 widget 標籤。

範例

<widget id="io.cordova.hellocordova" version="0.0.1" android-versionCode="13" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="https://cordova.dev.org.tw/ns/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
</widget>

Android 清單資訊

您可以在Android 開發人員文件中了解更多有關 Android 清單資訊的資訊。

測試活動生命週期

Android 提供開發人員設定來測試記憶體不足時的活動摧毀。在您的裝置或模擬器的開發人員選項選單中啟用「不保留活動」設定,以模擬記憶體不足的情況。您應該始終在啟用此設定的情況下進行一些測試,以確保您的應用程式正確維護狀態。