توفير التحديثات داخل التطبيق (الإصدار الأصلي)

يوضِّح هذا الدليل كيفية إتاحة التحديثات داخل التطبيق في تطبيقك باستخدام الرموز البرمجية الأصلية (C أو C++ ). وتتوفّر أدلة منفصلة للحالات التي يستخدم فيها التنفيذ لغة برمجة Kotlin أو لغة برمجة Java، والحالات التي يستخدم فيها التنفيذ Unity.

نظرة عامة على حزمة تطوير البرامج (SDK) المدمجة مع المحتوى

تعتبر حزمة تطوير البرامج (SDK) الأصلية لـ Play Core جزءًا من مجموعة Play Core SDK. تحتوي حزمة تطوير البرامج (SDK) الأصلية على ملف رأس C، app_update.h، الذي يلتف AppUpdateManager من مكتبة التحديثات داخل التطبيق في Java Play. يسمح ملف الرأس هذا لتطبيقك باستدعاء واجهة برمجة التطبيقات للحصول على تحديثات داخل التطبيق مباشرةً من الرمز الأصلي.

إعداد بيئة التطوير

تنزيل 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. عليك القيام بأي مما يلي:

    • ثبِّت الإصدار 4.0 من استوديو Android أو إصدارًا أحدث. استخدِم واجهة مستخدم SDK Manager لتثبيت الإصدار 10.0 من Android SDK Platform (المستوى 29 من واجهة برمجة التطبيقات).
    • ثبِّت أدوات سطر أوامر Android SDK واستخدِم sdkmanager لتثبيت Android SDK Platform الإصدار 10.0 (المستوى 29 من واجهة برمجة التطبيقات).
  2. يمكنك إعداد Android Studio لتطوير البرامج الأصلية باستخدام SDK Manager لتثبيت أحدث إصدار من CMake وAndroid Native Development Kit. لمزيد من المعلومات حول إنشاء أو استيراد مشاريع النصوص البرمجية الأصلية، يمكنك الاطّلاع على بدء استخدام NDK.

  3. قم بتنزيل ملف ZIP واستخرجه إلى جانب مشروعك.

    رابط التنزيل حجم الملف المجموع الاختباري لخوارزمية SHA-256
    36 ميبيبايت 782a8522d937848c83a715c9a258b95a3ff2879a7cd71855d137b41c00786a5e
  4. حدِّث ملف build.gradle في تطبيقك كما هو موضح أدناه:

    رائع

        // 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. حدِّث ملفات 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 بتحسين المنتج، بما في ذلك:

  • اسم حزمة التطبيق
  • إصدار حزمة التطبيق
  • إصدار Play Core Native SDK

سيتم جمع هذه البيانات عند تحميل حزمة تطبيقك على Play Console. لإيقاف عملية جمع البيانات هذه، عليك إزالة استيراد "$playcoreDir/playcore-native-metadata.jar" في ملف Build.gradle.

تجدُر الإشارة إلى أنّ عملية جمع البيانات هذه المتعلّقة باستخدامك لحزمة Play Core Native SDK واستخدام Google للبيانات التي يتم جمعها هي عملية منفصلة ومستقلة عن مجموعة Google من تبعيات المكتبة التي تم تعريفها في Gradle عند تحميل حزمة تطبيقك إلى Play Console.

بعد دمج Play Core Native SDK في مشروعك، يمكنك تضمين السطر التالي في الملفات التي تحتوي على طلبات بيانات من واجهة برمجة التطبيقات:

#include "play/app_update.h"

إعداد واجهة برمجة التطبيقات الخاصة بتحديث التطبيق

عند استخدام واجهة برمجة التطبيقات الخاصة بتحديث داخل التطبيق، يجب إعدادها أولاً من خلال استدعاء وظيفة 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 يشير إلى الأولوية القصوى. لضبط أولوية أحد التحديثات، استخدِم الحقل inAppUpdatePriority ضمن Edits.tracks.releases في واجهة برمجة التطبيقات Google Play Developer API. تُعد جميع الإصدارات المضافة حديثًا في الإصدار نفس أولوية الإصدار. لا يمكن ضبط الأولوية إلا عند طرح إصدار جديد ولا يمكن تغييرها لاحقًا.

حدِّد الأولوية باستخدام واجهة برمجة التطبيقات Google Play Developer API، كما هو موضَّح في مستندات Play Developer 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() بعد انتهاء تطبيقك من استخدام واجهة برمجة التطبيقات.

خطأ أثناء المعالجة

يوضِّح هذا القسم حلولاً للأخطاء الشائعة المُشار إليها بقيم محدّدة في AppUpdateErrorCode:

  • يشير رمز الخطأ -110, APP_UPDATE_INITIALIZATION_NEEDED إلى أنّه لم يتم إعداد واجهة برمجة التطبيقات بنجاح. استدعِ AppUpdateManager_init() لإعداد واجهة برمجة التطبيقات.
  • يشير رمز الخطأ -4, APP_UPDATE_INVALID_REQUEST إلى أن بعض معلَمات طلب تدفق التعديل قد تمت كتابتها بشكل غير صحيح. تأكَّد من أنّ العنصرَين AppUpdateInfo وAppUpdateOptions ليسا فارغَين ومن أنّهما منسقان بشكل صحيح.
  • يشير رمز الخطأ -5, APP_UPDATE_UNAVAILABLE إلى عدم توفّر تحديث. تأكَّد من أنّ الإصدار المستهدَف يتضمّن نفس اسم الحزمة ورقم تعريف التطبيق ومفتاح التوقيع. في حال توفُّر تحديث، امحُ ذاكرة التخزين المؤقت للتطبيق واطلب AppUpdateManager_requestAppUpdateInfo() مرة أخرى لإعادة تحميل AppUpdateInfo.
  • يشير رمز الخطأ -6, APP_UPDATE_NOT_ALLOWED إلى أنّ نوع التعديل المُشار إليه من خلال الكائن AppUpdateOption غير مسموح به. قبل بدء عملية التحديث، تأكَّد مما إذا كان العنصر AppUpdateInfo يشير إلى أنّ نوع التحديث مسموح به.

الخطوات التالية

اختبِر تحديثات تطبيقك داخل التطبيق للتأكّد من أنّ عملية الدمج تعمل بشكل صحيح.