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 Compat ライブラリ: 1.6.1
  • AndroidX WebKit ライブラリ: 1.6.0
  • AndroidX Core SplashScreen: 1.0.0
  • Google Services 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 Compat ライブラリ: 1.4.2
  • AndroidX WebKit ライブラリ: 1.4.0
  • AndroidX Core SplashScreen: 1.0.0-rc01
  • Google Services 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 Compat ライブラリ: 1.3.1
  • AndroidX WebKit ライブラリ: 1.4.0
  • Google Services 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 Compat ライブラリ: 1.3.0
  • AndroidX WebKit ライブラリ: 1.4.0
  • Google Services 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 バージョンのサポートを généralement 停止します。詳細は、Google の 配布ダッシュボード を参照してください。

システム要件

Cordova-Android は Android SDK に依存しており、macOS、Linux、または Windows オペレーティングシステムにインストールできます。

システムが必要な要件を満たしていることを確認するには、Google が提供する "Android Studio のインストール" ガイドを参照してください。

必要なソフトウェアとツール

Java Development Kit (JDK)

cordova-android 10.0.0 以降を使用している場合は、Java Development Kit (JDK) 11 をインストールします。

cordova-android 10.0.0 より前のバージョンを使用している場合は、Java Development Kit (JDK) 8 をインストールします。

JAVA_HOME 環境変数は、JDK のインストールパスに従って設定する必要があります。環境変数の設定方法については、環境変数の設定 セクションを参照してください。あるいは、cordova-android 10.0.0 以降では、JAVA_HOME の代わりに CORDOVA_JAVA_HOME を設定することで、Cordova 開発専用に JDK インストールを使用できます。

Gradle

Cordova-Android 6.4.0 以降、Gradle のインストールが必要です。

Windows にインストールする場合、Gradle のバイナリディレクトリへのパスを path 環境変数に追加する必要があります。システム環境変数の設定方法については、環境変数の設定 を参照してください。

注: これはシステムの Gradle バージョンです。システムの Gradle バイナリは、Android アプリケーションのビルドに必要な適切なバージョンの Gradle を宣言して取得する Gradle Wrapper ファイルを作成します。システムレベルとプロジェクトレベルの Gradle のバージョンは一致しない場合があり、一致する必要はありません。プロジェクトレベルの Gradle のバージョンは Cordova-Android のパッケージで定義され、Android がサポートするものに基づいて設定されます。

Android Studio

Android Studio をダウンロードしてインストールします。リンク先の Android デベロッパーサイトの手順に従って開始してください。

Android Studio を初めて開くと、Android SDK パッケージのインストールプロセスが案内されます。

SDK パッケージ

プロジェクトにインストールされている Cordova-Android のバージョンに基づいて、最新バージョンの SDK プラットフォームと SDK ツールをインストールすることをお勧めします。Cordova-Android のバージョンに基づいてサポートされているバージョンを確認するには、Android API レベルのサポート セクションを参照してください。

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
    • これは、apksigner および zipalign ツールに必要です。

注: 上記のディレクトリは、通常 Android SDK のルートにあります。

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. OK ボタンをクリックします
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

すべてのパスを追加したら、環境変数の設定と編集のために開いているすべてのウィンドウが閉じるまで、OK ボタンをクリックします。

プロジェクトの設定

エミュレーターのセットアップ

