دعم التحديثات داخل التطبيق (Kotlin أو Java)

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

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

تُعدّ مكتبة التحديث داخل التطبيق في Play جزءًا من مكتبات Google Play الأساسية. يُرجى تضمين الاعتمادية التالية لنظام Gradle المتوافق مع Google Play داخل التطبيق تحديث المكتبة.

Groovy

// In your app’s build.gradle file:
...
dependencies {
    // This dependency is downloaded from the Google’s Maven repository.
    // So, make sure you also include that repository in your project's build.gradle file.
    implementation 'com.google.android.play:app-update:2.1.0'

    // For Kotlin users also add the Kotlin extensions library for Play In-App Update:
    implementation 'com.google.android.play:app-update-ktx:2.1.0'
    ...
}

Kotlin

// In your app’s build.gradle.kts file:
...
dependencies {
    // This dependency is downloaded from the Google’s Maven repository.
    // So, make sure you also include that repository in your project's build.gradle file.
    implementation("com.google.android.play:app-update:2.1.0")

    // For Kotlin users also import the Kotlin extensions library for Play In-App Update:
    implementation("com.google.android.play:app-update-ktx:2.1.0")
    ...
}

التحقّق من توفّر تحديثات

قبل طلب تحديث، يُرجى التحقّق من توفُّر تحديث لتطبيقك. استخدام AppUpdateManager للتحقق من وجود تحديث:

Kotlin

val appUpdateManager = AppUpdateManagerFactory.create(context)

// Returns an intent object that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo

// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
        // This example applies an immediate update. To apply a flexible update
        // instead, pass in AppUpdateType.FLEXIBLE
        && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
    ) {
        // Request the update.
    }
}

Java

AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();

// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          // This example applies an immediate update. To apply a flexible update
          // instead, pass in AppUpdateType.FLEXIBLE
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request the update.
    }
});

تم إرجاع AppUpdateInfo على حالة توافر التحديث. بناءً على حالة علامة التبويب تحديث، يحتوي المثيل أيضًا على ما يلي:

  • في حال توفُّر تحديث والسماح بالتحديث، سيظهر المثيل أيضًا يحتوي على نية لبدء التحديث.
  • إذا كان هناك تحديث داخل التطبيق قيد التقدم، سيُبلغ المثيل أيضًا عن حالة التحديث قيد التقدم.

التحقّق من مدى قِدم التحديث

بالإضافة إلى التحقق مما إذا كان هناك تحديث متوفر أم لا، قد تحتاج أيضًا إلى التحقّق من مقدار الوقت المنقضي منذ آخر إشعار للمستخدم بالتحديث من خلال "متجر Play". يمكن أن يساعدك هذا في تحديد ما إذا كان يجب عليك بدء أو تحديث مرن أو تحديث فوري. على سبيل المثال، قد تنتظر بضعة أيام قبل إبلاغ المستخدم بتحديث مرن، وبعد بضعة أيام قبل طلب التحديث الفوري.

استخدام clientVersionStalenessDays() للتحقُّق من عدد الأيام التي مرت منذ توفُّر التحديث على "متجر Play":

Kotlin

val appUpdateManager = AppUpdateManagerFactory.create(context)

// Returns an intent object that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo

// Checks whether the platform allows the specified type of update,
// and current version staleness.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && (appUpdateInfo.clientVersionStalenessDays() ?: -1) >= DAYS_FOR_FLEXIBLE_UPDATE
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
              // Request the update.
    }
}

Java

AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();

// Checks whether the platform allows the specified type of update,
// and current version staleness.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && appUpdateInfo.clientVersionStalenessDays() != null
          && appUpdateInfo.clientVersionStalenessDays() >= DAYS_FOR_FLEXIBLE_UPDATE
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
              // Request the update.
    }
});

التأكّد من أولوية التحديث

تتيح لك واجهة Google Play Developer API تحديد أولوية كل تحديث. ويسمح هذا الإجراء لتطبيقك بتحديد مدى اقتراح تحديث للمستخدم. على سبيل المثال، يمكنك اتّباع الاستراتيجية التالية لضبط أولوية التعديل:

  • تحسينات طفيفة في واجهة المستخدم: تحديث ذو أولوية منخفضة، لا تطلب نهجًا مرنًا أو تحديث فوري. التعديل فقط في حال عدم تفاعل المستخدم مع تطبيقك.
  • تحسينات الأداء: تحديث ذو أولوية متوسطة وطلب صورة تحديث.
  • تحديث أمان مهم: تحديث ذو أولوية عالية وطلب إجراء فوري تحديث.

