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

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

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

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

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

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

ضبط وحدة جديدة لخدمة التوصيل عند الطلب

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

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

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

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

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

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

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

بعد أن ينتهي &quot;استوديو Android&quot; من إنشاء الوحدة، يمكنك فحص محتوياتها بنفسك من لوحة المشروع (اختَر عرض > نوافذ الأدوات > المشروع من شريط القوائم). يجب أن يكون الرمز التلقائي والموارد والتنظيم مشابهة لتلك الخاصة بوحدة تطبيق 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);

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

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

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

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

SERVICE_DIED تعطّلت الخدمة المسؤولة عن معالجة الطلب. أعِد محاولة إرسال الطلب.

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

INSUFFICIENT_STORAGE لا تتوفّر مساحة تخزين كافية على الجهاز لتثبيت وحدة الميزة. إشعار المستخدم بأنّه لا تتوفّر مساحة تخزين كافية لتثبيت هذه الميزة
SPLITCOMPAT_VERIFICATION_ERROR, SPLITCOMPAT_EMULATION_ERROR, SPLITCOMPAT_COPY_ERROR تعذّر على SplitCompat تحميل وحدة الميزات. من المفترض أن يتم حل هذه الأخطاء تلقائيًا بعد إعادة تشغيل التطبيق في المرة التالية.
PLAY_STORE_NOT_FOUND لم يتم تثبيت تطبيق "متجر Play" على الجهاز. أبلِغ المستخدم بأنّه يجب تنزيل تطبيق &quot;متجر Play&quot; لاستخدام هذه الميزة.
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 (المستوى 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 لأنشطة الوحدات

بعد تفعيل 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() بعد تفعيل 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 لتحميل جميع المكتبات الأصلية عند استخدام ميزة &quot;التسليم عند الطلب&quot; لوحدات الميزات. يحلّ ReLinker مشكلة في تحميل المكتبات المجمّعة من رموز برمجية أصلية بعد تثبيت وحدة ميزات. يمكنك معرفة المزيد من المعلومات عن ReLinker في نصائح Android JNI.

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

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

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

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

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

بعد أن يتم الإبلاغ عن وحدة تطبيق فوري على Android على أنّها INSTALLED، يمكنك الوصول إلى الرمز والموارد باستخدام Context محدَّث للتطبيق. لا يحتوي السياق الذي ينشئه تطبيقك قبل تثبيت وحدة (على سبيل المثال، السياق المخزَّن في متغير) على محتوى الوحدة الجديدة. لكنّ السياق الجديد يتيح ذلك، ويمكن الحصول عليه مثلاً باستخدام 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 (المستوى 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 Feature Delivery لتنزيلها عند الطلب. وتشبه هذه العملية عملية تنزيل وحدة ميزات، كما هو موضّح أدناه.

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 Feature Delivery Library حِزم APK التي تم نقلها إلى وحدة التخزين المحلية للجهاز.bundletool

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

لمحاكاة عمليات تثبيت الوحدات من &quot;متجر Play&quot;، تستخدم مكتبة &quot;عرض الميزات في Play&quot; بديلاً عن 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);