アプリ内アップデートをサポートする(ネイティブ)

このガイドでは、ネイティブ コード(C または C++)を使用してアプリ内でアプリ内アップデートをサポートする方法について説明します。実装に Kotlin プログラミング言語または Java プログラミング言語を使用する場合と、Unity を使用する場合については、個別のガイドで説明しています。

ネイティブ SDK の概要

Play Core Native SDK は Play Core SDK ファミリーに含まれています。Native SDK には、Java Play In-App Update Library の AppUpdateManager をラップする C ヘッダー ファイル app_update.h が含まれています。このヘッダー ファイルを使用すると、ネイティブ コードから直接アプリ内アップデート用の API を呼び出すことができます。

開発環境をセットアップする

Play Core Native SDKのダウンロード

ダウンロードする前に、次の利用規約に同意する必要があります。

利用規約

最終更新日: 2020 年 9 月 24 日
  1. Play Core ソフトウェア開発キットを使用することにより、Google API 利用規約(「API 利用規約」)に加えて、本規約に同意したことになります。各規約の間に矛盾がある場合は、本規約が API 利用規約よりも優先されます。本規約と API 利用規約をよくお読みください。
  2. 本規約において、「API」とは Google の API、その他のデベロッパー向けサービス、および関連するソフトウェア(あらゆる再配布可能コードを含む)を意味します。
  3. 「再配布可能コード」とは、API を呼び出す Google 提供のオブジェクト コードまたはヘッダー ファイルを指します。
  4. 本規約および API 利用規約の規定に従い、API クライアントの一部として含める形でのみ、再配布可能コードをコピーして配布することができます。Google とそのライセンサーは、再配布可能コードとそれに含まれる一切の権利、権原、および利益(すべての知的財産権とその他の所有権を含む)を保有します。デベロッパーは、再配布可能コードの修正、翻訳、派生物の作成をしてはなりません。
  5. Google は、通知を行い、Play Core ソフトウェア開発キットの使用を拒否する機会を提供したうえで、いつでも本規約を変更することができます。Google は、本規約の変更の通知を https://developer.android.com/guide/playcore/license に掲載します。変更が遡って適用されることはありません。

ダウンロードする: Play Core Native SDK

ダウンロードする: Play Core Native SDK