لتحديد الأولوية، يستخدم Google Play قيمة عددية بين 0 و5 مع 0. ويمثل العدد الافتراضي و5 يمثل الأولوية القصوى. لتعيين أولوية تحديث، يمكنك استخدام الحقل inAppUpdatePriority ضمن Edits.tracks.releases في واجهة Google Play Developer API: جميع الإصدارات التي تمت إضافتها مؤخرًا في الإصدار يحظى بالأولوية نفسها التي يحظى بها الإصدار. لا يمكن ضبط الأولوية إلا عندما طرح إصدار جديد ولا يمكن تغييره لاحقًا

تحديد الأولوية باستخدام Google Play Developer API كما هو موضَّح في Play واجهة برمجة تطبيقات المطور ذات الصلة. يجب تحديد أولوية التحديث داخل التطبيق في Edit.tracks المورد الذي تم تمريره في Edit.tracks: update . يوضح المثال التالي إطلاق تطبيق برمز الإصدار 88. وinAppUpdatePriority 5:

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

في رمز التطبيق، يمكنك التحقّق من مستوى الأولوية لتحديث معيّن باستخدام updatePriority() تأخذ الأولوية التي تم إرجاعها في الاعتبار inAppUpdatePriority لجميع التطبيقات. بين الإصدار المثبت وأحدث إصدار متاح، بغض النظر عن مسار الإصدار. على سبيل المثال، ضع في اعتبارك السيناريو التالي:

  • عندما تطرح الإصدار 1 في قناة الإصدار العلني بدون أولوية
  • طرح الإصدار 2 في مسار اختبار داخلي ذي الأولوية 5
  • طرح الإصدار 3 في قناة إصدار علني بدون أولوية

ستكون الأولوية لمستخدمي الإصدار العلني من الإصدار 1 إلى الإصدار 3. 5، على الرغم من نشر الإصدار 2 على مسار مختلف.

Kotlin

val appUpdateManager = AppUpdateManagerFactory.create(context)

// Returns an intent object that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo

// Checks whether the platform allows the specified type of update,
// and checks the update priority.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && appUpdateInfo.updatePriority() >= 4 /* high priority */
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request an immediate update.
    }
}

Java

AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();

// Checks whether the platform allows the specified type of update,
// and checks the update priority.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && appUpdateInfo.updatePriority() >= 4 /* high priority */
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request an immediate update.
    }
});

بدء تحديث

بعد التأكّد من توفّر تحديث، يمكنك طلب التحديث باستخدام AppUpdateManager.startUpdateFlowForResult():

Kotlin

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build())

Java

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build());

يمكن استخدام كل مثيل AppUpdateInfo لبدء عملية التحديث مرة واحدة فقط. لإعادة المحاولة تعديل المعلومات في حال تعذُّر التحديث، يُرجى طلب "AppUpdateInfo" جديد والتحقّق مرة أخرى أن التحديث متاح ويُسمح به.

يمكنك تسجيل مشغّل نتائج النشاط باستخدام ActivityResultContracts.StartIntentSenderForResult العقد. راجع القسم الذي يتناول الحصول على رد الاتصال لمعرفة حالة التحديث.

تعتمد الخطوات التالية على ما إذا كنت تطلب جدولاً مرنًا إجراء تحديث أو إجراء تحديث فوري

ضبط تحديث باستخدام AppUpdateOptions

AppUpdateOptions يحتوي على حقل AllowAssetPackDeletion يحدد ما إذا كان التحديث يُسمح بمحو حِزم مواد العرض في حال: مساحة تخزين محدودة على الجهاز تم ضبط هذا الحقل على false تلقائيًا، ولكن يمكنك استخدام setAllowAssetPackDeletion() لضبطها على true بدلاً من ذلك:

Kotlin

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE)
        .setAllowAssetPackDeletion(true)
        .build())

Java

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE)
        .setAllowAssetPackDeletion(true)
        .build());

تلقّي معاودة الاتصال لمعرفة حالة التحديث

بعد بدء التحديث، تحصل معاودة الاتصال بمشغّل نتائج النشاط المسجّلة على نتيجة مربع حوار التأكيد:

Kotlin

registerForActivityResult(StartIntentSenderForResult()) { result: ActivityResult ->
    // handle callback
    if (result.resultCode != RESULT_OK) {
        log("Update flow failed! Result code: " + result.resultCode);
        // If the update is canceled or fails,
        // you can request to start the update again.
    }
}

Java

registerForActivityResult(
    new ActivityResultContracts.StartIntentSenderForResult(),
    new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            // handle callback
            if (result.getResultCode() != RESULT_OK) {
                log("Update flow failed! Result code: " + result.getResultCode());
                // If the update is canceled or fails,
                // you can request to start the update again.
            }
        }
    });

