يوضّح هذا الدليل كيفية إتاحة التحديثات داخل التطبيق في تطبيقك باستخدام الرمز البرمجي الأصلي (C أو C++). تتوفّر أدلة منفصلة للحالات التي تُستخدم فيها لغة البرمجة Kotlin أو لغة البرمجة Java في التنفيذ، والحالات التي تُستخدم فيها Unity أو Unreal Engine في التنفيذ.
نظرة عامة على حزمة SDK الأصلية
تُعدّ حزمة تطوير البرامج (SDK) الأصلية في "مكتبة Play الأساسية" جزءًا من مجموعة حزمة تطوير البرامج (SDK) في "مكتبة Play الأساسية". تتضمّن حزمة SDK الأصلية ملف العنوان C، app_update.h، الذي يغلّف AppUpdateManager
من Java Play In-App Update Library. يتيح ملف العنوان هذا لتطبيقك إمكانية استدعاء واجهة برمجة التطبيقات الخاصة بالتحديثات داخل التطبيق مباشرةً من الرمز البرمجي الأصلي.
إعداد بيئة التطوير
تنزيل Play Core Native SDK
قبل تنزيل التطبيق، عليك الموافقة على الأحكام والشروط التالية.
الأحكام والشروط
Last modified: September 24, 2020- 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.
- For purposes of these terms, "APIs" means Google's APIs, other developer services, and associated software, including any Redistributable Code.
- “Redistributable Code” means Google-provided object code or header files that call the APIs.
- 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.
- 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.
عليك القيام بأي مما يلي:
- ثبِّت الإصدار 4.0 أو إصدارًا أحدث من استوديو Android. استخدِم واجهة مستخدم SDK Manager لتثبيت الإصدار 10.0 من منصة حزمة تطوير البرامج (SDK) لنظام التشغيل Android (المستوى 29 من واجهة برمجة التطبيقات).
- ثبِّت أدوات سطر أوامر حزمة تطوير البرامج (SDK) لنظام التشغيل Android
واستخدِم
sdkmanagerلتثبيت الإصدار 10.0 من منصة حزمة تطوير البرامج (SDK) لنظام التشغيل Android (مستوى واجهة برمجة التطبيقات 29).
جهِّز "استوديو Android" للتطوير الأصلي باستخدام SDK Manager لتثبيت أحدث إصدار من CMake وحزمة Android Native Development Kit (NDK). لمزيد من المعلومات حول إنشاء المشاريع الأصلية أو استيرادها، يُرجى الاطّلاع على البدء في استخدام NDK.
نزِّل ملف ZIP واستخرج ملفاته بجانب مشروعك.
رابط التنزيل الحجم المجموع الاختباري لخوارزمية SHA-256 54.8 ميغابايت 008b8fedc6179a6dc6ccc21af75591afc7036f78f3d5559d844f1b923934fef0 عدِّل ملف
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.1.0' implementation 'com.google.android.play:asset-delivery:2.3.0' implementation 'com.google.android.play:integrity:1.6.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.3.0") implementation("com.google.android.play:integrity:1.6.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")) ... }
عدِّل ملفات
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 ...)
جمع البيانات
قد تجمع حزمة تطوير البرامج (SDK) الأصلية في "مكتبة Play الأساسية" بيانات متعلقة بالإصدار لتمكين Google من تحسين المنتج، بما في ذلك:
- اسم حزمة التطبيق
- إصدار حزمة التطبيق
- إصدار حزمة تطوير البرامج (SDK) الأصلية في "مكتبة Play الأساسية"
سيتم جمع هذه البيانات عند تحميل حزمة تطبيقك
إلى Play Console. لإيقاف عملية جمع البيانات هذه، عليك إزالة
عملية استيراد $playcoreDir/playcore-native-metadata.jar في ملف build.gradle.
يُرجى العِلم أنّ عملية جمع البيانات هذه المتعلقة باستخدامك لحزمة تطوير البرامج (SDK) الأصلية في "مكتبة Play الأساسية" واستخدام Google للبيانات التي يتم جمعها منفصلة ومستقلة عن عملية جمع Google لموارد الاعتمادية للمكتبة التي تم الإفصاح عنها في Gradle عند تحميل حزمة تطبيقك إلى Play Console.
بعد دمج حزمة تطوير البرامج (SDK) الأصلية في "مكتبة Play الأساسية" في مشروعك، أدرِج السطر التالي في الملفات التي تحتوي على طلبات البيانات من واجهة برمجة التطبيقات:
#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() لمعرفة عدد الأيام التي مرّت منذ أن أصبح التحديث متاحًا في "متجر Google 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 Activity 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يشير إلى أنّ نوع التحديث مسموح به قبل بدء عملية التحديث.
الخطوات التالية
اختبِر التحديثات داخل التطبيق للتأكّد من أنّ عملية الدمج تعمل بشكل صحيح.