ضبط ميزة التسليم عند الطلب

تتيح لك وحدات الميزات فصل ميزات وموارد معيّنة عن الوحدة الأساسية في تطبيقك وتضمينها في حِزمة التطبيق. ومن خلال عرض الميزات في Play، يمكن للمستخدمين مثلاً تنزيل هذه المكوّنات وتثبيتها عند الطلب بعد تثبيت حزمة APK الأساسية لتطبيقك.

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

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

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

تساعدك هذه الصفحة في إضافة وحدة ميزات إلى مشروع تطبيقك و إعدادها للتسليم عند الطلب. قبل البدء، تأكَّد من استخدام Android Studio 3.5 أو إصدار أحدث وAndroid Gradle Plugin 3.5.0 أو إصدار أحدث.

ضبط وحدة جديدة للعرض عند الطلب

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

لإضافة وحدة ميزة إلى مشروع تطبيقك باستخدام "استوديو Android"، يُرجى اتّباع الخطوات التالية:

  1. افتح مشروع تطبيقك في بيئة التطوير المتكاملة (IDE)، إذا لم يسبق لك إجراء ذلك.
  2. اختَر ملف > جديد > وحدة جديدة من شريط القوائم.
  3. في مربّع الحوار إنشاء وحدة جديدة، اختَر وحدة ميزة ديناميكية وانقر على التالي.
  4. في قسم ضبط وحدتك الجديدة، أكمِل الخطوات التالية:
    1. اختَر وحدة التطبيق الأساسية لمشروع تطبيقك من القائمة المنسدلة.
    2. حدِّد اسم الوحدة. يستخدم IDE هذا الاسم لتحديد الوحدة كمشروع فرعي في Gradle في ملف إعدادات Gradle. عند إنشاء حِزمة تطبيقك، يستخدم Gradle العنصر الأخير من اسم المشروع الفرعي لإدراج سمة <manifest split> في بيان وحدة الميزات.
    3. حدِّد اسم الحزمة للوحدة. بشكل تلقائي، يقترح "استوديو Android" اسم حزمة يجمع بين اسم الحزمة الجذر للوحدة الأساسية واسم الوحدة الذي حدّدته في الخطوة السابقة.
    4. اختَر الحد الأدنى لمستوى واجهة برمجة التطبيقات الذي تريد أن تتوافق معه الوحدة. يجب أن تتطابق هذه القيمة مع قيمة الوحدة الأساسية.
  5. انقر على التالي.
  6. في قسم خيارات تنزيل الوحدة، أكمل ما يلي:

    1. حدِّد عنوان الوحدة باستخدام ما يصل إلى 50 حرفًا. يستخدم النظام الأساسي هذا العنوان لتعريف الوحدة للمستخدمين عند، على سبيل المثال، تأكيد ما إذا كان المستخدم يريد تنزيل الوحدة. ولهذا السبب، يجب أن تتضمّن الوحدة الأساسية في تطبيقك عنوان الوحدة باعتباره مورد سلسلة يمكنك ترجمته. عند إنشاء الوحدة باستخدام "استوديو Android"، يضيف IDE مورد السلسلة إلى الوحدة الأساسية نيابةً عنك ويُدخل الإدخال التالي في بيان وحدة الميزة:

      <dist:module
          ...
          dist:title="@string/feature_title">
      </dist:module>
      
    2. في القائمة المنسدلة ضمن تضمين وقت التثبيت، اختَر عدم تضمين الوحدة عند التثبيت. يُدخل Android Studio العناصر التالية في ملف بيان الوحدة لتعكس اختيارك:

      <dist:module ... >
        <dist:delivery>
            <dist:on-demand/>
        </dist:delivery>
      </dist:module>
      
    3. ضَع علامة في المربّع بجانب دمج إذا كنت تريد أن تكون هذه الوحدة متاحة للأجهزة التي تعمل بنظام التشغيل Android 4.4 (المستوى 20 لواجهة برمجة التطبيقات) والإصدارات الأقدم وأن تكون مضمّنة في حِزم APK متعددة. وهذا يعني أنّه يمكنك تفعيل السلوك عند الطلب لهذه الوحدة وإيقاف الدمج لحذفها من الأجهزة التي لا تتوافق مع تنزيل حِزم APK المجزّأة وتثبيتها. يُدخل Android Studio العناصر التالية في ملف بيان الوحدة لتعكس اختيارك:

      <dist:module ...>
          <dist:fusing dist:include="true | false" />
      </dist:module>
      
  7. انقر على إنهاء.

بعد أن ينتهي Android Studio من إنشاء وحدتك، يمكنك فحص محتوياتها بنفسك من لوحة المشروع (اختَر عرض > نوافذ الأدوات > المشروع من شريط القوائم). يجب أن يكون الرمز البرمجي والموارد والمؤسسة التلقائية مشابهةً لتلك الخاصة بوحدة التطبيق العادية.

بعد ذلك، عليك تنفيذ وظيفة التثبيت عند الطلب باستخدام مكتبة "عرض الميزات في Play".

تضمين مكتبة "عرض الميزات في Play" في مشروعك

قبل البدء، عليك أولاً إضافة مكتبة عرض الميزات في Play إلى مشروعك.

طلب وحدة عند الطلب

