يوضّح هذا الدليل كيفية إتاحة التحديثات داخل التطبيق في تطبيقك باستخدام الرمز البرمجي الأصلي (C أو C++). تتوفّر أدلة منفصلة للحالات التي يستخدم فيها التنفيذ لغة البرمجة Kotlin أو لغة البرمجة Java، والحالات التي يستخدم فيها التنفيذ Unity أو Unreal Engine.
نظرة عامة على حِزم SDK الأصلية
تُعدّ حزمة تطوير البرامج (SDK) الأصلية لمكتبة Play Core جزءًا من مجموعة حزمة تطوير البرامج (SDK) لمكتبة Play Core. تتضمّن حزمة Native 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 Studio أو إصدارًا أحدث. استخدِم واجهة مستخدم "مدير حزمة تطوير البرامج (SDK)" لتثبيت الإصدار 10.0 من "منصّة حزمة تطوير البرامج (SDK) لنظام التشغيل Android" (المستوى 29 من واجهة برمجة التطبيقات).
- ثبِّت أدوات سطر الأوامر لحزمة تطوير البرامج (SDK) لنظام التشغيل Android واستخدِم
sdkmanager
لتثبيت الإصدار 10.0 من حزمة تطوير البرامج (SDK) لنظام التشغيل Android (المستوى 29 من واجهة برمجة التطبيقات).
جهِّز "استوديو Android" للتطوير بلغة C/C++ من خلال استخدام مدير SDK لتثبيت أحدث إصدار من CMake و"حزمة تطوير البرامج الأصلية لنظام التشغيل Android" (NDK). لمزيد من المعلومات حول إنشاء المشاريع الأصلية أو استيرادها، يُرجى الاطّلاع على البدء في استخدام NDK.
نزِّل ملف ZIP واستخرِجه بجانب مشروعك.
رابط التنزيل الحجم المجموع الاختباري SHA-256 37.8 MiB 9db60185185342f28d2c278b60222333608c67bc022e458a25224eaea8c4c4b7 عدِّل ملف
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.3.0' 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.3.0") 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")) ... }
عدِّل ملفات
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 Core
سيتم جمع هذه البيانات عند تحميل حزمة تطبيقك
إلى Play Console. لإيقاف عملية جمع البيانات هذه، عليك إزالة عبارة الاستيراد
$playcoreDir/playcore-native-metadata.jar
في ملف build.gradle.
يُرجى العِلم أنّ عملية جمع البيانات هذه المتعلقة باستخدامك لحزمة تطوير البرامج (SDK) الأصلية في Play Core واستخدام 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()
للتحقّق من عدد الأيام التي مرّت منذ أن أصبح التحديث متاحًا من خلال "متجر 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()
لطلب عملية تحديث بشكل غير متزامن، مع تمرير jobject
لنشاط Android كمعلَمة نهائية.
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
يشير إلى أنّ نوع التحديث مسموح به قبل بدء عملية التحديث.
الخطوات التالية
اختبِر تحديثات التطبيق داخل التطبيق للتأكّد من أنّ عملية الدمج تعمل بشكل صحيح.