هناك قِيَم متعدّدة قد تتلقّاها من "onActivityResult()" رد الاتصال:

  • RESULT_OK: لدى المستخدم قبل التحديث. قد لا تصلك هذه الرسالة عند آخر المعلومات الفورية. معاودة الاتصال لأن التحديث يجب أن ينتهي بالفعل قبل أن يكون عنصر التحكم في الوقت هو التي تم ردها إلى تطبيقك.
  • RESULT_CANCELED: المستخدِم رفض التحديث أو ألغِه.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED: حدث خطأ آخر يمنع المستخدم من تقديم الموافقة أو تحديث من المتابعة.

التعامل مع التحديث المرن

عند بدء تحديث مرن، يظهر مربع حوار أولاً للمستخدم لطلب أو موافقتك. في حال وافق المستخدم، ستبدأ عملية التنزيل في الخلفية. يمكن للمستخدم مواصلة التفاعل مع تطبيقك. يصف هذا القسم كيفية مراقبة وإكمال تحديث مرن داخل التطبيق.

مراقبة حالة التحديث المرن

بعد بدء التنزيل لإجراء تحديث مرن، يجب أن يراقب التطبيق حالة التحديث لمعرفة وقت تثبيت التحديث وعرض التقدم في واجهة المستخدم لتطبيقك.

يمكنك مراقبة حالة عملية التحديث الجارية من خلال تسجيل أداة استماع تحديثات حالة التثبيت. ويمكنك أيضًا توفير شريط تقدّم في واجهة المستخدم الخاصة بالتطبيق من أجل لإعلام المستخدمين بتقدم التنزيل.

Kotlin

// Create a listener to track request state updates.
val listener = InstallStateUpdatedListener { state ->
    // (Optional) Provide a download progress bar.
    if (state.installStatus() == InstallStatus.DOWNLOADING) {
      val bytesDownloaded = state.bytesDownloaded()
      val totalBytesToDownload = state.totalBytesToDownload()
      // Show update progress bar.
    }
    // Log state or install the update.
}

// Before starting an update, register a listener for updates.
appUpdateManager.registerListener(listener)

// Start an update.

// When status updates are no longer needed, unregister the listener.
appUpdateManager.unregisterListener(listener)

Java

// Create a listener to track request state updates.
InstallStateUpdatedListener listener = state -> {
  // (Optional) Provide a download progress bar.
  if (state.installStatus() == InstallStatus.DOWNLOADING) {
      long bytesDownloaded = state.bytesDownloaded();
      long totalBytesToDownload = state.totalBytesToDownload();
      // Implement progress bar.
  }
  // Log state or install the update.
};

// Before starting an update, register a listener for updates.
appUpdateManager.registerListener(listener);

// Start an update.

// When status updates are no longer needed, unregister the listener.
appUpdateManager.unregisterListener(listener);

تثبيت تحديث مرن

عند رصد حالة InstallStatus.DOWNLOADED، عليك إعادة تشغيل التطبيق لتثبيت التحديث.

على عكس التحديثات الفورية، لا يشغّل Google Play أي تطبيق تلقائيًا إعادة التشغيل للحصول على تحديث مرن. وذلك لأنه أثناء إجراء عملية تحديث مرنة، يتوقع المستخدم استمرار التفاعل مع التطبيق إلى أن يقرر أنهم يريدون تثبيت التحديث.

ننصحك بتقديم إشعار (أو مؤشر آخر لواجهة المستخدم). لإبلاغ المستخدم بأنّ التحديث جاهز للتثبيت وطلب التأكيد. قبل إعادة تشغيل التطبيق.

يوضح المثال التالي تنفيذ تصميم متعدد الأبعاد شريط الإعلام المنبثق الذي يطلب تأكيد من المستخدم لإعادة تشغيل التطبيق:

Kotlin

val listener = { state ->
    if (state.installStatus() == InstallStatus.DOWNLOADED) {
        // After the update is downloaded, show a notification
        // and request user confirmation to restart the app.
        popupSnackbarForCompleteUpdate()
    }
    ...
}

// Displays the snackbar notification and call to action.
fun popupSnackbarForCompleteUpdate() {
    Snackbar.make(
        findViewById(R.id.activity_main_layout),
        "An update has just been downloaded.",
        Snackbar.LENGTH_INDEFINITE
    ).apply {
        setAction("RESTART") { appUpdateManager.completeUpdate() }
        setActionTextColor(resources.getColor(R.color.snackbar_action_text_color))
        show()
    }
}

Java

InstallStateUpdatedListener listener = state -> {
    if (state.installStatus() == InstallStatus.DOWNLOADED) {
        // After the update is downloaded, show a notification
        // and request user confirmation to restart the app.
        popupSnackbarForCompleteUpdate();
    }
    ...
};

