Hỗ trợ cập nhật trong ứng dụng (Gốc)

Hướng dẫn này mô tả cách hỗ trợ các bản cập nhật trong ứng dụng trên ứng dụng của bạn dùng mã gốc (C hoặc C++). Chúng tôi có hướng dẫn riêng cho các trường hợp trong đó quy trình triển khai sử dụng ngôn ngữ lập trình Kotlin hoặc ngôn ngữ lập trình Java cũng như các trường hợp có quy trình triển khai dùng hàm Unity.

Tổng quan về SDK gốc (native SDK)

SDK gốc của Play Core nằm trong nhóm SDK của Play Core. SDK gốc có một tệp tiêu đề C, app_update.h, gói AppUpdateManager từ Thư viện bản cập nhật trong ứng dụng của Java Play. Tệp tiêu đề này cho phép ứng dụng của bạn gọi API để cập nhật trực tiếp trong ứng dụng từ mã gốc.

Thiết lập môi trường phát triển

下载 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.14.0.zip

  1. Thực hiện một trong hai cách sau:

    • Cài đặt Android Studio phiên bản 4.0 trở lên. Sử dụng giao diện người dùng Trình quản lý SDK để cài đặt Nền tảng SDK Android phiên bản 10.0 (API cấp 29).
    • Cài đặt công cụ dòng lệnh của SDK Android và sử dụng sdkmanager để cài đặt Nền tảng SDK Android phiên bản 10.0 (API cấp 29).
  2. Chuẩn bị Android Studio cho việc phát triển bằng mã gốc bằng cách dùng Trình quản lý SDK để cài đặt CMake và Bộ phát triển mã gốc Android (NDK) mới nhất. Để biết thêm thông tin về việc tạo hoặc nhập các dự án gốc, xem Bắt đầu với NDK.

  3. Tải tệp zip xuống và giải nén cùng dự án của bạn.

    Đường liên kết để tải xuống Kích thước Giá trị tổng kiểm SHA-256
    36 MiB 782a8522d937848c83a715c9a258b95a3ff2879a7cd71855d137b41c00786a5e
  4. Cập nhật tệp build.gradle của ứng dụng như minh hoạ dưới đây:

    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.0.0'
            implementation 'com.google.android.play:asset-delivery:2.0.0'
            implementation 'com.google.android.play:integrity:1.0.1'
            implementation 'com.google.android.play:review:2.0.0'
    
            // 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.0.0")
        implementation("com.google.android.play:asset-delivery:2.0.0")
        implementation("com.google.android.play:integrity:1.0.1")
        implementation("com.google.android.play:review:2.0.0")
    
        // Import these common dependencies.
        implementation("com.google.android.gms:play-services-tasks:18.0.2")
        implementation(files("$playcoreDir/playcore-native-metadata.jar"))
        ...
    }
    
  5. Cập nhật các tệp CMakeLists.txt của ứng dụng như bên dưới:

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

Thu thập dữ liệu

SDK gốc của Play Core có thể thu thập một số dữ liệu liên quan đến phiên bản để cho phép Google cải thiện sản phẩm, trong đó có:

  • Tên gói của ứng dụng
  • Phiên bản gói của ứng dụng
  • Phiên bản SDK gốc của Play Core

Dữ liệu này sẽ được thu thập khi bạn tải gói ứng dụng lên Play Console. Để chọn không tham gia quá trình thu thập dữ liệu này, hãy xoá việc nhập $playcoreDir/playcore-native-metadata.jar trong tệp build.gradle.

Lưu ý: việc thu thập dữ liệu này liên quan đến việc bạn sử dụng SDK gốc của Play Core. Bên cạnh đó, việc Google sử dụng dữ liệu đã thu thập là riêng biệt cũng như độc lập với việc Google thu thập các phần phụ thuộc của thư viện được khai báo trong Gradle khi bạn tải gói ứng dụng lên Play Console.

Sau khi bạn tích hợp SDK Gốc của Play Core vào dự án của mình, hãy đưa dòng sau vào các tệp chứa lệnh gọi API:

#include "play/app_update.h"

Khởi chạy API cập nhật trong ứng dụng

Bất cứ khi nào bạn sử dụng API cập nhật trong ứng dụng, trước tiên hãy khởi chạy API bằng cách gọi hàm AppUpdateManager_init() như hiển thị trong ví dụ sau được tạo bằng 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.
  }
}

Kiểm tra xem có bản cập nhật chưa