عندما يحتاج تطبيقك إلى استخدام وحدة ميزات، يمكنه طلب وحدة خاصة أثناء تشغيله في المقدّمة من خلال الفئة SplitInstallManager. عند إجراء طلب، يجب أن يحدِّد تطبيقك اسم الوحدة كما هو محدَّد في عنصر split في بيان الوحدة المستهدَفة. عند إنشاء وحدة ميزة باستخدام "استوديو Android"، يستخدم نظام الإنشاء اسم الوحدة الذي تقدّمه لإدراج هذه السمة في بيان الوحدة في وقت الترجمة. للمزيد من المعلومات، يمكنك الاطّلاع على بيانات وحدة الميزات.

على سبيل المثال، لنفترض أنّ هناك تطبيقًا يتضمّن وحدة عند الطلب لالتقاط رسائل مصوّرة وإرسالها باستخدام كاميرا الجهاز، وتحدّد هذه الوحدة عند الطلب split="pictureMessages" في البيان. يستخدم العيّنة التالية السمة SplitInstallManager لطلب وحدة "pictureMessages" (إلى جانب وحدة إضافية لبعض الفلاتر الترويجية):

Kotlin

// Creates an instance of SplitInstallManager.
val splitInstallManager = SplitInstallManagerFactory.create(context)

// Creates a request to install a module.
val request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build()

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener { sessionId -> ... }
    .addOnFailureListener { exception ->  ... }

Java

// Creates an instance of SplitInstallManager.
SplitInstallManager splitInstallManager =
    SplitInstallManagerFactory.create(context);

// Creates a request to install a module.
SplitInstallRequest request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build();

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener(sessionId -> { ... })
    .addOnFailureListener(exception -> { ... });

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

ملاحظة: لا بأس بطلب وحدة ميزة تم تثبيتها من قبل على الجهاز. تُعتبر واجهة برمجة التطبيقات الطلب مكتملاً على الفور إذا رصدت أنّه سبق تثبيت الوحده . بالإضافة إلى ذلك، بعد تثبيت إحدى الوحدات، يحافظ Google Play على تحديثها تلقائيًا. وهذا يعني أنّه عند تحميل إصدار جديد من حِزمة تطبيقك، يُحدِّث النظام الأساسي جميع حِزم APK المثبَّتة التي تنتمي إلى تطبيقك. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة إدارة تحديثات التطبيقات.

للوصول إلى رمز الوحدة ومواردها، يجب أن يُفعّل تطبيقك SplitCompat. يُرجى العِلم أنّ حزمة SplitCompat ليست مطلوبة لتطبيقات Android الفورية.

تأجيل تثبيت الوحدات عند الطلب

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

يمكنك تحديد وحدة لتنزيلها لاحقًا باستخدام الإجراء deferredInstall() ، كما هو موضّح أدناه. وعلى عكس SplitInstallManager.startInstall()، لا يحتاج تطبيقك إلى أن يكون في المقدّمة لبدء طلب للقيام بأحد عمليات التثبيت المؤجّل.

Kotlin

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(listOf("promotionalFilters"))

Java

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(Arrays.asList("promotionalFilters"));

يتم تنفيذ طلبات عمليات التثبيت المؤجّلة بأفضل ما يمكن، ولا يمكنك تتبُّع مستوى تقدّمها. لذا، قبل محاولة الوصول إلى الوحدة التي حددتها للتثبيت المؤجل، يجب عليك التحقق من تثبيت الوحدة. إذا كنت بحاجة إلى توفّر الوحدة على الفور، استخدِم بدلاً من ذلك SplitInstallManager.startInstall() لطلبها، كما هو موضّح في القسم السابق .

مراقبة حالة الطلب

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

Kotlin

// Initializes a variable to later track the session ID for a given request.
var mySessionId = 0

// Creates a listener for request status updates.
val listener = SplitInstallStateUpdatedListener { state ->
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
}

// Registers the listener.
splitInstallManager.registerListener(listener)

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener { sessionId -> mySessionId = sessionId }
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener { exception ->
        // Handle request errors.
    }

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener)

Java

// Initializes a variable to later track the session ID for a given request.
int mySessionId = 0;

// Creates a listener for request status updates.
SplitInstallStateUpdatedListener listener = state -> {
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
};

// Registers the listener.
splitInstallManager.registerListener(listener);

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener(sessionId -> { mySessionId = sessionId; })
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener(exception -> {
        // Handle request errors.
    });

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener);

التعامل مع أخطاء الطلبات

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

من ناحية الرموز، يجب التعامل مع حالات تعذُّر تنزيل وحدة أو تثبيتها باستخدام addOnFailureListener() على النحو الموضّح أدناه:

Kotlin

splitInstallManager
    .startInstall(request)
    .addOnFailureListener { exception ->
        when ((exception as SplitInstallException).errorCode) {
            SplitInstallErrorCode.NETWORK_ERROR -> {
                // Display a message that requests the user to establish a
                // network connection.
            }
            SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED -> checkForActiveDownloads()
            ...
        }
    }

fun checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .sessionStates
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                // Check for active sessions.
                for (state in task.result) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        }
}

Java