// Displays the snackbar notification and call to action.
private void popupSnackbarForCompleteUpdate() {
  Snackbar snackbar =
      Snackbar.make(
          findViewById(R.id.activity_main_layout),
          "An update has just been downloaded.",
          Snackbar.LENGTH_INDEFINITE);
  snackbar.setAction("RESTART", view -> appUpdateManager.completeUpdate());
  snackbar.setActionTextColor(
      getResources().getColor(R.color.snackbar_action_text_color));
  snackbar.show();
}

عند الاتصال appUpdateManager.completeUpdate() في المقدّمة، يعرض النظام الأساسي واجهة مستخدم بملء الشاشة تعمل على إعادة تشغيل التطبيق في الخلفية. بعد تثبيت التحديث على النظام الأساسي، تتم إعادة تشغيل التطبيق في نشاطه الرئيسي.

إذا اتصلت بـ completeUpdate() بدلاً من ذلك عندما يكون تطبيقك في الخلفية، يتم تثبيت التحديث تلقائيًا بدون حجب واجهة مستخدم الجهاز.

عندما يجلب المستخدم تطبيقك إلى المقدمة، تحقّق مما إذا كان التطبيق يحتوي على هناك تحديث في انتظار التثبيت. إذا توفّر تحديث لتطبيقك في "DOWNLOADED" المستخدم، اطلب من المستخدم تثبيت التحديث. وبخلاف ذلك، سيتم تحديث البيانات يستمر شغل مساحة التخزين على جهاز المستخدم.

Kotlin

// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all app entry points.
override fun onResume() {
    super.onResume()

    appUpdateManager
        .appUpdateInfo
        .addOnSuccessListener { appUpdateInfo ->
            ...
            // If the update is downloaded but not installed,
            // notify the user to complete the update.
            if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                popupSnackbarForCompleteUpdate()
            }
        }
}

Java

// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all app entry points.
@Override
protected void onResume() {
  super.onResume();

  appUpdateManager
      .getAppUpdateInfo()
      .addOnSuccessListener(appUpdateInfo -> {
              ...
              // If the update is downloaded but not installed,
              // notify the user to complete the update.
              if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                  popupSnackbarForCompleteUpdate();
              }
          });
}

معالجة طلب التحديث الفوري

عند بدء عملية تعديل فورية والموافقة على المستخدم لبدء عملية التعديل يعرض Google Play مدى تقدُّم عملية التحديث أعلى واجهة مستخدم التطبيق في طوال مدة التحديث. إذا أغلق المستخدم تطبيقك أو أنهىه خلال التحديث، سيستمر تنزيل وتثبيت التحديث في الخلفية. بدون تأكيد إضافي من المستخدم.

ومع ذلك، عندما يعود تطبيقك إلى المقدّمة، يجب التأكّد من أنّ عدم إيقاف التحديث في UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS الولاية. في حال إيقاف التحديث في هذه الحالة، استأنف التحديث:

Kotlin

// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all entry points into the app.
override fun onResume() {
    super.onResume()

    appUpdateManager
        .appUpdateInfo
        .addOnSuccessListener { appUpdateInfo ->
            ...
            if (appUpdateInfo.updateAvailability()
                == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
            ) {
                // If an in-app update is already running, resume the update.
                appUpdateManager.startUpdateFlowForResult(
                  appUpdateInfo,
                  activityResultLauncher,
                  AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build())
            }
        }
}

Java

// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all entry points into the app.
@Override
protected void onResume() {
  super.onResume();

  appUpdateManager
      .getAppUpdateInfo()
      .addOnSuccessListener(
          appUpdateInfo -> {
            ...
            if (appUpdateInfo.updateAvailability()
                == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                // If an in-app update is already running, resume the update.
                appUpdateManager.startUpdateFlowForResult(
                  appUpdateInfo,
                  activityResultLauncher,
                  AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build());
            }
          });
}

ويعرض مسار التحديث نتيجة كما هو موضح في الوثائق المرجعية لـ startUpdateFlowForResult(). وعلى وجه الخصوص، يجب أن يكون تطبيقك قادرًا على التعامل مع الحالات التي يرفض فيها المستخدم تحديث أو إلغاء التنزيل. وعندما يقوم المستخدم بتنفيذ أحد هذين الإجراءين، يتم إغلاق واجهة مستخدم Google Play. يجب أن يحدّد تطبيقك أفضل طريقة للمتابعة.

السماح للمستخدم بالمواصلة بدون التحديث، إن أمكن، ومطالبته مرة أخرى لاحقًا. إذا كان تطبيقك لا يعمل بدون التحديث، يمكنك عرض قبل إعادة بدء عملية التحديث أو مطالبة المستخدم إغلاق التطبيق. وبهذه الطريقة، يعرف المستخدم أنّه يمكنه إعادة تشغيل تطبيقك. عندما يصبحون جاهزين لتثبيت التحديث المطلوب.

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

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