play-core-native-sdk-1.11.0.zip

  1. 次のいずれかの操作を行います。

    • Android Studio バージョン 4.0 以降をインストールし、SDK Manager UI を使用して、Android SDK Platform バージョン 10.0(API レベル 29)をインストールします。
    • Android SDK コマンドライン ツールをインストールし、sdkmanager を使用して Android SDK Platform バージョン 10.0(API レベル 29)をインストールする。
  2. Android Studio をネイティブ開発で使用できるようにするため、SDK Manager を使用して最新の CMake と Android Native Development Kit(NDK)をインストールします。ネイティブ プロジェクトの作成やインポートの詳細については、NDK のスタートガイドをご覧ください。

  3. zip ファイルをダウンロードして、プロジェクトと同じ場所に展開します。

    ダウンロード リンク サイズ SHA-256 チェックサム
    55.6 MB 058b4069f09714da938656d43b6dc28d3bc6f821c9d406e9c96a1c3af014dc45
  4. アプリの build.gradle ファイルを以下のように更新します。

    Groovy

        // App build.gradle
    
        plugins {
          id 'com.android.application'
        }
    
        // Define a path to the extracted Play Core SDK files.
        // If using a relative path, wrap it with file() since CMake requires absolute paths.
        def playcoreDir = file('../path/to/playcore-native-sdk')
    
        android {
            defaultConfig {
                ...
                externalNativeBuild {
                    cmake {
                        // Define the PLAYCORE_LOCATION directive.
                        arguments "-DANDROID_STL=c++_static",
                                  "-DPLAYCORE_LOCATION=$playcoreDir"
                    }
                }
                ndk {
                    // Skip deprecated ABIs. Only required when using NDK 16 or earlier.
                    abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
                }
            }
            buildTypes {
                release {
                    // Include Play Core Library proguard config files to strip unused code while retaining the Java symbols needed for JNI.
                    proguardFile "$playcoreDir/proguard/common.pgcfg"
                    proguardFile "$playcoreDir/proguard/per-feature-proguard-files"
                    ...
                }
                debug {
                    ...
                }
            }
            externalNativeBuild {
                cmake {
                    path 'src/main/CMakeLists.txt'
                }
            }
        }
    
        dependencies {
            // Use the Play Core AAR included with the SDK.
            implementation files("$playcoreDir/playcore.aar")
    
            // Use the following dependency for the Play Integrity API.
            implementation("com.google.android.play:integrity:1.0.0")
            ...
        }
        

    Kotlin

    // App build.gradle
    
    plugins {
        id("com.android.application")
    }
    
    // Define a path to the extracted Play Core SDK files.
    // If using a relative path, wrap it with file() since CMake requires absolute paths.
    val playcoreDir = file("../path/to/playcore-native-sdk")
    
    android {
        defaultConfig {
            ...
            externalNativeBuild {
                cmake {
                    // Define the PLAYCORE_LOCATION directive.
                    arguments += listOf("-DANDROID_STL=c++_static", "-DPLAYCORE_LOCATION=$playcoreDir")
                }
            }
            ndk {
                // Skip deprecated ABIs. Only required when using NDK 16 or earlier.
                abiFilters.clear()
                abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
            }
        }
        buildTypes {
            release {
                // Include Play Core Library proguard config files to strip unused code while retaining the Java symbols needed for JNI.
                proguardFile("$playcoreDir/proguard/common.pgcfg")
                proguardFile("$playcoreDir/proguard/per-feature-proguard-files")
                ...
            }
            debug {
                ...
            }
        }
        externalNativeBuild {
            cmake {
                path = "src/main/CMakeLists.txt"
            }
        }
    }
    
    dependencies {
        // Use the Play Core AAR included with the SDK.
        implementation(files("$playcoreDir/playcore.aar"))
        ...
    }
    
  5. アプリの CMakeLists.txt ファイルを以下のように更新します。

    cmake_minimum_required(VERSION 3.6)
    
    ...
    
    # Add a static library called “playcore” built with the c++_static STL.
    include(${PLAYCORE_LOCATION}/playcore.cmake)
    add_playcore_static_library()
    
    // In this example “main” is your native code library, i.e. libmain.so.
    add_library(main SHARED
            ...)
    
    target_include_directories(main PRIVATE
            ${PLAYCORE_LOCATION}/include
            ...)
    
    target_link_libraries(main
            android
            playcore
            ...)
    

Play Core Native SDK をプロジェクトに統合したら、API 呼び出しを含むファイルに次の行を含めます。

#include "play/app_update.h"

アプリ内アップデートの API を初期化する

アプリ内アップデートの API を使用するときは、まず AppUpdateManager_init() を呼び出して初期化する必要があります。android_native_app_glue.h を使用してビルドされた次の例をご覧ください。

void android_main(android_app* app) {
  app->onInputEvent = HandleInputEvent;

  AppUpdateErrorCode error_code =
    AppUpdateManager_init(app->activity->vm, app->activity->clazz);
  if (error_code == APP_UPDATE_NO_ERROR) {
    // You can use the API.
  }
}

アップデートの有無の確認

アップデートをリクエストする前に、アプリに利用できるアップデートがあるかどうかを確認します。AppUpdateManager_requestInfo() により、後でアプリ内レビューフローを起動するために必要な情報を収集する非同期リクエストが開始されます。リクエストが正常に開始すると、関数は APP_UPDATE_NO_ERROR を返します。

AppUpdateErrorCode error_code = AppUpdateManager_requestInfo()

if (error_code == APP_UPDATE_NO_ERROR) {
    // The request has successfully started, check the result using
    // AppUpdateManager_getInfo.
}