splitInstallManager
    .startInstall(request)
    .addOnFailureListener(exception -> {
        switch (((SplitInstallException) exception).getErrorCode()) {
            case SplitInstallErrorCode.NETWORK_ERROR:
                // Display a message that requests the user to establish a
                // network connection.
                break;
            case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED:
                checkForActiveDownloads();
            ...
    });

void checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .getSessionStates()
        .addOnCompleteListener( task -> {
            if (task.isSuccessful()) {
                // Check for active sessions.
                for (SplitInstallSessionState state : task.getResult()) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        });
}

يوضّح الجدول التالي حالات الخطأ التي قد يحتاج تطبيقك إلى معالجتها:

رمز الخطأ الوصف الإجراء المقترَح
ACTIVE_SESSIONS_LIMIT_EXCEEDED تم رفض الطلب لأنّ هناك طلبًا واحدًا على الأقل يجري تنزيله حاليًا. تحقَّق مما إذا كانت هناك أي طلبات لا تزال قيد التنزيل، كما هو موضّح في النموذج أعلاه.
MODULE_UNAVAILABLE يتعذّر على Google Play العثور على الوحدة المطلوبة بناءً على الإصدار الحالي المثبَّت من التطبيق والجهاز وحساب المستخدم على Google Play. إذا لم يكن لدى المستخدم إذن الوصول إلى الوحدة، أرسِل إليه إشعارًا بذلك.
INVALID_REQUEST تلقّى Google Play الطلب، ولكن الطلب غير صالح. تأكَّد من أنّ المعلومات المضمّنة في الطلب كاملة ودقيقة.
الجلسة التي لم يتم العثور عليها لم يتم العثور على جلسة لرقم تعريف جلسة معيّن. إذا كنت تحاول مراقبة حالة طلب من خلال رقم تعريف الجلسة، تأكَّد من صحة رقم تعريف الجلسة.
واجهة برمجة التطبيقات_غير متوفرة لا تتوفّر "مكتبة إرسال الميزات" في Play على الجهاز الحالي. وهذا يعني أنّ الجهاز لا يمكنه تنزيل الميزات المثبَّتة وتثبيتها عند الطلب. بالنسبة إلى الأجهزة التي تعمل بالإصدار 4.4 من نظام التشغيل Android (المستوى 20 من واجهة برمجة التطبيقات) أو إصدار أقدم، يجب تضمين وحدات الميزات في وقت التثبيت باستخدام dist:fusing سمة البيان. لمزيد من المعلومات، يمكنك الاطّلاع على المزيد من المعلومات حول بيان وحدة الميزات.
NETWORK_ERROR تعذّرت تلبية الطلب بسبب خطأ في الشبكة. اطلب من المستخدم إما إنشاء اتصال بالشبكة أو التغيير إلى شبكة أخرى.
ACCESS_DENIED يتعذّر على التطبيق تسجيل الطلب بسبب عدم توفّر أذونات كافية. ويحدث ذلك عادةً عندما يكون التطبيق في الخلفية. حاوِل إرسال الطلب عندما يعود التطبيق إلى المقدّمة.
INCOMPATIBLE_WITH_EXISTING_SESSION يحتوي الطلب على وحدة واحدة أو أكثر سبق أن تم طلبها ولكن لم يتم تثبيتها بعد. يمكنك إنشاء طلب جديد لا يتضمّن الوحدات التي سبق لتطبيقك طلبها، أو الانتظار إلى أن تنتهي عملية تثبيت كل الوحدات المطلوبة حاليًا قبل إعادة محاولة إرسال الطلب.

يُرجى العِلم أنّ طلب وحدة سبق تثبيتها لا يؤدي إلى ظهور خطأ.

SERVICE_DIED توقّفت الخدمة المسؤولة عن معالجة الطلب. يُرجى إعادة إرسال الطلب.

تتلقّى SplitInstallStateUpdatedListener SplitInstallSessionState مع رمز الخطأ هذا والحالة FAILED ومعرّف الجلسة -1.

سعة التخزين غير متوفرة لا يتوفّر مساحة تخزين كافية على الجهاز لتثبيت وحدة الميزة. أبلِغ المستخدم بأنّه لا تتوفّر لديه مساحة تخزين كافية لتثبيت هذه الميزة.
SPLITCOMPAT_VERIFICATION_ERROR وSPLITCOMPAT_EMULATION_ERROR وSPLITCOMPAT_COPY_ERROR تعذّر على SplitCompat تحميل وحدة الميزات. من المفترض أن يتم حلّ هذه الأخطاء تلقائيًا بعد إعادة تشغيل التطبيق التالي.
متجر Play لم يتم العثور عليه لم يتم تثبيت تطبيق "متجر Play" على الجهاز. أخبِر المستخدم بأنّه يجب تنزيل تطبيق "متجر Play" لتنزيل هذه الميزة.
APP_NOT_OWNED لم يتم تثبيت التطبيق من خلال Google Play ولا يمكن تنزيل الميزة. لا يمكن أن يحدث هذا الخطأ إلا لعمليات التثبيت المؤجّلة. إذا أردت أن يحصل المستخدم على التطبيق من Google Play، استخدِم startInstall() الذي يمكنه الحصول على تأكيد المستخدم اللازم.
INTERNAL_ERROR حدث خطأ داخلي في "متجر Play". يُرجى إعادة إرسال الطلب.

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

تعديل حالة الاسم المعرِّف

بعد تسجيل مستمع وتسجيل معرّف الجلسة لطلبك، استخدِم StateUpdatedListener.onStateUpdate() للتعامل مع تغييرات الحالة، كما هو موضّح أدناه.

Kotlin

override fun onStateUpdate(state : SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIED) {
       // Retry the request.
       return
    }
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.DOWNLOADING -> {
              val totalBytes = state.totalBytesToDownload()
              val progress = state.bytesDownloaded()
              // Update progress bar.
            }
            SplitInstallSessionStatus.INSTALLED -> {

              // After a module is installed, you can start accessing its content or
              // fire an intent to start an activity in the installed module.
              // For other use cases, see access code and resources from installed modules.

              // If the request is an on demand module for an Android Instant App
              // running on Android 8.0 (API level 26) or higher, you need to
              // update the app context using the SplitInstallHelper API.
            }
        }
    }
}

