תמיכה בעדכונים בתוך האפליקציה (מקור)

במדריך הזה מוסבר איך לתמוך בבתוך האפליקציה עדכונים באפליקציה באמצעות קוד נייטיב (C או C++). יש מדריכים נפרדים למקרים שבהם ההטמעה משתמשת שפת התכנות Kotlin או תכנות Java שפה ומקרים שבהם מוטמעת באמצעות Unity.

סקירה כללית על Native SDK

Play Core Native SDK הוא חלק מ-Play Core משפחת ה-SDK. השפה המקומית ב-SDK יש קובץ כותרת C, app_update.h, שעוטף AppUpdateManager מספריית העדכונים בתוך האפליקציה של Java Play. קובץ הכותרת הזה מאפשר לאפליקציה מפעילים את ה-API כדי לבצע עדכונים בתוך האפליקציה ישירות מקוד ה-Native.

הגדרת סביבת הפיתוח

Download Play Core Native SDK

Before downloading, you must agree to the following terms and conditions.

Terms and Conditions

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.
Download Play Core Native SDK

play-core-native-sdk-1.14.0.zip

  1. בצע אחת מהפעולות הבאות:

    • מתקינים את Android Studio בגרסה 4.0 ואילך. שימוש ב-SDK ממשק משתמש של מנהל להתקנת פלטפורמת Android SDK גרסה 10.0 (רמת API 29).
    • להתקין את כלי שורת הפקודה של Android SDK. ולהשתמש ב-sdkmanager כדי להתקין פלטפורמת Android SDK גרסה 10.0 (רמת API 29).
  2. כדי להכין את Android Studio לפיתוח מקורי באמצעות מנהל ה-SDK כדי להתקין את הגרסה העדכנית ביותר CMake ו-Android Native Development Kit (NDK). מידע נוסף על ליצירה או לייבוא של פרויקטים מקוריים, תחילת העבודה עם NDK.

  3. מורידים את קובץ ה-ZIP ומחלצים אותו לצד הפרויקט.

    קישור להורדה גודל סיכום ביקורת (checksum) SHA-256
    36MiB 782a8522d937848c83a715c9a258b95a3ff2879a7cd71855d137b41c00786a5e
  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.1'
    
            // 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.1")
    
        // 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 Native SDK יאסוף נתונים שקשורים לגרסאות כדי לאפשר ל-Google: לשפר את המוצר, כולל:

  • שם החבילה של האפליקציה
  • גרסת החבילה של האפליקציה
  • גרסת ה-SDK של Play Core Native

הנתונים האלה ייאספו כשתעלו את חבילת האפליקציה אל Play Console. כדי לבטל את ההסכמה לתהליך איסוף הנתונים הזה, צריך להסיר את ייבוא של $playcoreDir/playcore-native-metadata.jar בקובץ build.gradle.

לתשומת ליבך, איסוף הנתונים הזה שקשור לשימוש שלך ב-Play Core Native SDK השימוש של Google בנתונים שנאספו הוא נפרד ובלתי תלוי אוסף יחסי התלות של ספריות שהוצהרו ב-Gradle כשמעלים את האפליקציה חבילה ל-Play Console.

אחרי שמשלבים את 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:

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

בדיקת עדיפות העדכון

ממשק ה-API של Google Play למפתחים מאפשר להגדיר את העדיפות של כל עדכון. כך האפליקציה שלך יכולה להחליט באיזו מידה להמליץ למשתמש על עדכון. לדוגמה, שימוש באסטרטגיה הבאה להגדרת עדיפות העדכון:

  • שיפורים קטנים בממשק המשתמש: עדכון עדיפות נמוכה; לא בקשה או עדכון מיידי. עדכון רק כשהמשתמש אין אינטראקציה באפליקציה שלכם.
  • שיפורי ביצועים: עדכון לעדיפות בינונית; לבקש
  • עדכון אבטחה קריטי: עדכון בעדיפות גבוהה; בקשה מיידית

כדי לקבוע עדיפות, Google Play משתמשת בערך שלם בין 0 ל-5, עם 0 להיות ברירת המחדל ו-5 היא העדיפות הגבוהה ביותר. כדי להגדיר עדיפות של משתמשים בשדה inAppUpdatePriority שמתחת Edits.tracks.releases בממשק API של Google Play למפתחים. כל הגרסאות החדשות שנוספו בגרסה הן נחשבת לאותה עדיפות כמו של פריט התוכן. ניתן להגדיר עדיפות רק כאשר בתהליך השקה של גרסה חדשה, ואי אפשר לשנות אותה מאוחר יותר.

הגדרת העדיפות באמצעות ממשק API של Google Play למפתחים, כפי שמתואר ב-Play ממשק API למפתחים תיעוד. לציין את העדיפות של העדכונים בתוך האפליקציה בעמודה Edit.tracks שהמשאב הועבר Edit.tracks: update . בדוגמה הבאה מוצגת השקה של אפליקציה עם קוד גרסה 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 וגם AppUpdateOptions שכבר אין לך צורך בהתקשרות 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.
    }
}

כדי לפנות משאבים, אפשר להפעיל AppUpdateManager_destroy() אחרי שהאפליקציה סיימה להשתמש ב-API.

טיפול בשגיאות

בקטע הזה מתוארים פתרונות לשגיאות נפוצות שמצביעות על AppUpdateErrorCode ערכים:

  • קוד שגיאה של -110, APP_UPDATE_INITIALIZATION_NEEDED מציין ה-API לא הופעל. חיוג למספר AppUpdateManager_init() אל מתחילה לאתחל את ה-API.
  • קוד שגיאה של -4, APP_UPDATE_INVALID_REQUEST מציין של בקשת העדכון בפורמט שגוי. צריך לוודא האובייקטים AppUpdateInfo ו-AppUpdateOptions אינם ריקים בפורמט הנכון.
  • קוד השגיאה -5, APP_UPDATE_UNAVAILABLE מציין שאין יש עדכון זמין. צריך לוודא שגרסת היעד זהה package name, מזהה בקשה, ומפתח החתימה. אם יש עדכון זמין, מנקים את המטמון של האפליקציה וקוראים שוב אל AppUpdateManager_requestAppUpdateInfo() כדי רענון AppUpdateInfo.
  • קוד שגיאה של -6, APP_UPDATE_NOT_ALLOWED מציין שסוג העדכון שמצוין על ידי האובייקט AppUpdateOption. יש לבדוק אם האובייקט AppUpdateInfo מציין שסוג העדכון מותר לפני להתחיל את תהליך העדכון.

השלבים הבאים

בודקים את העדכונים בתוך האפליקציה כדי לוודא שהשילוב פועל כראוי.