Cordova アプリを Android エミュレーターで実行する場合は、最初に 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 ファイルが生成されます。ライブラリプロジェクトでサポートされているネイティブプラットフォームごとに 1 つ (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 のみがビルドされる場合に、バージョンコードに 0 の「abi ディジット」を追加するかどうか。

これらのプロパティは、次の 4 つの方法のいずれかで設定できます。

  • 環境変数を使用する

      export ORG_GRADLE_PROJECT_cdvMinSdkVersion=20
      cordova build android
    
  • Cordova の build または run コマンドで --gradleArg フラグを使用する

      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
    

後者の 2 つのオプションはどちらも、Android プラットフォームフォルダに追加のファイルを含めることを伴います。一般的に、このフォルダの内容を編集することはお勧めしません。これらの変更が失われたり、上書きされたりする可能性があるためです。代わりに、before_build フックスクリプトを使用して、これらのファイルをフォルダにコピーする必要があります。

build.gradle の拡張

build.gradle ファイルを直接編集するのではなくカスタマイズする必要がある場合は、build-extras.gradle という名前の兄弟ファイルを作成することをお勧めします。このファイルは、存在する場合、メインの build.gradle スクリプトに含まれます。このファイルは、Android のプラットフォームディレクトリ (<your-project>/platforms/android/app) の app フォルダに配置する必要があります。before_build フックスクリプトを使用して、このファイルをコピーすることをお勧めします。

例を次に示します。

// 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 引数を変更するには、Cordova の build および run コマンドで --jvmargs フラグを使用できます。これは、ビルドプロセス中に 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 プロパティの設定」を参照)、apk がビルドされたアーキテクチャを示すためにコードの最後の桁を使用できるように、アプリのバージョンコードも 10 倍になります。この乗算は、バージョンコードが 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、リリースの場合はバンドル
APK をビルドするか、AAB (Android App Bundle) ファイルをビルドするかを指定します。
許容値: apk または bundle

Cordova CLIbuild または run コマンドを使用する場合、上記のパラメータを引数として指定できます。

**注**: これらがプラットフォーム固有の引数であることを示すには、二重の -- を使用する必要があります。

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 ファイルを含めて、cdvReleaseSigningPropertiesFile および cdvDebugSigningPropertiesFile Gradle プロパティでそれを指すことによって、署名プロパティを指定することもできます (「Gradle プロパティの設定」を参照)。

ファイル内容の例

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

自動署名には、storePassword および keyPassword プロパティが必要です。

デバッグ

Android SDK に付属のデバッグツールについては、Android のデバッグに関する開発者向けドキュメントを参照してください。さらに、Android のWeb アプリのデバッグに関する開発者向けドキュメントでは、Webview で実行されているアプリの部分のデバッグについて紹介しています。

Android Studio でプロジェクトを開く

Cordova-Android プロジェクトは、Android Studio で開くことができます。これは、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 アプリは通常、ユーザーが操作する一連のアクティビティで構成されます。アクティビティは、アプリケーションを構成する個々の画面と考えることができます。アプリの異なるタスクには、多くの場合、独自のアクティビティがあります。各アクティビティには、アクティビティがユーザーのデバイスのフォアグラウンドに出入りするときに維持される独自のライフサイクルがあります。

対照的に、Android プラットフォーム上の Cordova アプリケーションは、*単一の* Android アクティビティに埋め込まれた Webview 内で実行されます。このアクティビティのライフサイクルは、発生するドキュメントイベントを通じてアプリケーションに公開されます。イベントは Android のライフサイクルと一致することが保証されていませんが、状態の保存と復元のためのガイドラインを提供できます。これらのイベントは、おおよそ次のように Android コールバックにマップされます。

Cordova イベント おおよその Android の同等物 意味
deviceready onCreate() アプリケーションが起動しています (バックグラウンドからではありません)
pause onPause() アプリケーションはバックグラウンドに移行しています
再開 onResume() アプリケーションはフォアグラウンドに戻っています

他のほとんどのCordovaプラットフォームも同様のライフサイクルの概念を持ち、ユーザーのデバイスで同様のアクションが発生したときに同じイベントを発生させます。ただし、Androidには、ネイティブのアクティビティライフサイクルによって、特有の課題が生じることがあります。

Androidの違いは何ですか?

Androidでは、デバイスのメモリが不足している場合、OSはリソースを解放するためにバックグラウンドでアクティビティを強制終了することがあります。残念ながら、アプリケーションを保持しているアクティビティが強制終了されると、アプリケーションが存在するWebViewも破棄されます。この場合、アプリケーションが維持している状態はすべて失われます。ユーザーがアプリケーションに戻ると、アクティビティとWebViewはOSによって再作成されますが、Cordovaアプリの状態は自動的に復元されません。このため、アプリケーションは発生するライフサイクルイベントを認識し、アプリケーションを離れたときにユーザーのコンテキストが失われないように、適切な状態を維持することが不可欠です。

これはいつ発生する可能性がありますか?

アプリケーションは、ユーザーの視界から外れると、OSによって破棄される可能性があります。これが発生する主な状況は2つあります。最初の、そして最も明白なケースは、ユーザーがホームボタンを押すか、別のアプリケーションに切り替える場合です。

ただし、特定のプラグインによって発生する可能性のある、2番目の(そしてはるかに微妙な)ケースがあります。上記のように、Cordovaアプリケーションは通常、WebViewを含む単一のアクティビティに限定されます。ただし、プラグインによって他のアクティビティが起動され、Cordovaアクティビティが一時的にバックグラウンドにプッシュされる場合があります。これらの他のアクティビティは通常、デバイスにインストールされているネイティブアプリケーションを使用して特定のタスクを実行するために起動されます。たとえば、Cordovaカメラプラグインは、写真を撮影するためにデバイスにネイティブにインストールされているカメラアクティビティを起動します。このようにインストールされているカメラアプリケーションを再利用することで、ユーザーが写真を撮ろうとしたときに、アプリケーションがネイティブアプリのように感じられます。残念ながら、ネイティブアクティビティがアプリをバックグラウンドにプッシュすると、OSがアプリを強制終了する可能性があります。

この2番目のケースをより明確に理解するために、カメラプラグインを使用した例を見てみましょう。ユーザーがプロフィール写真を撮影する必要があるアプリケーションがあるとします。すべてが計画どおりに進んだ場合のアプリケーションのイベントフローは次のようになります

  1. ユーザーはアプリを操作していて、写真を撮る必要があります
  2. カメラプラグインはネイティブのカメラアクティビティを起動します
    • Cordovaアクティビティはバックグラウンドにプッシュされます(一時停止イベントが発生します)
  3. ユーザーは写真を撮ります
  4. カメラアクティビティが終了します
    • Cordovaアクティビティはフォアグラウンドに移動します(再開イベントが発生します)
  5. ユーザーは中断したところからアプリケーションに戻ります

ただし、デバイスのメモリが不足している場合、このイベントフローが中断される可能性があります。アクティビティがOSによって強制終了された場合、上記のイベントシーケンスは代わりに次のように実行されます

  1. ユーザーはアプリを操作していて、写真を撮る必要があります
  2. カメラプラグインはネイティブのカメラアクティビティを起動します
    • OSはCordovaアクティビティを破棄します(一時停止イベントが発生します)
  3. ユーザーは写真を撮ります
  4. カメラアクティビティが終了します
    • OSはCordovaアクティビティを再作成します(devicereadyイベントと再開イベントが発生します)
  5. ユーザーは、アプリのログイン画面に突然戻った理由がわかりません

このインスタンスでは、OSはバックグラウンドでアプリケーションを強制終了し、アプリケーションはライフサイクルの一部として状態を維持しませんでした。ユーザーがアプリに戻ると、WebViewが再作成され、アプリは最初から再起動されたように見えました(そのため、ユーザーは混乱しました)。このイベントシーケンスは、ホームボタンが押された場合、またはユーザーがアプリケーションを切り替えた場合に発生することと同じです。上記のエクスペリエンスを防ぐための鍵は、イベントをサブスクライブし、アクティビティライフサイクルの一部として状態を適切に維持することです。

ライフサイクルの尊重

上記の例では、発生するJavaScriptイベントはイタリック体で示されています。これらのイベントは、アプリケーションの状態を保存および復元する機会です。アプリケーションの`bindEvents`関数に、状態を保存することでライフサイクルイベントに応答するコールバックを登録する必要があります。どのような情報をどのように保存するかは、ユーザーの裁量に任されていますが、アプリケーションに戻ったときにユーザーを中断した場所に正確に復元できるように、十分な情報を保存する必要があります。

上記の例には、2番目に説明した状況(つまり、プラグインが外部アクティビティを起動する場合)にのみ適用される追加の要素が1つあります。ユーザーが写真の撮影を終了したときに、アプリケーションの状態が失われただけでなく、ユーザーが撮影した写真も失われました。通常、その写真は、カメラプラグインに登録されたコールバックを通じてアプリケーションに配信されます。ただし、WebViewが破棄されると、そのコールバックは永久に失われました。幸いなことに、cordova-android 5.1.0以降では、アプリケーションの再開時にそのプラグイン呼び出しの結果を取得する手段が提供されています。

プラグインコールバック結果の取得(cordova-android 5.1.0以降)

プラグインによってバックグラウンドにプッシュされたCordovaアクティビティがOSによって破棄されると、保留中のコールバックも失われます。これは、新しいアクティビティを起動したプラグイン(例:カメラプラグイン)にコールバックを渡した場合、アプリケーションが再作成されたときにそのコールバックが実行されないことを意味します。ただし、cordova-android **5.1.0**以降、`resume`イベントのペイロードには、アクティビティが破棄される前に行われた外部アクティビティを起動したプラグインリクエストからの保留中のプラグイン結果が含まれます。

`resume`イベントのペイロードは、次の形式に従います

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

そのペイロードのフィールドは次のように定義されています

  • `pluginServiceName`:結果を返すプラグインの名前(例:「Camera」)。これは、プラグインのplugin.xmlファイルの`<name>`タグにあります
  • `pluginStatus`:プラグイン呼び出しの状態(下記参照)
  • `result`:プラグイン呼び出しの結果

`pendingResult`フィールドの`pluginStatus`の可能な値には、次のものが含まれます

  • `"OK"` - プラグイン呼び出しは成功しました
  • `"No Result"` - プラグイン呼び出しは結果なしで終了しました
  • `"Error"` - プラグイン呼び出しで一般的なエラーが発生しました
  • その他のさまざまなエラー
    • "Class not found"
    • "Illegal access"
    • "Instantiation error"
    • "Malformed url"
    • "IO error"
    • "Invalid action"
    • "JSON error"

**注:** `result`フィールドに含まれる内容と、返される`pluginStatus`の意味は、プラグインによって異なります。期待される結果と値の使用方法については、プラグインのAPIドキュメントを参照してください。

以下は、`resume`イベントと`pause`イベントを使用して状態を管理する簡単なアプリケーション例です。 `resume`イベントペイロードからプラグイン呼び出しの結果を取得する方法の例として、Apacheカメラプラグインを使用しています。 `resume`の`event.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:usesCleartextTraffic`またはその他のAndroidアプリケーション要素の設定の詳細については、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 id="io.cordova.hellocordova" version="0.0.1" android-versionCode="13" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="https://cordova.dokyumento.jp/ns/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
</widget>

Androidマニフェスト情報

Androidマニフェスト情報の詳細については、Android開発者向けドキュメントを参照してください。

アクティビティライフサイクルのテスト

Androidは、低メモリでのアクティビティの破棄をテストするための開発者設定を提供しています。デバイスまたはエミュレーターの[開発者向けオプション]メニューで[アクティビティを保持しない]設定を有効にして、低メモリシナリオをシミュレートします。アプリケーションが状態を適切に維持していることを確認するために、この設定を有効にして常にテストを行う必要があります。