Java

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) {
       // Retry the request.
       return;
    }
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.DOWNLOADING:
              int totalBytes = state.totalBytesToDownload();
              int progress = state.bytesDownloaded();
              // Update progress bar.
              break;

            case SplitInstallSessionStatus.INSTALLED:

              // After a module is installed, you can start accessing its content or
              // fire an intent to start an activity in the installed module.
              // For other use cases, see access code and resources from installed modules.

              // If the request is an on demand module for an Android Instant App
              // running on Android 8.0 (API level 26) or higher, you need to
              // update the app context using the SplitInstallHelper API.
        }
    }
}

في الجدول أدناه، يتم توضيح الحالات المحتمَلة لطلب التثبيت.

حالة الطلب الوصف الإجراء المقترَح
في انتظار المراجعة تم قبول الطلب ومن المفترض أن يبدأ التنزيل قريبًا. احرص على تهيئة مكونات واجهة المستخدم، مثل شريط التقدم، لتقديم ملاحظات للمستخدم بشأن عملية التنزيل.
REQUIRES_USER_CONFIRMATION يتطلب التنزيل تأكيدًا من المستخدم. تحدث هذه الحالة غالبًا إذا لم يتم تثبيت التطبيق من خلال Google Play. اطلب من المستخدم تأكيد تنزيل الميزة من خلال Google Play. لمزيد من المعلومات، انتقِل إلى القسم الذي يتناول كيفية الحصول على تأكيد من المستخدم.
التنزيل جارٍ التنزيل. إذا كنت تقدّم شريط تقدّم للتنزيل، استخدِم SplitInstallSessionState.bytesDownloaded() وSplitInstallSessionState.totalBytesToDownload() لتعديل واجهة المستخدم (اطّلِع على نموذج الرمز البرمجي أعلاه هذا الجدول).
تم التنزيل نزَّل الجهاز الوحدة ولكن لم يبدأ التثبيت بعد. يجب أن تفعِّل التطبيقات حزمة SplitCompat للوصول إلى الوحدات التي تم تنزيلها وتجنُّب ظهور هذه الحالة. وهذا الإجراء مطلوب للوصول إلى رمز وحدة الميزات ومواردها.
جارٍ التثبيت يُثبِّت الجهاز الوحدة حاليًا. عدِّل شريط التقدّم. وعادةً ما تكون هذه الحالة قصيرة.
تم التثبيت تم تثبيت الوحدة على الجهاز. رمز الوصول والمرجع في الوحدة لمواصلة رحلة المستخدِم

إذا كانت الوحدة مخصّصة لتطبيق Android فوري يعمل على الإصدار 8.0 من نظام التشغيل Android (المستوى 26 لواجهة برمجة التطبيقات) أو إصدار أحدث، عليك استخدام splitInstallHelper لتحديث مكوّنات التطبيق باستخدام الوحدة الجديدة.

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

الحصول على تأكيد من المستخدم

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

Kotlin

override fun onSessionStateUpdate(state: SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          // an activity result launcher registered via registerForActivityResult
          activityResultLauncher)
    }
    ...
 }

Java

@Override void onSessionStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          // an activity result launcher registered via registerForActivityResult
          activityResultLauncher);
    }
    ...
 }

يمكنك تسجيل مشغّل نتائج النشاط باستخدام العقدة المضمّنة ActivityResultContracts.StartIntentSenderForResult . يُرجى الاطّلاع على واجهات برمجة تطبيقات نتائج الأنشطة.

يتم تعديل حالة الطلب استنادًا إلى ردّ المستخدم:

  • إذا وافق المستخدم على التأكيد، تتغير حالة الطلب إلى PENDING وتستأنف عملية التنزيل.
  • إذا رفض المستخدم التأكيد، تتغيّر حالة الطلب إلى CANCELED.
  • إذا لم يحدّد المستخدم خيارًا قبل إغلاق مربّع الحوار، تظل حالة الطلب على القيمة REQUIRES_USER_CONFIRMATION. يمكن لتطبيقك مطالبة المستخدم مرة أخرى بإكمال الطلب.

لتلقّي مكالمة تلقائية تتضمّن ردّ المستخدم، يمكنك إلغاء ActivityResultCallback كما هو موضّح أدناه.

Kotlin

registerForActivityResult(StartIntentSenderForResult()) { result: ActivityResult -> {
        // Handle the user's decision. For example, if the user selects "Cancel",
        // you may want to disable certain functionality that depends on the module.
    }
}

Java