AppUpdateManager_getInfo() を使用すると、リクエストの継続的なプロセスと結果を追跡できます。エラーコードに加えて、この関数は AppUpdateInfo 不透明構造体を返します。これを使用して、更新リクエストに関する情報を取得できます。たとえば、info に対して null 以外の結果が返されるまで、すべてのゲームループでこの関数を呼び出すことができます。

AppUpdateInfo* info;
GameUpdate() {

   // Keep calling this in every game loop until info != nullptr
   AppUpdateErrorCode error_code = AppUpdateManager_getInfo(&info);

   if (error_code == APP_UPDATE_NO_ERROR && info != nullptr) {
       // Successfully started, check the result in the following functions
   }
...
}

アップデートの新しさの確認

アップデートが適用可能であるかどうかを確認するだけでなく、ユーザーが Google Play ストアからアップデートの通知を受信してから経過した期間を確認することもできます。これは、フレキシブル アップデートと即時アップデートのどちらを開始したらよいかを判断する際に有用です。たとえば、ユーザーにフレキシブル アップデートを通知するまで数日待ち、その後、即時アップデートを要求するまでさらに数日待つこともできます。

AppUpdateInfo_getClientVersionStalenessDays() を使用して、Play ストアからのアップデートの入手が可能になった時点から経過した日数を確認します。

int32_t staleness_days = AppUpdateInfo_getClientVersionStalenessDays(info);

アップデートの優先度を確認する

Google Play Developer API では、各アップデートの優先度を設定できます。 これにより、アプリはユーザーにアップデートを推奨する程度を決定できるようになります。たとえば、アップデートの優先度を設定するための次のような戦略を考えてみます。

  • UI の細かな改善: 優先度が低いアップデート。フレキシブル アップデートも即時アップデートもリクエストしません。ユーザーがアプリを操作していないときにのみ更新します。
  • パフォーマンスの改善: 優先度が中程度のアップデート。フレキシブル アップデートをリクエストします。
  • 重要なセキュリティ アップデート: 優先度が高いアップデート。即時アップデートをリクエストします。

優先度を指定するため、Google Play は 0~5 の整数値を使用します。0 はデフォルトの値、5 は優先度が最も高い値です。アップデートの優先度を設定するには、Google Play Developer API の Edits.tracks.releases の下にある inAppUpdatePriority フィールドを使用します。リリースで新たに追加されたすべてのバージョンの優先度は、リリースと同じと見なされます。優先度は新しいリリースの公開時にのみ設定できます。後から変更することはできません。

Google Play Developer API を使用した優先度の設定については、Play Developer API のドキュメントをご覧ください。Edit.tracks: update メソッドで渡される Edit.tracks リソースでアプリ内アップデートの優先度を指定します。次の例に、アプリ(バージョン コード 88、inAppUpdatePriority 5)のリリースを示します。

{
  "releases": [{
      "versionCodes": ["88"],
      "inAppUpdatePriority": 5,
      "status": "completed"
  }]
}

アプリのコードで、AppUpdateInfo_getPriority() を使用して特定のアップデートの優先度を確認できます。

int32_t priority = AppUpdateInfo_getPriority(info);

アップデートの開始

アップデートが利用可能であることを確認したら、AppUpdateManager_requestStartUpdate() を使用してアップデートをリクエストできます。アップデートをリクエストする前に、最新の AppUpdateInfo オブジェクトを取得し、AppUpdateOptions オブジェクトを作成して、アップデート フローを構成します。AppUpdateOptions オブジェクトでは、アプリ内アップデート フローのオプション(アップデートをフレキシブルと即時のどちらにするかを含む)を定義します。

次の例では、フレキシブル アップデート フロー用の AppUpdateOptions オブジェクトを作成します。

// Creates an AppUpdateOptions configuring a flexible in-app update flow.
AppUpdateOptions* options;
AppUpdateErrorCode error_code = AppUpdateOptions_createOptions(APP_UPDATE_TYPE_FLEXIBLE, &options);

次の例では、即時アップデート フロー用の AppUpdateOptions オブジェクトを作成します。

