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

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

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

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

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

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

إعداد وحدة جديدة للتسليم عند الطلب

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

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

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

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

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

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

      <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 المثبَّتة التي تخصّ تطبيقك. لمزيد من المعلومات، يُرجى الاطّلاع على المقالة إدارة تحديثات التطبيقات.

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

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

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

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

سعة التخزين غير متوفرة لا تتوفّر مساحة تخزين مجانية كافية على الجهاز لتثبيت وحدة الميزات. أبلِغ المستخدم بأنّه لا تتوفّر لديه مساحة تخزين كافية لتثبيت هذه الميزة.
SPLITCOMPAT_VERIFICATION_ERROR، وSPLITCOMPAT_EMULATION_ERROR، وSPLITCOMPAT_COPY_ERROR تعذّر على spCompat تحميل وحدة الميزات. ومن المفترض أن يتم حلّ هذه الأخطاء تلقائيًا بعد إعادة تشغيل التطبيق في المرة التالية.
متجر Play لم يتم العثور عليه تطبيق "متجر 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() لتعديل واجهة المستخدم (اطّلِع على نموذج الرمز أعلى هذا الجدول).
تمّ تنزيله. تم تنزيل الوحدة على الجهاز ولكن لم يبدأ التثبيت بعد. ويجب أن تفعِّل التطبيقات SegmentCompat للوصول إلى الوحدات التي تم تنزيلها وتجنُّب هذه الحالة. وهذا الإجراء مطلوب للوصول إلى رمز وحدة الميزات ومواردها.
جارٍ التثبيت يتم حاليًا تثبيت الوحدة على الجهاز. عدِّل شريط التقدّم. عادةً ما تكون هذه الولاية قصيرة.
تم التثبيت يتم تثبيت الوحدة على الجهاز. رمز الوصول والمورد في الوحدة لمواصلة رحلة المستخدم.

إذا كانت الوحدة خاصة بتطبيق 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 لكل من تطبيقك ولكل نشاط في وحدات الميزات التي ينزلها تطبيقك.

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

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

تفعيل splitCompat

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

بعد تفعيل spCompat في تطبيقك، عليك أيضًا تفعيل المصدر

يُرجى تعريف splitCompatApplication في البيان.

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

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

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

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

يمكنك أيضًا تفعيل splitCompat في أنشطة أو خدمات معينة أثناء التشغيل. ويجب تفعيل SegmentCompat بهذه الطريقة لبدء الأنشطة المضمَّنة في وحدات الميزات. ولإجراء ذلك، يمكنك إلغاء 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);
    }
}

تفعيل spCompat للأنشطة المتعلّقة بالوحدات

بعد تفعيل المصدر لإجراء ذلك، استخدِم الطريقة SplitCompat.installActivity() كما يلي:

Kotlin

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

Java

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

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

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

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

Kotlin

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

Java

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

المعلمة الأولى لـ setClassName هي اسم حزمة التطبيق والمعلمة الثانية هي اسم فئة النشاط الكامل.

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

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

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

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، بنقل الغرض من تطبيق المتصل. وإذا لم تكن الوحدة متوفرة، يمكن للمكوِّن تنزيل الوحدة أو عرض رسالة خطأ مناسبة إلى تطبيق المتصل.

رمز الدخول والموارد من الوحدات المثبَّتة

في حال تفعيل المصدر

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Kotlin

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

Java

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

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

عند طلب وحدة عند الطلب لتطبيق Android الفوري على نظام 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 وتقسيمات حسب مستوى واجهة برمجة تطبيقات Android للمستوى 28 والأدنى.
  • لا يمكنك تخزين عناصر Android ApplicationInfo أو محتواها أو العناصر التي تحتوي عليها داخل تطبيقك في ذاكرة التخزين المؤقت. وعليك جلب هذه العناصر دائمًا حسب الحاجة من خلال سياق التطبيق. قد يؤدي التخزين المؤقت لهذه العناصر إلى تعطُّل التطبيق عند تثبيت وحدة ميزات.

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

للتحقّق من وحدات الميزات المثبَّتة حاليًا على الجهاز، يمكنك طلب 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 معرفة استخدام حِزم 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);