registerForActivityResult(
    new ActivityResultContracts.StartIntentSenderForResult(),
    new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            // Handle the user's decision. For example, if the user selects "Cancel",
            // you may want to disable certain functionality that depends on the module.
        }
    });

إلغاء طلب تثبيت

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

Kotlin

splitInstallManager
    // Cancels the request for the given session ID.
    .cancelInstall(mySessionId)

Java

splitInstallManager
    // Cancels the request for the given session ID.
    .cancelInstall(mySessionId);

الوصول إلى الوحدات

للوصول إلى الرموز البرمجية والموارد من وحدة تم تنزيلها بعد تنزيلها، يجب أن يفعّل تطبيقك مكتبة SplitCompat لتطبيقك وكل نشاط في وحدات الميزات التي تنزيلها.

يُرجى العِلم أنّ المنصة تواجه قيودًا التالية على الوصول إلى محتوى الوحدة لبعض الوقت (أيام في بعض الحالات) بعد تنزيل الوحدة:

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

تفعيل SplitCompat

لكي يتمكّن تطبيقك من الوصول إلى الرموز البرمجية والموارد من وحدة تم تنزيلها، عليك تفعيل SplitCompat باستخدام إحدى الطريقتَين الموضّحتَين في القسمَين التاليَين فقط.

بعد تفعيل حزمة SplitCompat لتطبيقك، عليك أيضًا تفعيل حزمة SplitCompat لكل نشاط في وحدات الميزات التي تريد أن يصل إليها تطبيقك.

تعريف فئة SplitCompatApplication في البيان

إنّ أبسط طريقة لتفعيل SplitCompat هي تحديد SplitCompatApplication كالفئة الفرعية Application في ملف بيان تطبيقك، كما هو موضّح أدناه:

<application
    ...
    android:name="com.google.android.play.core.splitcompat.SplitCompatApplication">
</application>

بعد تثبيت التطبيق على جهاز، يمكنك الوصول إلى الرموز البرمجية والموارد من وحدات الميزات التي تم تنزيلها تلقائيًا.

استدعاء SplitCompat في وقت التشغيل

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

إذا كانت لديك فئة Application مخصّصة، اجعلها بدلاً من ذلك تمتد من SplitCompatApplication لتفعيل SplitCompat لتطبيقك، كما هو موضّح أدناه:

Kotlin

class MyApplication : SplitCompatApplication() {
    ...
}

Java

public class MyApplication extends SplitCompatApplication {
    ...
}

تتجاهل SplitCompatApplication ببساطة ContextWrapper.attachBaseContext() لتضمين SplitCompat.install(Context applicationContext). إذا كنت لا تريد أن تشمل فئة Application الصف SplitCompatApplication، يمكنك إلغاء طريقةattachBaseContext() يدويًا على النحو التالي:

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    // Emulates installation of future on demand modules using SplitCompat.
    SplitCompat.install(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Emulates installation of future on demand modules using SplitCompat.
    SplitCompat.install(this);
}

إذا كانت الوحدة عند الطلب متوافقة مع كلٍّ من التطبيقات الفورية والتطبيقات المثبَّتة، يمكنك استخدام SplitCompat بشكل مشروط على النحو التالي:

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    if (!InstantApps.isInstantApp(this)) {
        SplitCompat.install(this)
    }
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    if (!InstantApps.isInstantApp(this)) {
        SplitCompat.install(this);
    }
}

تفعيل SplitCompat لأنشطة الوحدات

بعد تفعيل spCompat للتطبيق الأساسي، يجب تفعيل تقسيمات لكل نشاط ينزله تطبيقك في وحدة الميزات. لإجراء ذلك، استخدِم طريقة SplitCompat.installActivity() على النحو التالي:

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    // Emulates installation of on demand modules using SplitCompat.
    SplitCompat.installActivity(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Emulates installation of on demand modules using SplitCompat.
    SplitCompat.installActivity(this);
}

الوصول إلى المكوّنات المحدّدة في وحدات الميزات

بدء نشاط محدّد في وحدة من الميزات

يمكنك إطلاق الأنشطة المحدّدة في وحدات الميزات باستخدام startActivity() بعد تفعيل SplitCompat.

Kotlin

startActivity(Intent()
  .setClassName("com.package", "com.package.module.MyActivity")
  .setFlags(...))

Java

startActivity(new Intent()
  .setClassName("com.package", "com.package.module.MyActivity")
  .setFlags(...));

المَعلمة الأولى التي يتم تمريرها إلى setClassName هي اسم حِزمة التطبيق، والملفَّق المَعلمة الثانية هي اسم الفئة الكامل للنشاط.

عندما يكون لديك نشاط في وحدة ميزة نزّلته عند الطلب، عليك تفعيل SplitCompat في النشاط.

بدء خدمة محدّدة في وحدة ميزة

يمكنك تشغيل الخدمات المحدّدة في وحدات الميزات باستخدام startService() بعد تفعيل SplitCompat.

Kotlin

startService(Intent()
  .setClassName("com.package", "com.package.module.MyService")
  .setFlags(...))

Java

startService(new Intent()
  .setClassName("com.package", "com.package.module.MyService")
  .setFlags(...));

تصدير مكوّن محدّد في وحدة ميزة

يجب عدم تضمين مكوّنات Android التي تم تصديرها داخل وحدات اختيارية.

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

