支援應用程式內更新 (原生)

本指南說明如何使用原生程式碼 (C 或 C++) 在您的應用程式中支援 應用程式內更新。我們針對您的導入使用 Kotlin 或 Java 程式設計語言 以及 Unity 的情況分別提供單獨指南。

原生 SDK 總覽

Play Core 原生 SDK 屬於 Play Core SDK 系列的一部分。原生 SDK 包含 C 標頭檔案 app_update.h,可納入 Java Play 應用程式內更新程式庫中的 AppUpdateManager。應用程式可利用這個標頭檔案,直接從原生程式碼呼叫 API,以進行應用程式內更新。

設定開發環境

下載 Play Core Native SDK

下載前,請先同意以下條款及細則。

條款及細則

Last modified: September 24, 2020
  1. By using the Play Core Software Development Kit, you agree to these terms in addition to the Google APIs Terms of Service ("API ToS"). If these terms are ever in conflict, these terms will take precedence over the API ToS. Please read these terms and the API ToS carefully.
  2. For purposes of these terms, "APIs" means Google's APIs, other developer services, and associated software, including any Redistributable Code.
  3. “Redistributable Code” means Google-provided object code or header files that call the APIs.
  4. Subject to these terms and the terms of the API ToS, you may copy and distribute Redistributable Code solely for inclusion as part of your API Client. Google and its licensors own all right, title and interest, including any and all intellectual property and other proprietary rights, in and to Redistributable Code. You will not modify, translate, or create derivative works of Redistributable Code.
  5. Google may make changes to these terms at any time with notice and the opportunity to decline further use of the Play Core Software Development Kit. Google will post notice of modifications to the terms at https://developer.android.com/guide/playcore/license. Changes will not be retroactive.
下載 Play Core Native SDK

play-core-native-sdk-1.15.2.zip

  1. 執行下列其中一項操作:

  2. 使用 SDK Manager 安裝最新的 CMake 和 Android Native Development Kit (NDK),讓 Android Studio 準備進行原生開發。如要進一步瞭解如何建立或匯入原生專案,請參閱 NDK 入門指南

  3. 下載 ZIP 檔案,然後將其連同專案一起解壓縮。

    下載連結 大小 SHA-256 總和檢查碼
    37.8 MiB 56c5408b5b84cd3c18f268395b2cc23d53152dc9b228e979cc81f8b7f121f73c
  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/gms_task.pgcfg'
                    proguardFile '$playcoreDir/proguard/per-feature-proguard-files'
                    ...
                }
                debug {
                    ...
                }
            }
            externalNativeBuild {
                cmake {
                    path 'src/main/CMakeLists.txt'
                }
            }
        }
    
        dependencies {
            // Import these feature-specific AARs for each Google Play Core library.
            implementation 'com.google.android.play:app-update:2.1.0'
            implementation 'com.google.android.play:asset-delivery:2.2.2'
            implementation 'com.google.android.play:integrity:1.4.0'
            implementation 'com.google.android.play:review:2.0.2'
    
            // Import these common dependencies.
            implementation 'com.google.android.gms:play-services-tasks:18.0.2'
            implementation files("$playcoreDir/playcore-native-metadata.jar")
            ...
        }
        

    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/gms_task.pgcfg")
                proguardFile("$playcoreDir/proguard/per-feature-proguard-files")
                ...
            }
            debug {
                ...
            }
        }
        externalNativeBuild {
            cmake {
                path = "src/main/CMakeLists.txt"
            }
        }
    }
    
    dependencies {
        // Import these feature-specific AARs for each Google Play Core library.
        implementation("com.google.android.play:app-update:2.1.0")
        implementation("com.google.android.play:asset-delivery:2.2.2")
        implementation("com.google.android.play:integrity:1.4.0")
        implementation("com.google.android.play:review:2.0.2")
    
        // Import these common dependencies.
        implementation("com.google.android.gms:play-services-tasks:18.0.2")
        implementation(files("$playcoreDir/playcore-native-metadata.jar"))
        ...
    }
    
  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 原生 SDK 可收集版本相關資料,讓 Google 能夠改善產品,這包括:

  • 應用程式的套件名稱
  • 應用程式的套件版本
  • Play Core 原生 SDK 版本

當您將應用程式套件上傳至 Play 管理中心時,系統就會收集這類資料。如要選擇退出此資料收集程序,請移除 build.gradle 檔案中的 $playcoreDir/playcore-native-metadata.jar 匯入項目。

請注意,上述與使用 Play Core 原生 SDK 相關的資料收集行為,以及 Google 使用所收集資料的行為,與您在上傳應用程式套件至 Play 管理中心時,Google 收集 Gradle 中宣告的程式庫依附元件無關。

將 Play Core 原生 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 的非空值結果為止:

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
   }
...
}

檢查更新是否過時

除了檢查是否有可用的更新,建議您也同時確認自從使用者上次收到 Play 商店的更新通知後已過了多久。這可以幫助您判斷是否要啟動彈性更新還是立即更新。例如,您可以等待幾天再通知使用者進行彈性更新,接著過幾天後提出需要立即更新。

您可以利用 AppUpdateInfo_getClientVersionStalenessDays() 查看透過 Play 商店推出更新的天數。

int32_t staleness_days = AppUpdateInfo_getClientVersionStalenessDays(info);

檢查更新的優先順序

您可以透過 Google Play Developer API 設定每項更新的優先順序。應用程式可藉此判斷向使用者建議更新的強烈程度。舉例來說,您可以運用下列策略設定更新的優先順序:

  • 使用者介面微幅調整:低度優先等級更新;不需要提出彈性更新和立即更新的要求。僅在使用者未與應用程式互動時更新。
  • 效能提升:中度優先等級更新;要求彈性更新。
  • 重大安全性更新:高度優先等級更新;要求立即更新。

為了判定優先等級,Google Play 使用一個介於 0 到 5 之間的整數值,其中 0 是預設值,5 是最高優先順序。如要設定更新的優先順序,請在 Google Play Developer API 中,使用 Edits.tracks.releases 下的 inAppUpdatePriority 欄位。系統會將此發行版本中所有新增版本的優先順序視同此發行版本的優先順序。只能在推出新版本時才能設定優先順序,而且日後無法更改。

請按照 Play Developer API 說明文件所述,使用 Google 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);

如要釋出資源,請分別呼叫 AppUpdateInfo_destroy()AppUpdateOptions_destroy(),釋出不再需要的 AppUpdateInfoAppUpdateOptions 例項。

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,表示更新流程要求的部分參數格式錯誤。請確認 AppUpdateInfoAppUpdateOptions 物件並非空值且格式正確。
  • 錯誤代碼為 -5, APP_UPDATE_UNAVAILABLE,表示沒有適用的更新項目。確認目標版本使用相同的 套件名稱應用程式 ID簽署金鑰。如果有可用的更新,請清除應用程式的快取,並再次呼叫 AppUpdateManager_requestAppUpdateInfo(),以重新整理 AppUpdateInfo
  • 錯誤代碼為 -6, APP_UPDATE_NOT_ALLOWED,表示系統不允許 AppUpdateOption 物件指定的更新類型。啟動更新流程之前,請檢查 AppUpdateInfo 物件是否指示允許該更新類型。

後續步驟

測試應用程式的應用程式內更新,以驗證整合是否正常運作。