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

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

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

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

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

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

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

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

لإضافة وحدة ميزات إلى مشروع تطبيقك باستخدام "استوديو 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" ما يلي في ملف بيان الوحدة ليعكس اختيارك:

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

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

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

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

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

تم العثور على الخدمة انتهت الخدمة المسؤولة عن معالجة الطلب. أعِد محاولة الطلب.

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

INSUFFICIENT_STORAGE لا تتوفّر مساحة تخزين مجانية كافية على الجهاز لتثبيت وحدة الميزات. أبلِغ المستخدم بأنّه لا تتوفّر لديه مساحة تخزين كافية لتثبيت هذه الميزة.
SPLITCOMPAT_VERIFICATION_ERROR، SPLITCOMPAT_EMULATION_ERROR، SPLITCOMPAT_COPY_ERROR تعذَّر على سبليت Compat تحميل وحدة الميزة. ومن المفترض أن يتم حلّ هذه الأخطاء تلقائيًا بعد إعادة تشغيل التطبيق في المرة التالية.
PLAY_STORE_NOT_FOUND تطبيق "متجر Play" غير مثبَّت على الجهاز. أخبِر المستخدم بأنّ تطبيق "متجر Play" مطلوب لتنزيل هذه الميزة.
APP_NOT_OWNED لم يتم تثبيت التطبيق من خلال Google Play ولا يمكن تنزيل الميزة. لا يمكن أن يحدث هذا الخطأ إلا لعمليات التثبيت المؤجَّلة. إذا أردت أن يكتسب المستخدم التطبيق على Google Play، استخدِم startInstall() التي يمكنها الحصول على تأكيد المستخدم اللازم.
خطأ داخلي حدث خطأ داخلي في "متجر 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 فوري يعمل بنظام التشغيل Android 8.0 (المستوى 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 Library لكل من تطبيقك ولكل نشاط في وحدات الميزات التي يُنزِّلها التطبيق.

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

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

تفعيل SplitCompat

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

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

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

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

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

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

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

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

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

Kotlin

class MyApplication : SplitCompatApplication() {
    ...
}

Java

public class MyApplication extends SplitCompatApplication {
    ...
}

تلغي SplitCompatApplication ببساطة ContextWrapper.attachBaseContext() لتضمين SplitCompat.install(Context applicationContext). إذا كنت لا تريد إطالة SplitCompatApplication لفئة Application، يمكنك إلغاء طريقة 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);
}

إذا كانت وحدة "عند الطلب" متوافقة مع كل من التطبيقات الفورية والتطبيقات المثبّتة، يمكنك استدعاء سبليت Compat بشروط كما يلي:

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);
    }
}

تفعيل سبليتCompat لأنشطة الوحدات

بعد تفعيل SplitCompat لتطبيقك الأساسي، ستحتاج إلى تفعيل SplitCompat لكلّ نشاط ينزِّله تطبيقك في وحدة من الميزات. لإجراء ذلك، استخدِم طريقة 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() بعد تفعيل سبليت سبليت.

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() بعد تفعيل سبليت.

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 في مقالة نصائح JNI على Android.

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

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

إذا كنت تستخدم System.loadLibrary() لتحميل الرموز البرمجية الأصلية وكانت مكتبتك الأصلية تعتمد على مكتبة أخرى في الوحدة، يجب تحميل تلك المكتبة الأخرى يدويًا أولاً. في حال استخدام "أداة إعادة الربط"، تكون العملية المكافئة هي 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 الفورية على Android 8.0 والإصدارات الأحدث

عند طلب وحدة عند الطلب لتطبيق Android فوري على جهاز Android 8.0 (المستوى 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 وتقسيمCompat على المستوى 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" حِزم APK هذه تلقائيًا لمحاكاة طلب الوحدات وتنزيلها وتثبيتها من "متجر 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 Feature Delivery Library إمكانية استخدام حِزم APK المجزّأة محليًا لاختبار تثبيت وحدات الميزات بدون الاتصال بـ "متجر Play".

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

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

bundletool install-apks --apks my_app.apks

عند بدء التطبيق وإكمال خطوات المستخدمين لتنزيل وحدة من الميزات وتثبيتها، تستخدم "مكتبة الميزات في Play" حِزم APK التي تم نقلها من قِبل "bundletool" إلى مساحة التخزين المحلية للجهاز.

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

لمحاكاة عمليات تثبيت الوحدات من "متجر Play"، تستخدم "مكتبة الميزات في Play" بديلاً لـ SplitInstallManager، يُسمى FakeSplitInstallManager، لطلب الوحدة. عند استخدام bundletool مع العلامة --local-testing من أجل إنشاء مجموعة من حِزم APK ونشرها على جهازك الاختباري، يتضمّن ذلك بيانات وصفية توجِّه "مكتبة تقديم الميزات في Play" إلى تبديل طلبات بيانات من واجهة برمجة التطبيقات تلقائيًا إلى استدعاء 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);