ولا يشكّل ذلك مشكلة في المكوّنات الداخلية، إذ لا يمكن الوصول إليها إلا من خلال التطبيق، ما يتيح للتطبيق التحقّق من تثبيت الوحدة قبل الوصول إلى المكوّن.

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

الوصول إلى الرموز البرمجية والموارد من الوحدات المثبَّتة

في حال تفعيل SplitCompat لسياق التطبيق الأساسي والأنشطة في وحدة الميزات، يمكنك استخدام الرمز البرمجي والموارد من وحدة الميزات كما لو كانت جزءًا من حزمة APK الأساسية، بعد تثبيت الوحدة الاختيارية.

الوصول إلى رمز من وحدة مختلفة

الوصول إلى الرمز الأساسي من وحدة

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

الوصول إلى رمز الوحدة من وحدة أخرى

لا يمكن الوصول إلى كائن أو فئة داخل وحدة بشكلٍ ثابت من وحدة أخرى مباشرةً، ولكن يمكن الوصول إليهما بشكلٍ غير مباشر باستخدام ميزة "العرض المرجعي".

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

لتبسيط التفاعلات مع العنصر بعد إنشائه، يُنصح بتحديد واجهة في الوحدة الأساسية وتنفيذها في وحدة الميزات. على سبيل المثال:

Kotlin

// In the base module
interface MyInterface {
  fun hello(): String
}

// In the feature module
object MyInterfaceImpl : MyInterface {
  override fun hello() = "Hello"
}

// In the base module, where we want to access the feature module code
val stringFromModule = (Class.forName("com.package.module.MyInterfaceImpl")
    .kotlin.objectInstance as MyInterface).hello();

Java

// In the base module
public interface MyInterface {
  String hello();
}

// In the feature module
public class MyInterfaceImpl implements MyInterface {
  @Override
  public String hello() {
    return "Hello";
  }
}

// In the base module, where we want to access the feature module code
String stringFromModule =
   ((MyInterface) Class.forName("com.package.module.MyInterfaceImpl").getConstructor().newInstance()).hello();

الوصول إلى المراجع ومواد العرض من وحدة مختلفة

بعد تثبيت إحدى الوحدات، يمكنك الوصول إلى الموارد ومواد العرض ضمن الوحدة بالطريقة العادية، مع مراعاة أمرَين:

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

تحميل رمز أصلي في تطبيق باستخدام ميزة "العرض عند الطلب"

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

تحميل رمز أصلي من وحدة اختيارية

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

إذا كنت تستخدم System.loadLibrary() لتحميل الرمز الأصلي وكانت مكتبتك الأصلية تعتمد على مكتبة أخرى في الوحدة، عليك أولاً تحميل تلك المكتبة الأخرى يدويًا. إذا كنت تستخدم ReLinker، تكون العملية المكافئة هي Relinker.recursively().loadLibrary().

إذا كنت تستخدم dlopen() في الرمز البرمجي الأصلي لتحميل مكتبة محدّدة في ملف وحدة اختيارية، لن تعمل مع مسارات المكتبات النسبية. أفضل حلّ هو استرداد المسار المطلق للمكتبة من رمز Java من خلال ClassLoader.findLibrary() ثم استخدامه في طلب dlopen(). يمكنك إجراء ذلك قبل إدخال الرمز البرمجي الأصلي أو استخدام طلب JNI من الرمز البرمجي الأصلي إلى Java.

الوصول إلى تطبيقات Android الفورية المثبَّتة

بعد أن تُبلغ وحدة تطبيق Android الفوري عن أنّها INSTALLED، يمكنك الوصول إلى الرمز البرمجي والموارد باستخدام سياق التطبيق الذي تم تعديله. إنّ السياق الذي ينشئه تطبيقك قبل تثبيت وحدة (مثل سياق سبق تخزينه في متغيّر) لا يحتوي على محتوى الوحدة الجديدة. أمّا السياق الجديد، فيؤدّي إلى ذلك، ويمكن الحصول عليه مثلاً باستخدام createPackageContext.

Kotlin

// Generate a new context as soon as a request for a new module
// reports as INSTALLED.
override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                val newContext = context.createPackageContext(context.packageName, 0)
                // If you use AssetManager to access your app’s raw asset files, you’ll need
                // to generate a new AssetManager instance from the updated context.
                val am = newContext.assets
            }
        }
    }
}

Java

// Generate a new context as soon as a request for a new module
// reports as INSTALLED.
@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // If you use AssetManager to access your app’s raw asset files, you’ll need
                // to generate a new AssetManager instance from the updated context.
                AssetManager am = newContext.getAssets();
        }
    }
}

تطبيقات Android الفورية على الإصدار 8.0 من نظام Android والإصدارات الأحدث