// Creates an AppUpdateOptions configuring an immediate in-app update flow.
AppUpdateOptions* options;
AppUpdateErrorCode error_code = AppUpdateOptions_createOptions(APP_UPDATE_TYPE_IMMEDIATE, &options);

また、AppUpdateOptions オブジェクトには AllowAssetPackDeletion フィールドも含まれており、デバイスのストレージが限られている場合に、アップデートでアセットパックの消去を許可するかどうかを定義します。このフィールドはデフォルトでは false に設定されていますが、代わりに AppUpdateOptions_setAssetPackDeletionAllowed() メソッドを使用して true に設定することもできます。

bool allow = true;
AppUpdateErrorCode error_code = AppUpdateOptions_setAssetPackDeletionAllowed(options, allow);

最新の AppUpdateInfo オブジェクトと、適切に構成された AppUpdateOptions オブジェクトを取得後、AppUpdateManager_requestStartUpdate() を呼び出してアップデート フローを非同期リクエストし、最後のパラメータの Android のアクティビティ jobject で渡します。

AppUpdateErrorCode request_error_code =
AppUpdateManager_requestStartUpdate(info, options, app->activity->clazz);

リソースを解放するには、不要になった AppUpdateInfoAppUpdateOptions のインスタンスを、AppUpdateInfo_destroy()AppUpdateOptions_destroy() をそれぞれ呼び出すことでリリースします。

AppUpdateInfo_destroy(info);
AppUpdateOptions_destroy(options);

即時アップデート フローの場合、Google Play にはユーザーの確認ページが表示されます。ユーザーがリクエストを承認すると、Google Play はフォアグラウンドでアップデートのダウンロードとインストールを自動的に行い、インストールが成功するとアプリを再起動して更新したバージョンにします。

フレキシブル アップデート フローでは、ユーザーがアプリの使用を続けながら、最新の AppUpdateInfo オブジェクトのリクエストを継続し、現在の更新ステータスを追跡できます。ダウンロードが正常に完了したら、次の例に示すように、AppUpdateManager_requestCompleteUpdate() を呼び出してアップデートの完了をトリガーする必要があります。

AppUpdateStatus status = AppUpdateInfo_getStatus(info);
if (status == APP_UPDATE_DOWNLOADED) {
    AppUpdateErrorCode error_code = AppUpdateManager_requestCompleteUpdate();
    if (error_code != APP_UPDATE_NO_ERROR)
    {
      // There was an error while completing the update flow.
    }
}

アプリで API の使用が終了したら、AppUpdateManager_destroy() 関数を呼び出してリソースを解放します。

エラー処理

このセクションでは、特定の AppUpdateErrorCode 値で示される一般的なエラーの解決策について説明します。

  • エラーコード -110, APP_UPDATE_INITIALIZATION_NEEDED は、API が正常に初期化されていないことを示します。AppUpdateManager_init() を呼び出して API を初期化します。
  • エラーコード -4, APP_UPDATE_INVALID_REQUEST は、アップデート フロー リクエストの一部のパラメータの形式が正しくないことを示します。AppUpdateInfo オブジェクトと AppUpdateOptions オブジェクトが null ではなく、正しくフォーマットされていることを確認してください。
  • エラーコード -5, APP_UPDATE_UNAVAILABLE は、適用可能なアップデートがないことを示します。対象のバージョンに同じパッケージ名アプリケーション ID署名鍵があることを確認します。利用可能なアップデートがある場合は、アプリのキャッシュを消去し、もう一度 AppUpdateManager_requestAppUpdateInfo() を呼び出して AppUpdateInfo を更新します。
  • エラーコード -6, APP_UPDATE_NOT_ALLOWED は、AppUpdateOption オブジェクトによって指定されたアップデート タイプが許可されていないことを示します。アップデート フローを開始する前に、AppUpdateInfo オブジェクトでアップデート タイプが許可されているかどうかを確認してください。

次のステップ

アプリ内アップデートをテストして、統合が正しく機能していることを確認する。