Trước khi yêu cầu cập nhật, hãy kiểm tra xem có bản cập nhật cho ứng dụng của bạn hay không. AppUpdateManager_requestInfo() sẽ bắt đầu một yêu cầu không đồng bộ, thu thập thông tin cần thiết để khởi chạy quy trình (flow) cập nhật trong ứng dụng vào lúc khác. Hàm trả về APP_UPDATE_NO_ERROR nếu yêu cầu khởi chạy thành công.

AppUpdateErrorCode error_code = AppUpdateManager_requestInfo()

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

Bạn có thể theo dõi tiến trình và kết quả của yêu cầu bằng cách sử dụng AppUpdateManager_getInfo(). Ngoài mã lỗi, hàm này còn trả về một cấu trúc mờ AppUpdateInfo mà bạn có thể sử dụng để truy xuất thông tin về yêu cầu cập nhật. Ví dụ: bạn có thể muốn gọi hàm này trong mọi vòng lặp trò chơi cho đến khi hàm trả về kết quả không rỗng cho 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
   }
...
}

Kiểm tra tình trạng lỗi thời của bản cập nhật

Ngoài việc kiểm tra xem có bản cập nhật hay không, bạn cũng nên kiểm tra xem đã bao lâu kể từ lần gần nhất người dùng được Cửa hàng Play thông báo về bản cập nhật. Điều này có thể giúp bạn quyết định xem nên tiến hành cập nhật linh hoạt hay cập nhật tức thì. Ví dụ: bạn có thể chờ một vài ngày trước khi thông báo cho người dùng về bản cập nhật linh hoạt và vài ngày sau đó mới yêu cầu cập nhật ngay.

Sử dụng AppUpdateInfo_getClientVersionStalenessDays() để kiểm tra đã bao nhiêu ngày đã trôi qua kể từ khi bản cập nhật đó có trên Cửa hàng Play:

int32_t staleness_days = AppUpdateInfo_getClientVersionStalenessDays(info);

Kiểm tra mức độ ưu tiên của bản cập nhật

API Nhà phát triển Google Play cho phép bạn thiết lập mức độ ưu tiên của mỗi bản cập nhật. Điều này cho phép ứng dụng của bạn quyết định mức độ đề xuất bản cập nhật cho người dùng. Ví dụ: nên cân nhắc chiến lược sau đây về việc đặt mức độ ưu tiên cho bản cập nhật:

  • Cải tiến nhỏ về giao diện người dùng: Bản cập nhật có mức độ ưu tiên thấp; không yêu cầu cập nhật linh hoạt cũng như cập nhật tức thì. Chỉ cập nhật khi người dùng không tương tác với ứng dụng.
  • Cải thiện hiệu suất: Cập nhật có mức độ ưu tiên trung bình; yêu cầu cập nhật linh hoạt.
  • Cập nhật bảo mật quan trọng: Cập nhật có mức độ ưu tiên cao; yêu cầu cập nhật ngay.

Để xác định mức độ ưu tiên, Google Play sử dụng giá trị số nguyên từ 0 đến 5, trong đó 0 là giá trị mặc định và 5 là mức độ ưu tiên cao nhất. Để đặt mức độ ưu tiên cho một bản cập nhật, hãy sử dụng trường inAppUpdatePriority trong Edits.tracks.releases trong API nhà phát triển Google Play. Tất cả các phiên bản mới thêm trong bản phát hành này đều có cùng mức độ ưu tiên với bản phát hành. Bạn chỉ có thể đặt mức độ ưu tiên khi ra mắt bản phát hành mới và không thể thay đổi sau này.

Đặt mức độ ưu tiên bằng cách sử dụng API Nhà phát triển Google Play như mô tả trong tài liệu về API Nhà phát triển Play. Chỉ định mức độ ưu tiên của bản cập nhật ứng dụng trong tài nguyên Edit.tracksđược chuyển trong phương thức Edit.tracks: update. Ví dụ sau minh họa việc phát hành một ứng dụng có mã phiên bản 88 và inAppUpdatePriority 5:

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

Trong mã của ứng dụng, bạn có thể kiểm tra mức độ ưu tiên của một bản cập nhật cụ thể bằng cách sử dụng AppUpdateInfo_getPriority():

int32_t priority = AppUpdateInfo_getPriority(info);

Bắt đầu cập nhật