عند طلب وحدة عند الطلب لتطبيق Android فوري على الإصدار 8.0 من Android (المستوى 26 لواجهة برمجة التطبيقات) والإصدارات الأحدث، بعد أن يُبلغ طلب التثبيت عن الخطأ INSTALLED، عليك تحديث التطبيق باستخدام سياق الوحدة الجديدة من خلال طلب SplitInstallHelper.updateAppInfo(Context context). بخلاف ذلك، لا يكون التطبيق على دراية بكود الوحدة ومواردها بعد. بعد تعديل البيانات الوصفية للتطبيق، عليك تحميل محتويات الوحدات أثناء حدث الخيط الرئيسي التالي من خلال استدعاء Handler جديد، كما هو موضّح أدناه:

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                // You need to perform the following only for Android Instant Apps
                // running on Android 8.0 (API level 26) and higher.
                if (BuildCompat.isAtLeastO()) {
                    // Updates the app’s context with the code and resources of the
                    // installed module.
                    SplitInstallHelper.updateAppInfo(context)
                    Handler().post {
                        // Loads contents from the module using AssetManager
                        val am = context.assets
                        ...
                    }
                }
            }
        }
    }
}

Java

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
            // You need to perform the following only for Android Instant Apps
            // running on Android 8.0 (API level 26) and higher.
            if (BuildCompat.isAtLeastO()) {
                // Updates the app’s context with the code and resources of the
                // installed module.
                SplitInstallHelper.updateAppInfo(context);
                new Handler().post(new Runnable() {
                    @Override public void run() {
                        // Loads contents from the module using AssetManager
                        AssetManager am = context.getAssets();
                        ...
                    }
                });
            }
        }
    }
}

تحميل مكتبات C/C++

إذا كنت تريد تحميل مكتبات C/C++ من وحدة سبق أن حمّلها الجهاز في تطبيق فوري، استخدِم SplitInstallHelper.loadLibrary(Context context, String libName)، كما هو موضّح أدناه:

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.INSTALLED -> {
                // Updates the app’s context as soon as a module is installed.
                val newContext = context.createPackageContext(context.packageName, 0)
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, my-cpp-lib)
                ...
            }
        }
    }
}

Java

public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.INSTALLED:
                // Updates the app’s context as soon as a module is installed.
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, my-cpp-lib);
                ...
        }
    }
}

القيود المعروفة

  • من غير الممكن استخدام Android WebView في نشاط يصل إلى الموارد أو الأصول من وحدة اختيارية. ويعود سبب ذلك إلى عدم التوافق بين WebView وSplitCompat على المستوى 28 من واجهة برمجة التطبيقات لنظام التشغيل Android والإصدارات الأقدم.
  • لا يمكنك تخزين عناصر ApplicationInfo في Android مؤقتًا أو محتواها أو العناصر التي تحتوي عليها داخل تطبيقك. عليك دائمًا جلب هذه العناصر حسب الحاجة من سياق التطبيق. وقد يؤدي تخزين هذه العناصر مؤقتًا إلى تعطُّل التطبيق عند تثبيت وحدة ميزة.

إدارة الوحدات المثبَّتة

للتحقّق من وحدات الميزات المثبَّتة حاليًا على الجهاز، يمكنك استدعاء SplitInstallManager.getInstalledModules()، الذي يعرض Set<String> لأسماء الوحدات المثبَّتة، كما هو موضّح أدناه.

Kotlin

val installedModules: Set<String> = splitInstallManager.installedModules

Java

Set<String> installedModules = splitInstallManager.getInstalledModules();

إلغاء تثبيت الوحدات

يمكنك أن تطلب من الجهاز إلغاء تثبيت الوحدات من خلال استدعاء SplitInstallManager.deferredUninstall(List<String> moduleNames) كما هو موضّح أدناه.

Kotlin

// Specifies two feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(listOf("pictureMessages", "promotionalFilters"))

Java

// Specifies two feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(Arrays.asList("pictureMessages", "promotionalFilters"));

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

تنزيل مراجع لغوية إضافية

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

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

Kotlin

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply()
...

// Creates a request to download and install additional language resources.
val request = SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build()

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request)

Java

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply();
...

// Creates a request to download and install additional language resources.
SplitInstallRequest request =
    SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build();

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request);

تتم معالجة الطلب كما لو كان طلبًا لوحدة ميزة. وهذا يعني أنّه يمكنك مراقبة حالة الطلب كالمعتاد.

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

Kotlin

splitInstallManager.deferredLanguageInstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))

Java

splitInstallManager.deferredLanguageInstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));

الوصول إلى مراجع اللغة التي تم تنزيلها

للوصول إلى موارد اللغة التي تم تنزيلها، يجب أن يشغّل تطبيقك الأسلوب SplitCompat.installActivity() ضمن الأسلوب attachBaseContext() لكل نشاط يتطلّب الوصول إلى هذه الموارد، كما هو موضّح أدناه.

Kotlin

override fun attachBaseContext(base: Context) {
  super.attachBaseContext(base)
  SplitCompat.installActivity(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
  super.attachBaseContext(base);
  SplitCompat.installActivity(this);
}

لكل نشاط تريد استخدام موارد لغوية نزّلها تطبيقك، عدِّل السياق الأساسي واضبط لغة جديدة من خلال Configuration:

Kotlin

override fun attachBaseContext(base: Context) {
  val configuration = Configuration()
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
  val context = base.createConfigurationContext(configuration)
  super.attachBaseContext(context)
  SplitCompat.install(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
  Configuration configuration = new Configuration();
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
  Context context = base.createConfigurationContext(configuration);
  super.attachBaseContext(context);
  SplitCompat.install(this);
}

لكي تصبح هذه التغييرات سارية، عليك إعادة إنشاء نشاطك بعد تثبيت اللغة الجديدة واستعدادها للاستخدام. يمكنك استخدام الطريقة Activity#recreate().

Kotlin

when (state.status()) {
  SplitInstallSessionStatus.INSTALLED -> {
      // Recreates the activity to load resources for the new language
      // preference.
      activity.recreate()
  }
  ...
}

Java

switch (state.status()) {
  case SplitInstallSessionStatus.INSTALLED:
      // Recreates the activity to load resources for the new language
      // preference.
      activity.recreate();
  ...
}

إلغاء تثبيت موارد اللغة الإضافية

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

Kotlin

val installedLanguages: Set<String> = splitInstallManager.installedLanguages

Java

Set<String> installedLanguages = splitInstallManager.getInstalledLanguages();

ويمكنك بعد ذلك تحديد اللغات التي تريد إلغاء تثبيتها باستخدام طريقة deferredLanguageUninstall() كما هو موضّح أدناه.

Kotlin

splitInstallManager.deferredLanguageUninstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))

Java

splitInstallManager.deferredLanguageUninstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));

اختبار عمليات تثبيت الوحدات على الجهاز

تتيح لك "مكتبة عرض الميزات" في Play اختبار قدرة تطبيقك على تنفيذ ما يلي محليًا بدون الاتصال بـ "متجر Play":

توضّح هذه الصفحة كيفية نشر حِزم APK المجزّأة لتطبيقك على جهازك التجريبي كي تتمكّن ميزة "عرض الميزات في Play" من استخدام هذه الحِزم تلقائيًا لمحاكاة طلب الوحدات وتنزيلها وتثبيتها من "متجر Play".

على الرغم من أنّه ليس عليك إجراء أي تغييرات على منطق تطبيقك، عليك استيفاء المتطلبات التالية:

  • نزِّل أحدث إصدار من bundletool وثبِّته. تحتاج إلى bundletool لإنشاء مجموعة جديدة من حِزم APK القابلة للتثبيت من حِزمة تطبيقك.

إنشاء مجموعة من حِزم APK

أنشئ حِزم APK المجزّأة لتطبيقك، على النحو التالي، إذا لم يسبق لك إجراء ذلك:

  1. أنشئ حِزمة تطبيق لتطبيقك باستخدام إحدى الطريقتَين التاليتَين:
  2. استخدِم bundletool لإنشاء مجموعة من حِزم APK لجميع إعدادات الأجهزة باستخدام الأمر التالي:

    bundletool build-apks --local-testing
      --bundle my_app.aab
      --output my_app.apks
    

تتضمّن العلامة --local-testing بيانات وصفية في بيانات حِزم APK تتيح لمكتبة عرض الميزات في Play معرفة استخدام حِزم APK المجزّأة على الجهاز لاختبار تثبيت وحدات الميزات بدون الاتصال بـ "متجر Play".

نشر تطبيقك على الجهاز

بعد إنشاء مجموعة من حِزم APK باستخدام العلامة --local-testing، استخدِم bundletool لتثبيت الإصدار الأساسي من تطبيقك ونقل ملف APK إضافي إلى مساحة التخزين المتوفّرة على جهازك. يمكنك تنفيذ الإجراءين باستخدام الأمر التالي:

bundletool install-apks --apks my_app.apks

الآن، عند تشغيل تطبيقك وإكمال مسار المستخدم لتنزيل ملف APK الخاص بأحد bundletool وحدات الميزات وتثبيته، تستخدم مكتبة Play Feature Delivery Library حِزم APK التي bundletool تم نقلها إلى مساحة التخزين التلقائية على الجهاز.

محاكاة خطأ في الشبكة

لمحاكاة عمليات تثبيت الوحدات من "متجر Play"، تستخدم مكتبة "عرض الميزات في Play" SplitInstallManagerبديلاً يُسمى FakeSplitInstallManager، لطلب الوحدة. عند استخدام bundletool مع العلامة --local-testing لإنشاء مجموعة من حِزم APK ونشرها على جهاز الاختبار، فإنه يتضمّن بيانات وصفية تطلب من مكتبة Play Feature Delivery Library تبديل طلبات بيانات واجهة برمجة التطبيقات في تطبيقك تلقائيًا لتشغيل FakeSplitInstallManager بدلاً من SplitInstallManager.

يتضمّن FakeSplitInstallManager علامة منطقية يمكنك تفعيلها لمحاكاة خطأ في الشبكة في المرة التالية التي يطلب فيها تطبيقك تثبيت وحدة معيّنة. للوصول إلى FakeSplitInstallManager في اختباراتك، يمكنك الحصول على مثيل منه باستخدام FakeSplitInstallManagerFactory، كما هو موضّح أدناه:

Kotlin

// Creates an instance of FakeSplitInstallManager with the app's context.
val fakeSplitInstallManager = FakeSplitInstallManagerFactory.create(context)
// Tells Play Feature Delivery Library to force the next module request to
// result in a network error.
fakeSplitInstallManager.setShouldNetworkError(true)

Java

// Creates an instance of FakeSplitInstallManager with the app's context.
FakeSplitInstallManager fakeSplitInstallManager =
    FakeSplitInstallManagerFactory.create(context);
// Tells Play Feature Delivery Library to force the next module request to
// result in a network error.
fakeSplitInstallManager.setShouldNetworkError(true);