Sau khi xác nhận đã có bản cập nhật, bạn có thể yêu cầu cập nhật bằng AppUpdateManager_requestStartUpdate(): Trước khi yêu cầu cập nhật, hãy lấy đối tượng AppUpdateInfo mới nhất và tạo đối tượng AppUpdateOptions để định cấu hình quy trình cập nhật. Đối tượng AppUpdateOptions xác định các tùy chọn cho quy trình cập nhật trong ứng dụng, bao gồm cả việc nên cập nhật linh hoạt hay ngay lập tức.

Ví dụ sau đây sẽ tạo một đối tượng AppUpdateOptions cho quy trình cập nhật linh hoạt:

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

Ví dụ sau đây sẽ tạo một đối tượng AppUpdateOptions cho quy trình cập nhật ngay:

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

Đối tượng AppUpdateOptions cũng chứa trường AllowAssetPackDeletion xác định liệu quá trình cập nhật có được phép xoá các gói tài sản trong trường hợp bộ nhớ thiết bị hạn chế hay không. Trường này được đặt thành false theo mặc định, nhưng bạn có thể sử dụng phương thức AppUpdateOptions_setAssetPackDeletionAllowed()để đặt thành true:

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

Sau khi có đối tượng AppUpdateInfo cập nhật và đối tượng AppUpdateOptions được định cấu hình đúng cách, hãy gọi AppUpdateManager_requestStartUpdate() để yêu cầu không đồng bộ quy trình cập nhật, chuyển vào Hoạt động Android jobject để có thông số cuối cùng.

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

Để giải phóng tài nguyên, hãy huỷ bỏ các bản sao của AppUpdateInfoAppUpdateOptions mà bạn không cần nữa bằng cách gọi lần lượt AppUpdateInfo_destroy()AppUpdateOptions_destroy().

AppUpdateInfo_destroy(info);
AppUpdateOptions_destroy(options);

Đối với quy trình cập nhật ngay, Google Play sẽ hiện trang yêu cầu người dùng xác nhận. Khi người dùng chấp nhận yêu cầu, Google Play sẽ tự động tải xuống và cài đặt bản cập nhật ở nền trước, sau đó khởi động lại ứng dụng với phiên bản đã cập nhật nếu quá trình cài đặt thành công.

Để có quy trình cập nhật linh hoạt, bạn có thể tiếp tục yêu cầu đối tượng AppUpdateInfo cập nhật để theo dõi trạng thái cập nhật hiện tại trong khi người dùng tiếp tục tương tác với ứng dụng. Sau khi tải xuống thành công, bạn phải kích hoạt việc hoàn tất quá trình cập nhật bằng cách gọi AppUpdateManager_requestCompleteUpdate() như trong ví dụ sau:

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

Giải phóng tài nguyên bằng cách gọi hàm AppUpdateManager_destroy() sau khi ứng dụng của bạn sử dụng xong API.

Xử lý lỗi

Phần này mô tả giải pháp cho các lỗi phổ biến được biểu thị bằng các giá trị AppUpdateErrorCode cụ thể:

  • Mã lỗi -110, APP_UPDATE_INITIALIZATION_NEEDED cho biết API chưa được khởi chạy thành công. Gọi AppUpdateManager_init() để khởi chạy API.
  • Mã lỗi -4, APP_UPDATE_INVALID_REQUEST đã chỉ ra một số tham số của yêu cầu luồng cập nhật không đúng định dạng. Kiểm tra để đảm bảo các đối tượng AppUpdateInfoAppUpdateOptions không rỗng và được định dạng đúng.
  • Mã lỗi -5, APP_UPDATE_UNAVAILABLE cho biết không có bản cập nhật hiện hành nào. Đảm bảo phiên bản đích có cùng tên gói, ID ứng dụngkhóa ký. Nếu có bản cập nhật, hãy xóa bộ nhớ đệm của ứng dụng và gọi lại AppUpdateManager_requestAppUpdateInfo() để làm mới AppUpdateInfo.
  • Mã lỗi -6, APP_UPDATE_NOT_ALLOWED cho biết loại cập nhật do đối tượng AppUpdateOption biểu thị là không được phép. Kiểm tra xem đối tượng AppUpdateInfo có cho biết liệu loại cập nhật có được cho phép hay không trước khi bắt đầu quy trình cập nhật.

Các bước tiếp theo

Kiểm thử bản cập nhật trong ứng dụng của ứng dụng để xác minh quá trình tích hợp của bạn đang hoạt động chính xác.