پیکربندی تحویل درخواستی

ماژول های ویژگی به شما این امکان را می دهند که ویژگی ها و منابع خاصی را از ماژول پایه برنامه خود جدا کرده و در بسته نرم افزاری خود قرار دهید. برای مثال، از طریق Play Feature Delivery، کاربران می‌توانند بعداً پس از اینکه APK پایه برنامه شما را نصب کرده‌اند، آن مؤلفه‌ها را برحسب تقاضا دانلود و نصب کنند.

به عنوان مثال، یک برنامه پیام متنی را در نظر بگیرید که شامل قابلیت ضبط و ارسال پیام های تصویری است، اما تنها درصد کمی از کاربران پیام های تصویری ارسال می کنند. ممکن است منطقی باشد که پیام‌رسانی تصویری را به عنوان یک ماژول ویژگی قابل دانلود اضافه کنید. به این ترتیب، دانلود اولیه برنامه برای همه کاربران کوچک‌تر است و فقط کاربرانی که پیام‌های تصویری ارسال می‌کنند باید آن مؤلفه اضافی را دانلود کنند.

به خاطر داشته باشید، این نوع ماژولارسازی به تلاش بیشتری نیاز دارد و احتمالاً کدهای موجود برنامه شما را تغییر می‌دهد، بنابراین به دقت در نظر بگیرید که کدام یک از ویژگی‌های برنامه شما از در دسترس قرار گرفتن کاربران در صورت تقاضا بیشترین سود را خواهد داشت. برای درک بهتر موارد استفاده بهینه و دستورالعمل‌های ویژگی‌های درخواستی، بهترین شیوه‌های UX برای تحویل در صورت تقاضا را بخوانید.

اگر می‌خواهید به تدریج ویژگی‌های برنامه را در طول زمان، بدون فعال کردن گزینه‌های تحویل پیشرفته، مانند تحویل در صورت تقاضا، مدولار کنید، در عوض تحویل زمان نصب را پیکربندی کنید .

این صفحه به شما کمک می کند یک ماژول ویژگی را به پروژه برنامه خود اضافه کنید و آن را برای تحویل درخواستی پیکربندی کنید. قبل از شروع، مطمئن شوید که از Android Studio نسخه 3.5 یا بالاتر و Android Gradle Plugin نسخه 3.5.0 یا بالاتر استفاده می کنید.

یک ماژول جدید برای تحویل در صورت تقاضا پیکربندی کنید

ساده ترین راه برای ایجاد یک ماژول ویژگی جدید استفاده از Android Studio 3.5 یا بالاتر است. از آنجایی که ماژول های ویژگی وابستگی ذاتی به ماژول برنامه پایه دارند، می توانید آنها را فقط به پروژه های برنامه موجود اضافه کنید.

برای افزودن یک ماژول ویژگی به پروژه برنامه خود با استفاده از Android Studio، به صورت زیر عمل کنید:

  1. اگر قبلاً این کار را نکرده اید، پروژه برنامه خود را در IDE باز کنید.
  2. File > New > New Module را از نوار منو انتخاب کنید.
  3. در گفتگوی Create New Module ، Dynamic Feature Module را انتخاب کرده و روی Next کلیک کنید.
  4. در بخش پیکربندی ماژول جدید ، موارد زیر را تکمیل کنید:
    1. ماژول برنامه کاربردی پایه را برای پروژه برنامه خود از منوی کشویی انتخاب کنید.
    2. نام ماژول را مشخص کنید. IDE از این نام برای شناسایی ماژول به عنوان یک پروژه فرعی Gradle در فایل تنظیمات Gradle شما استفاده می کند. هنگامی که بسته برنامه خود را می سازید، Gradle از آخرین عنصر نام پروژه فرعی برای تزریق ویژگی <manifest split> در مانیفست ماژول ویژگی استفاده می کند.
    3. نام بسته ماژول را مشخص کنید. به طور پیش‌فرض، اندروید استودیو نام بسته‌ای را پیشنهاد می‌کند که نام بسته ریشه ماژول پایه و نام ماژولی را که در مرحله قبل مشخص کرده‌اید ترکیب می‌کند.
    4. حداقل سطح API را که می خواهید ماژول از آن پشتیبانی کند، انتخاب کنید. این مقدار باید با ماژول پایه مطابقت داشته باشد.
  5. روی Next کلیک کنید.
  6. در قسمت گزینه های دانلود ماژول موارد زیر را تکمیل کنید:

    1. عنوان ماژول را با حداکثر 50 کاراکتر مشخص کنید. این پلتفرم از این عنوان برای شناسایی ماژول برای کاربران استفاده می‌کند، مثلاً زمانی که تأیید می‌کند کاربر می‌خواهد ماژول را دانلود کند یا خیر. به همین دلیل، ماژول پایه برنامه شما باید عنوان ماژول را به عنوان منبع رشته ای داشته باشد که می توانید آن را ترجمه کنید. هنگام ایجاد ماژول با استفاده از Android Studio، IDE منبع رشته را به ماژول پایه برای شما اضافه می کند و ورودی زیر را در مانیفست ماژول ویژگی تزریق می کند:

      <dist:module
          ...
          dist:title="@string/feature_title">
      </dist:module>
      
    2. در منوی کرکره‌ای زیر Install-time inclusion ، ماژول در زمان نصب شامل نشود را انتخاب کنید. Android Studio موارد زیر را در مانیفست ماژول تزریق می کند تا انتخاب شما را منعکس کند:

      <dist:module ... >
        <dist:delivery>
            <dist:on-demand/>
        </dist:delivery>
      </dist:module>
      
    3. اگر می‌خواهید این ماژول برای دستگاه‌های دارای Android 4.4 (سطح API 20) و پایین‌تر در دسترس باشد و در چند APK گنجانده شود، کادر کنار Fusing را علامت بزنید. این بدان معناست که می‌توانید رفتار درخواستی را برای این ماژول فعال کنید و فیوزینگ را غیرفعال کنید تا آن را از دستگاه‌هایی که از دانلود و نصب فایل‌های APK تقسیم‌شده پشتیبانی نمی‌کنند حذف کنید. Android Studio موارد زیر را در مانیفست ماژول تزریق می کند تا انتخاب شما را منعکس کند:

      <dist:module ...>
          <dist:fusing dist:include="true | false" />
      </dist:module>
      
  7. روی Finish کلیک کنید.

پس از اتمام ساخت ماژول اندروید استودیو، محتویات آن را خودتان از پنجره Project بررسی کنید ( مشاهده > ابزار ویندوز > پروژه را از نوار منو انتخاب کنید). کد، منابع و سازمان پیش‌فرض باید مشابه ماژول برنامه استاندارد باشد.

در مرحله بعد، باید قابلیت نصب بر اساس تقاضا را با استفاده از کتابخانه Play Feature Delivery پیاده سازی کنید.

کتابخانه تحویل ویژگی Play را در پروژه خود قرار دهید

قبل از شروع، ابتدا باید کتابخانه تحویل ویژگی Play را به پروژه خود اضافه کنید .

ماژول درخواستی را درخواست کنید

هنگامی که برنامه شما نیاز به استفاده از یک ماژول ویژگی دارد، می‌تواند زمانی که در پیش‌زمینه است، از طریق کلاس SplitInstallManager درخواست کند. هنگام درخواست، برنامه شما باید نام ماژول را همانطور که توسط عنصر split در مانیفست ماژول هدف تعریف شده است، مشخص کند. هنگامی که یک ماژول ویژگی را با استفاده از Android Studio ایجاد می کنید ، سیستم ساخت از نام ماژول شما برای تزریق این ویژگی به مانیفست ماژول در زمان کامپایل استفاده می کند. برای اطلاعات بیشتر، درباره مانیفست های ماژول ویژگی بخوانید.

به عنوان مثال، برنامه ای را در نظر بگیرید که دارای یک ماژول درخواستی برای گرفتن و ارسال پیام های تصویری با استفاده از دوربین دستگاه است و این ماژول درخواستی split="pictureMessages" را در مانیفست خود مشخص می کند. نمونه زیر از SplitInstallManager برای درخواست ماژول pictureMessages (همراه با یک ماژول اضافی برای برخی از فیلترهای تبلیغاتی) استفاده می کند:

کاتلین

// 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 ->  ... }

جاوا

// 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 از استراتژی «آتش و فراموش کردن» استفاده می‌کند. یعنی درخواست دانلود ماژول را به پلتفرم ارسال می کند، اما نظارت نمی کند که آیا نصب با موفقیت انجام شده است یا خیر. برای پیشبرد سفر کاربر پس از نصب یا رسیدگی به خطاها، مطمئن شوید که وضعیت درخواست را زیر نظر دارید.

توجه: اشکالی ندارد که ماژول ویژگی را که قبلاً روی دستگاه نصب شده است درخواست کنید. اگر تشخیص دهد که ماژول قبلاً نصب شده است، API فوراً درخواست را تکمیل شده در نظر می گیرد. علاوه بر این، پس از نصب یک ماژول، Google Play آن را به طور خودکار به روز نگه می دارد. یعنی وقتی نسخه جدیدی از بسته برنامه خود را آپلود می کنید، پلتفرم تمام APK های نصب شده متعلق به برنامه شما را به روز می کند. برای اطلاعات بیشتر، مدیریت به‌روزرسانی‌های برنامه را بخوانید.

برای دسترسی به کد و منابع ماژول، برنامه شما باید SplitCompat را فعال کند . توجه داشته باشید که SplitCompat برای برنامه های فوری اندروید مورد نیاز نیست.

نصب ماژول های درخواستی را به تعویق بیندازید

اگر نیازی ندارید برنامه خود را فوراً یک ماژول درخواستی دانلود و نصب کنید، می‌توانید نصب را برای زمانی که برنامه در پس‌زمینه است به تعویق بیندازید. برای مثال، اگر می‌خواهید برخی از مواد تبلیغاتی را برای راه‌اندازی بعدی برنامه خود از قبل بارگیری کنید.

می‌توانید با استفاده از متد deferredInstall() یک ماژول را مشخص کنید که بعداً دانلود شود، همانطور که در زیر نشان داده شده است. و بر خلاف SplitInstallManager.startInstall() ، برنامه شما برای شروع درخواست نصب معوق نیازی به در پیش زمینه بودن ندارد.

کاتلین

// 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"))

جاوا

// 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() برای درخواست آن استفاده کنید، همانطور که در بخش قبل نشان داده شده است.

وضعیت درخواست را نظارت کنید

برای اینکه بتوانید نوار پیشرفت را به‌روزرسانی کنید، پس از نصب یک intent اجرا کنید، یا به‌خوبی خطای درخواست را مدیریت کنید، باید به‌روزرسانی‌های وضعیت را از وظیفه SplitInstallManager.startInstall() ناهمزمان گوش کنید. قبل از اینکه بتوانید به‌روزرسانی‌ها را برای درخواست نصب خود دریافت کنید، یک شنونده ثبت کنید و شناسه جلسه درخواست را دریافت کنید، همانطور که در زیر نشان داده شده است.

کاتلین

// 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)

جاوا

// 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 باشد. برای پیشنهاداتی در مورد نحوه رسیدگی به این موقعیت‌ها از دیدگاه کاربر، دستورالعمل‌های UX ما را برای تحویل درخواستی بررسی کنید.

از نظر کد، شما باید در دانلود یا نصب یک ماژول با استفاده از addOnFailureListener() با شکست مواجه شوید، همانطور که در زیر نشان داده شده است:

کاتلین

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.
                    }
                }
            }
        }
}

جاوا

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 در دستگاه فعلی پشتیبانی نمی‌شود. یعنی دستگاه قادر به دانلود و نصب ویژگی ها در صورت تقاضا نیست. برای دستگاه‌های دارای Android 4.4 (سطح API 20) یا پایین‌تر، باید ماژول‌های ویژگی را در زمان نصب با استفاده از ویژگی dist:fusing manifest اضافه کنید. برای کسب اطلاعات بیشتر، درباره مانیفست ماژول ویژگی بخوانید.
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 Store روی دستگاه نصب نشده است. به کاربر اطلاع دهید که برای دانلود این ویژگی به برنامه Play Store نیاز است.
APP_NOT_OWNED این برنامه توسط گوگل پلی نصب نشده است و این ویژگی قابل دانلود نیست. این خطا فقط برای نصب های معوق رخ می دهد. اگر می‌خواهید کاربر برنامه را در Google Play دریافت کند، از startInstall() استفاده کنید که می‌تواند تأییدیه کاربر لازم را دریافت کند.
INTERNAL_ERROR یک خطای داخلی در فروشگاه Play رخ داد. درخواست را دوباره امتحان کنید.

اگر کاربر درخواست دانلود یک ماژول درخواستی را دارد و خطایی رخ می‌دهد، در نظر بگیرید که گفتگویی را نمایش دهید که دو گزینه برای کاربر فراهم می‌کند: دوباره امتحان کنید (که دوباره درخواست را انجام می‌دهد) و لغو (که درخواست را رها می‌کند). برای پشتیبانی بیشتر، باید پیوند راهنما را نیز ارائه دهید که کاربران را به مرکز راهنمای Google Play هدایت کند.

به روز رسانی های ایالت را مدیریت کنید

پس از اینکه شنونده ای را ثبت کردید و شناسه جلسه را برای درخواست خود ضبط کردید، از StateUpdatedListener.onStateUpdate() برای مدیریت تغییرات حالت، مانند شکل زیر استفاده کنید.

کاتلین

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.
            }
        }
    }
}

جاوا

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

            case SplitInstallSessionStatus.INSTALLED:

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

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

حالت های احتمالی برای درخواست نصب شما در جدول زیر توضیح داده شده است.

حالت درخواست توضیحات اقدام پیشنهادی
در انتظار درخواست پذیرفته شد و دانلود باید به زودی شروع شود. برای ارائه بازخورد کاربر در مورد دانلود، مؤلفه‌های رابط کاربری، مانند نوار پیشرفت، را راه‌اندازی کنید.
REQUIRES_USER_CONFIRMATION دانلود نیاز به تایید کاربر دارد. معمولاً این وضعیت زمانی رخ می دهد که برنامه از طریق Google Play نصب نشده باشد. از کاربر بخواهید که دانلود ویژگی را از طریق Google Play تأیید کند. برای کسب اطلاعات بیشتر، به بخش نحوه دریافت تأییدیه کاربر بروید.
در حال دانلود دانلود در حال انجام است. اگر نوار پیشرفت برای دانلود ارائه می کنید، از روش های SplitInstallSessionState.bytesDownloaded() و SplitInstallSessionState.totalBytesToDownload() برای به روز رسانی رابط کاربری استفاده کنید (نمونه کد بالا را در این جدول ببینید).
دانلود شد دستگاه ماژول را دانلود کرده است اما نصب هنوز شروع نشده است. برنامه‌ها باید SplitCompat را فعال کنند تا به ماژول‌های دانلود شده دسترسی داشته باشند و از دیدن این وضعیت اجتناب کنند. این برای دسترسی به کد و منابع ماژول ویژگی مورد نیاز است.
در حال نصب دستگاه در حال نصب ماژول است. نوار پیشرفت را به روز کنید. این حالت معمولا کوتاه است.
نصب شده است ماژول بر روی دستگاه نصب شده است. برای ادامه سفر کاربر به کد و منبع در ماژول دسترسی پیدا کنید .

اگر این ماژول برای یک برنامه فوری Android است که روی Android 8.0 (سطح API 26) یا بالاتر اجرا می‌شود، باید از splitInstallHelper برای به‌روزرسانی اجزای برنامه با ماژول جدید استفاده کنید.

ناموفق درخواست قبل از نصب ماژول روی دستگاه انجام نشد. از کاربر بخواهید درخواست را دوباره امتحان کند یا آن را لغو کند.
لغو دستگاه در حال لغو درخواست است. برای کسب اطلاعات بیشتر، به بخش نحوه لغو درخواست نصب بروید.
لغو شد درخواست لغو شده است.

دریافت تاییدیه کاربر

در برخی موارد، Google Play ممکن است قبل از برآورده کردن درخواست دانلود، به تأیید کاربر نیاز داشته باشد. به عنوان مثال، اگر برنامه شما توسط Google Play نصب نشده باشد یا اگر در حال تلاش برای دانلود بزرگ از طریق داده تلفن همراه هستید. در چنین مواردی، وضعیت درخواست REQUIRES_USER_CONFIRMATION را گزارش می‌دهد و قبل از اینکه دستگاه بتواند ماژول‌های موجود در درخواست را دانلود و نصب کند، برنامه شما باید تأییدیه کاربر را دریافت کند. برای دریافت تأیید، برنامه شما باید به صورت زیر از کاربر درخواست کند:

کاتلین

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

جاوا

@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 داخلی، یک راه‌انداز نتیجه فعالیت ثبت کنید. APIهای نتیجه فعالیت را ببینید.

وضعیت درخواست بسته به پاسخ کاربر به روز می شود:

  • اگر کاربر تاییدیه را بپذیرد، وضعیت درخواست به PENDING تغییر می‌کند و دانلود ادامه می‌یابد.
  • اگر کاربر تأیید را رد کند، وضعیت درخواست به CANCELED تغییر می کند.
  • اگر کاربر انتخابی را قبل از از بین رفتن گفتگو انجام ندهد، وضعیت درخواست به صورت REQUIRES_USER_CONFIRMATION باقی می ماند. برنامه شما می تواند دوباره از کاربر بخواهد که درخواست را تکمیل کند.

برای دریافت پاسخ به تماس با پاسخ کاربر، می‌توانید ActivityResultCallback را مطابق شکل زیر لغو کنید.

کاتلین

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.
    }
}

جاوا

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() را با استفاده از شناسه جلسه درخواست، مطابق شکل زیر فراخوانی کند.

کاتلین

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

جاوا

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 را برای برنامه شما فعال کند، همانطور که در زیر نشان داده شده است:

کاتلین

class MyApplication : SplitCompatApplication() {
    ...
}

جاوا

public class MyApplication extends SplitCompatApplication {
    ...
}

SplitCompatApplication به سادگی ContextWrapper.attachBaseContext() را نادیده می گیرد تا SplitCompat.install(Context applicationContext) شامل شود. اگر نمی‌خواهید کلاس Application شما SplitCompatApplication گسترش دهد، می‌توانید به صورت دستی متد attachBaseContext() را به صورت زیر لغو کنید:

کاتلین

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

جاوا

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

اگر ماژول درخواستی شما هم با برنامه های فوری و هم با برنامه های نصب شده سازگار است، می توانید SplitCompat را به صورت مشروط فراخوانی کنید، به شرح زیر:

کاتلین

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

جاوا

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

SplitCompat را برای فعالیت های ماژول فعال کنید

پس از اینکه SplitCompat را برای برنامه پایه خود فعال کردید، باید SplitCompat را برای هر فعالیتی که برنامه شما در یک ماژول ویژگی دانلود می کند، فعال کنید. برای انجام این کار، از متد SplitCompat.installActivity() به صورت زیر استفاده کنید:

کاتلین

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

جاوا

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

دسترسی به اجزای تعریف شده در ماژول های ویژگی

یک فعالیت تعریف شده در یک ماژول ویژگی را شروع کنید

می توانید پس از فعال کردن SplitCompat، فعالیت های تعریف شده در ماژول های ویژگی را با استفاده از startActivity() اجرا کنید.

کاتلین

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

جاوا

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

پارامتر اول setClassName نام بسته برنامه و پارامتر دوم نام کامل کلاس فعالیت است.

وقتی فعالیتی در یک ماژول ویژگی دارید که به صورت درخواستی دانلود کرده اید، باید SplitCompat را در فعالیت فعال کنید .

یک سرویس تعریف شده در یک ماژول ویژگی را شروع کنید

می توانید خدمات تعریف شده در ماژول های ویژگی را با استفاده از startService() پس از فعال کردن SplitCompat راه اندازی کنید.

کاتلین

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

جاوا

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

یک جزء تعریف شده در یک ماژول ویژگی را صادر کنید

شما نباید اجزای اندروید صادر شده را در ماژول های اختیاری قرار دهید.

سیستم ساخت، ورودی های مانیفست را برای همه ماژول ها در ماژول پایه ادغام می کند. اگر یک ماژول اختیاری حاوی یک مؤلفه صادر شده باشد، حتی قبل از نصب ماژول قابل دسترسی خواهد بود و در صورت فراخوانی از یک برنامه دیگر می‌تواند به دلیل از دست دادن کد باعث خرابی شود.

این مشکلی برای اجزای داخلی نیست. آنها فقط توسط برنامه قابل دسترسی هستند، بنابراین برنامه می تواند قبل از دسترسی به مؤلفه بررسی کند که ماژول نصب شده است .

اگر به یک جزء صادراتی نیاز دارید و می‌خواهید محتوای آن در یک ماژول اختیاری باشد، یک الگوی پروکسی را در نظر بگیرید. شما می توانید این کار را با افزودن یک جزء صادر شده پروکسی در پایه انجام دهید. در صورت دسترسی، مولفه پروکسی می تواند وجود ماژول حاوی محتوا را بررسی کند. اگر ماژول موجود باشد، مؤلفه پراکسی می‌تواند مؤلفه داخلی را از طریق یک Intent از ماژول شروع کند و هدف را از برنامه تماس گیرنده منتقل کند. اگر ماژول موجود نباشد، مؤلفه می تواند ماژول را دانلود کند یا یک پیام خطای مناسب را به برنامه تماس گیرنده برگرداند.

دسترسی به کد و منابع از ماژول های نصب شده

اگر SplitCompat را برای زمینه برنامه پایه خود و فعالیت‌های ماژول ویژگی خود فعال کنید، می‌توانید از کد و منابع یک ماژول ویژگی استفاده کنید که گویی بخشی از APK پایه است، پس از نصب ماژول اختیاری.

کد دسترسی از یک ماژول دیگر

دسترسی به کد پایه از یک ماژول

کدی که در داخل ماژول پایه شما قرار دارد می تواند مستقیماً توسط ماژول های دیگر استفاده شود. شما نیازی به انجام کار خاصی ندارید. فقط وارد کنید و از کلاس های مورد نیاز خود استفاده کنید.

از ماژول دیگری به کد ماژول دسترسی پیدا کنید

یک شی یا کلاس در داخل یک ماژول را نمی توان مستقیماً از ماژول دیگری به طور ایستا دسترسی داشت، اما می توان به طور غیر مستقیم و با استفاده از بازتاب به آن دسترسی داشت.

با توجه به هزینه های عملکردی بازتاب، باید مراقب باشید که این چند وقت یکبار اتفاق می افتد. برای موارد استفاده پیچیده، از چارچوب‌های تزریق وابستگی مانند Dagger 2 برای تضمین یک تماس بازتابی واحد در طول عمر برنامه استفاده کنید.

برای ساده سازی تعاملات با شی پس از نمونه سازی، توصیه می شود یک رابط در ماژول پایه و پیاده سازی آن در ماژول ویژگی تعریف شود. به عنوان مثال:

کاتلین

// 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();

جاوا

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

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

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

دسترسی به منابع و دارایی ها از یک ماژول دیگر

هنگامی که یک ماژول نصب می شود، می توانید به منابع و دارایی های درون ماژول به روش استاندارد و با دو اخطار دسترسی داشته باشید:

  • اگر از ماژول دیگری به منبعی دسترسی دارید، ماژول به شناسه منبع دسترسی نخواهد داشت، اگرچه هنوز می توان به منبع با نام دسترسی داشت. توجه داشته باشید که بسته مورد استفاده برای ارجاع به منبع، بسته ماژولی است که منبع در آن تعریف شده است.
  • اگر می‌خواهید به دارایی‌ها یا منابعی که در یک ماژول تازه نصب شده از یک ماژول نصب‌شده دیگر در برنامه‌تان وجود دارد، دسترسی داشته باشید، باید این کار را با استفاده از زمینه برنامه انجام دهید. زمینه مولفه ای که سعی در دسترسی به منابع دارد هنوز به روز نمی شود. همچنین، می‌توانید آن مؤلفه را دوباره ایجاد کنید (مثلاً فراخوانی Activity.recreate() ) یا پس از نصب ماژول ویژگی، SplitCompat را مجدداً روی آن نصب کنید.

با استفاده از تحویل درخواستی، کد بومی را در یک برنامه بارگیری کنید

توصیه می‌کنیم از ReLinker برای بارگیری تمام کتابخانه‌های بومی خود هنگام استفاده از تحویل درخواستی ماژول‌های ویژگی استفاده کنید. ReLinker مشکل بارگیری کتابخانه های بومی را پس از نصب ماژول ویژگی برطرف می کند. می‌توانید درباره ReLinker در نکات Android JNI اطلاعات بیشتری کسب کنید.

کد بومی را از یک ماژول اختیاری بارگیری کنید

پس از نصب یک تقسیم، توصیه می کنیم کد اصلی آن را از طریق ReLinker بارگیری کنید. برای برنامه های فوری باید از این روش ویژه استفاده کنید.

اگر از System.loadLibrary() برای بارگیری کد بومی خود استفاده می کنید و کتابخانه بومی شما به کتابخانه دیگری در ماژول وابسته است، باید ابتدا آن کتابخانه دیگر را به صورت دستی بارگیری کنید. اگر از ReLinker استفاده می کنید، عملیات معادل Relinker.recursively().loadLibrary() است.

اگر از dlopen() در کد بومی برای بارگذاری یک کتابخانه تعریف شده در یک ماژول اختیاری استفاده می کنید، با مسیرهای کتابخانه نسبی کار نخواهد کرد. بهترین راه حل این است که مسیر مطلق کتابخانه را از کد جاوا از طریق ClassLoader.findLibrary() بازیابی کنید و سپس از آن در فراخوانی dlopen() خود استفاده کنید. این کار را قبل از وارد کردن کد بومی انجام دهید یا از یک فراخوانی JNI از کد بومی خود به جاوا استفاده کنید.

به برنامه‌های Instant Android نصب شده دسترسی داشته باشید

پس از گزارش ماژول برنامه فوری Android به‌عنوان INSTALLED ، می‌توانید با استفاده از زمینه برنامه تازه‌سازی شده به کد و منابع آن دسترسی پیدا کنید. زمینه‌ای که برنامه شما قبل از نصب یک ماژول ایجاد می‌کند (مثلاً موردی که قبلاً در یک متغیر ذخیره شده است) حاوی محتوای ماژول جدید نیست. اما یک زمینه جدید انجام می‌شود—این را می‌توان، برای مثال، با استفاده از createPackageContext به دست آورد.

کاتلین

// 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
            }
        }
    }
}

جاوا

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

برنامه‌های فوری اندروید در اندروید ۸.۰ و بالاتر

هنگام درخواست ماژول درخواستی برای برنامه Instant Android در Android 8.0 (سطح API 26) و بالاتر، پس از گزارش درخواست نصب به عنوان INSTALLED ، باید از طریق تماس با SplitInstallHelper.updateAppInfo(Context context) برنامه را با زمینه ماژول جدید به روز کنید. در غیر این صورت، برنامه هنوز از کد و منابع ماژول آگاه نیست. پس از به‌روزرسانی فراداده برنامه، باید محتویات ماژول را در طول رویداد رشته اصلی بعدی با فراخوانی یک Handler جدید بارگیری کنید، همانطور که در زیر نشان داده شده است:

کاتلین

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
                        ...
                    }
                }
            }
        }
    }
}

جاوا

@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++ را از ماژولی که دستگاه قبلاً در برنامه Instant دانلود کرده است بارگیری کنید، از SplitInstallHelper.loadLibrary(Context context, String libName) استفاده کنید، همانطور که در زیر نشان داده شده است:

کاتلین

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

جاوا

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 در Android API سطح 28 و پایین تر است.
  • نمی‌توانید اشیاء Android ApplicationInfo ، محتویات آن‌ها یا اشیایی که آنها را در برنامه خود دارند، کش کنید. همیشه باید این اشیاء را در صورت نیاز از یک زمینه برنامه واکشی کنید. ذخیره چنین اشیایی می تواند باعث از کار افتادن برنامه در هنگام نصب یک ماژول ویژگی شود.

مدیریت ماژول های نصب شده

برای بررسی اینکه کدام یک از ماژول‌های ویژگی در حال حاضر روی دستگاه نصب شده‌اند، می‌توانید SplitInstallManager.getInstalledModules() را فراخوانی کنید که یک Set<String> از نام ماژول‌های نصب‌شده را مطابق شکل زیر برمی‌گرداند.

کاتلین

val installedModules: Set<String> = splitInstallManager.installedModules

جاوا

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

ماژول ها را حذف کنید

می‌توانید با فراخوانی SplitInstallManager.deferredUninstall(List<String> moduleNames) از دستگاه درخواست کنید که ماژول‌ها را حذف نصب کند، همانطور که در زیر نشان داده شده است.

کاتلین

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

جاوا

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

حذف نصب ماژول بلافاصله انجام نمی شود. یعنی دستگاه در صورت نیاز آنها را در پس زمینه حذف نصب می کند تا فضای ذخیره سازی ذخیره شود. همانطور که در بخش قبل توضیح داده شد، می توانید تأیید کنید که دستگاه یک ماژول را با فراخوانی SplitInstallManager.getInstalledModules() و بررسی نتیجه حذف کرده است.

منابع زبان اضافی را دانلود کنید

با بسته‌های برنامه، دستگاه‌ها فقط کد و منابعی را که برای اجرای برنامه شما نیاز دارند دانلود می‌کنند. بنابراین، برای منابع زبان، دستگاه کاربر فقط منابع زبان برنامه شما را دانلود می‌کند که با یک یا چند زبان در حال حاضر انتخاب شده در تنظیمات دستگاه مطابقت دارد.

اگر می‌خواهید برنامه شما به منابع زبان اضافی دسترسی داشته باشد—مثلاً برای پیاده‌سازی انتخابگر زبان درون‌برنامه، می‌توانید از کتابخانه تحویل ویژگی Play برای دانلود آن‌ها در صورت درخواست استفاده کنید. فرآیند مشابه دانلود یک ماژول ویژگی است، همانطور که در زیر نشان داده شده است.

کاتلین

// 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)

جاوا

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

درخواست به گونه ای انجام می شود که گویی درخواستی برای یک ماژول ویژگی است. یعنی می‌توانید وضعیت درخواست را مانند حالت عادی نظارت کنید .

اگر برنامه شما فوراً به منابع زبان اضافی نیاز ندارد، می‌توانید نصب را برای زمانی که برنامه در پس‌زمینه است، موکول کنید، همانطور که در زیر نشان داده شده است.

کاتلین

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

جاوا

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

به منابع زبان دانلود شده دسترسی داشته باشید

برای دسترسی به منابع زبان دانلود شده، برنامه شما باید روش SplitCompat.installActivity() را در متد attachBaseContext() هر فعالیتی که نیاز به دسترسی به آن منابع دارد اجرا کند، همانطور که در زیر نشان داده شده است.

کاتلین

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

جاوا

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

برای هر فعالیتی که می‌خواهید از منابع زبانی که برنامه‌تان دانلود کرده است استفاده کنید، زمینه پایه را به‌روزرسانی کنید و از طریق Configuration آن یک منطقه محلی جدید تنظیم کنید:

کاتلین

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

جاوا

@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() استفاده کنید.

کاتلین

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

جاوا

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

منابع زبان اضافی را حذف نصب کنید

مشابه ماژول های ویژگی، می توانید منابع اضافی را در هر زمان حذف نصب کنید. قبل از درخواست حذف، ممکن است بخواهید ابتدا تعیین کنید که کدام زبان ها در حال حاضر نصب شده اند، به شرح زیر.

کاتلین

val installedLanguages: Set<String> = splitInstallManager.installedLanguages

جاوا

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

سپس می توانید تصمیم بگیرید که کدام زبان ها را با استفاده از متد deferredLanguageUninstall() حذف نصب کنید، همانطور که در زیر نشان داده شده است.

کاتلین

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

جاوا

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

ماژول تست محلی نصب می شود

کتابخانه تحویل ویژگی Play به شما امکان می‌دهد بدون اتصال به فروشگاه Play، توانایی برنامه خود را برای انجام کارهای زیر به صورت محلی آزمایش کنید:

این صفحه نحوه استقرار فایل‌های APK تقسیم‌شده برنامه‌تان را در دستگاه آزمایشی خود توضیح می‌دهد تا «تحویل ویژگی Play» به‌طور خودکار از آن APKها برای شبیه‌سازی درخواست، دانلود و نصب ماژول‌ها از فروشگاه Play استفاده کند.

اگرچه نیازی به ایجاد هیچ تغییری در منطق برنامه خود ندارید، اما باید شرایط زیر را رعایت کنید:

  • آخرین نسخه bundletool را دانلود و نصب کنید. برای ایجاد مجموعه جدیدی از APKهای قابل نصب از بسته برنامه خود، به bundletool نیاز دارید.

مجموعه ای از APK بسازید

اگر قبلاً این کار را نکرده‌اید، APK‌های تقسیم‌شده برنامه‌تان را به شرح زیر بسازید:

  1. با استفاده از یکی از روش های زیر یک بسته برنامه برای برنامه خود بسازید:
  2. از bundletool برای ایجاد مجموعه‌ای از APK برای همه پیکربندی‌های دستگاه با دستور زیر استفاده کنید:

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

پرچم --local-testing شامل متا داده‌هایی در مانیفست‌های APK شما می‌شود که به کتابخانه تحویل ویژگی Play اجازه می‌دهد بدون اتصال به فروشگاه Play، از APK‌های تقسیم‌شده محلی برای آزمایش نصب ماژول‌های ویژگی استفاده کند.

برنامه خود را روی دستگاه نصب کنید

پس از ساختن مجموعه ای از APK با استفاده از پرچم --local-testing ، از bundletool برای نصب نسخه پایه برنامه خود و انتقال فایل های APK اضافی به حافظه محلی دستگاه خود استفاده کنید. با دستور زیر می توانید هر دو عمل را انجام دهید:

bundletool install-apks --apks my_app.apks

اکنون، وقتی برنامه خود را راه‌اندازی می‌کنید و جریان کاربر را برای دانلود و نصب یک ماژول ویژگی کامل می‌کنید، کتابخانه تحویل ویژگی Play از فایل‌های APK استفاده می‌کند که bundletool به حافظه محلی دستگاه منتقل شده است.

یک خطای شبکه را شبیه سازی کنید

برای شبیه‌سازی نصب‌های ماژول از فروشگاه Play، کتابخانه تحویل ویژگی Play از جایگزینی برای SplitInstallManager به نام FakeSplitInstallManager برای درخواست ماژول استفاده می‌کند. هنگامی که از bundletool با پرچم --local-testing برای ساخت مجموعه‌ای از فایل‌های APK و استقرار آن‌ها در دستگاه آزمایشی خود استفاده می‌کنید، شامل ابرداده‌هایی است که به کتابخانه تحویل ویژگی Play دستور می‌دهد تا به‌جای SplitInstallManager ، تماس‌های API برنامه‌تان را به‌طور خودکار تغییر دهد تا FakeSplitInstallManager را فراخوانی کند.

FakeSplitInstallManager شامل یک پرچم بولین است که می توانید آن را فعال کنید تا یک خطای شبکه را دفعه بعد که برنامه شما درخواست نصب یک ماژول را می دهد، شبیه سازی کند. برای دسترسی به FakeSplitInstallManager در تست های خود، می توانید نمونه ای از آن را با استفاده از FakeSplitInstallManagerFactory دریافت کنید، همانطور که در زیر نشان داده شده است:

کاتلین

// 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)

جاوا

// 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);
،

ماژول های ویژگی به شما این امکان را می دهند که ویژگی ها و منابع خاصی را از ماژول پایه برنامه خود جدا کرده و در بسته نرم افزاری خود قرار دهید. برای مثال، از طریق Play Feature Delivery، کاربران می‌توانند بعداً پس از اینکه APK پایه برنامه شما را نصب کرده‌اند، آن مؤلفه‌ها را برحسب تقاضا دانلود و نصب کنند.

به عنوان مثال، یک برنامه پیام متنی را در نظر بگیرید که شامل قابلیت ضبط و ارسال پیام های تصویری است، اما تنها درصد کمی از کاربران پیام های تصویری ارسال می کنند. ممکن است منطقی باشد که پیام‌رسانی تصویری را به عنوان یک ماژول ویژگی قابل دانلود اضافه کنید. به این ترتیب، دانلود اولیه برنامه برای همه کاربران کوچک‌تر است و فقط کاربرانی که پیام‌های تصویری ارسال می‌کنند باید آن مؤلفه اضافی را دانلود کنند.

به خاطر داشته باشید، این نوع ماژولارسازی به تلاش بیشتری نیاز دارد و احتمالاً کدهای موجود برنامه شما را تغییر می‌دهد، بنابراین به دقت در نظر بگیرید که کدام یک از ویژگی‌های برنامه شما از در دسترس قرار گرفتن کاربران در صورت تقاضا بیشترین سود را خواهد داشت. برای درک بهتر موارد استفاده بهینه و دستورالعمل‌های ویژگی‌های درخواستی، بهترین شیوه‌های UX برای تحویل در صورت تقاضا را بخوانید.

اگر می‌خواهید به تدریج ویژگی‌های برنامه را در طول زمان، بدون فعال کردن گزینه‌های تحویل پیشرفته، مانند تحویل در صورت تقاضا، مدولار کنید، در عوض تحویل زمان نصب را پیکربندی کنید .

این صفحه به شما کمک می کند یک ماژول ویژگی را به پروژه برنامه خود اضافه کنید و آن را برای تحویل درخواستی پیکربندی کنید. قبل از شروع، مطمئن شوید که از Android Studio نسخه 3.5 یا بالاتر و Android Gradle Plugin نسخه 3.5.0 یا بالاتر استفاده می کنید.

یک ماژول جدید برای تحویل در صورت تقاضا پیکربندی کنید

ساده ترین راه برای ایجاد یک ماژول ویژگی جدید استفاده از Android Studio 3.5 یا بالاتر است. از آنجا که ماژول های ویژگی وابستگی ذاتی به ماژول برنامه پایه دارند ، می توانید آنها را فقط به پروژه های برنامه موجود اضافه کنید.

برای افزودن یک ماژول ویژگی به پروژه برنامه خود با استفاده از Android Studio ، به شرح زیر ادامه دهید:

  1. اگر قبلاً این کار را نکرده اید ، پروژه برنامه خود را در IDE باز کنید.
  2. File > New > New Module را از نوار منو انتخاب کنید.
  3. در گفتگوی Create New Module ، Dynamic Feature Module را انتخاب کرده و روی Next کلیک کنید.
  4. در پیکربندی بخش جدید ماژول خود ، موارد زیر را تکمیل کنید:
    1. از منوی کشویی ماژول برنامه پایه را برای پروژه برنامه خود انتخاب کنید.
    2. یک نام ماژول را مشخص کنید. IDE از این نام برای شناسایی ماژول به عنوان یک زیر مجموعه Gradle در پرونده تنظیمات Gradle استفاده می کند. هنگامی که بسته برنامه خود را می سازید ، Gradle از آخرین عنصر نام Subproject برای تزریق ویژگی <manifest split> در مانیفست ماژول ویژگی استفاده می کند.
    3. نام بسته ماژول را مشخص کنید. به طور پیش فرض ، Android Studio یک نام بسته را پیشنهاد می کند که نام بسته اصلی ماژول پایه و نام ماژول را که در مرحله قبل مشخص کرده اید ، ترکیب می کند.
    4. حداقل سطح API را که می خواهید از ماژول پشتیبانی کنید انتخاب کنید. این مقدار باید با ماژول پایه مطابقت داشته باشد.
  5. روی Next کلیک کنید.
  6. در بخش گزینه های بارگیری ماژول ، موارد زیر را کامل کنید:

    1. عنوان ماژول را با استفاده از حداکثر 50 کاراکتر مشخص کنید. این پلتفرم از این عنوان برای شناسایی ماژول برای کاربران استفاده می کند ، به عنوان مثال ، تأیید اینکه آیا کاربر می خواهد ماژول را بارگیری کند یا خیر. به همین دلیل ، ماژول پایه برنامه شما باید عنوان ماژول را به عنوان یک منبع رشته ای درج کند که می توانید ترجمه کنید. هنگام ایجاد ماژول با استفاده از Android Studio ، IDE منبع رشته را به ماژول پایه برای شما اضافه می کند و ورودی زیر را در مانیفست ماژول ویژگی تزریق می کند:

      <dist:module
          ...
          dist:title="@string/feature_title">
      </dist:module>
      
    2. در منوی کشویی در زیر شمول نصب ، ماژول را در زمان نصب قرار ندهید . Android Studio موارد زیر را در مانیفست ماژول تزریق می کند تا انتخاب شما را منعکس کند:

      <dist:module ... >
        <dist:delivery>
            <dist:on-demand/>
        </dist:delivery>
      </dist:module>
      
    3. اگر می خواهید این ماژول در دسترس دستگاه هایی باشد که Android 4.4 (API سطح 20) را در اختیار دارد و در چند اپلیکیشن در دسترس است ، کادر کنار فیوز را بررسی کنید. این بدان معنی است که می توانید رفتار تقاضا را برای این ماژول فعال کنید و فیوژن را غیرفعال کنید تا آن را از دستگاه هایی که از بارگیری و نصب APK های تقسیم شده پشتیبانی نمی کنند ، حذف کنید. Android Studio موارد زیر را در مانیفست ماژول تزریق می کند تا انتخاب شما را منعکس کند:

      <dist:module ...>
          <dist:fusing dist:include="true | false" />
      </dist:module>
      
  7. روی پایان کلیک کنید.

بعد از اتمام Android Studio ایجاد ماژول خود ، محتوای آن را از صفحه Project بازرسی کنید ( مشاهده> ویندوز ابزار> پروژه را از نوار منو انتخاب کنید). کد پیش فرض ، منابع و سازمان باید شبیه به ماژول برنامه استاندارد باشد.

در مرحله بعد ، شما باید با استفاده از کتابخانه تحویل ویژگی Play ، قابلیت نصب On Demand را پیاده سازی کنید.

کتابخانه تحویل ویژگی بازی را در پروژه خود وارد کنید

قبل از شروع کار ، ابتدا باید کتابخانه تحویل ویژگی بازی را به پروژه خود اضافه کنید .

ماژول درخواستی را درخواست کنید

هنگامی که برنامه شما نیاز به استفاده از یک ماژول ویژگی دارد ، می تواند یکی را درخواست کند در حالی که از طریق کلاس SplitInstallManager در پیش زمینه است. هنگام درخواست ، برنامه شما باید نام ماژول را مطابق با عنصر split در مانیفست ماژول هدف مشخص کند. هنگامی که یک ماژول ویژگی را با استفاده از Android Studio ایجاد می کنید ، سیستم ساخت از نام ماژول ای که ارائه می دهید برای تزریق این ویژگی در مانیفست ماژول در زمان کامپایل استفاده می کند. برای اطلاعات بیشتر ، در مورد مانیفست های ماژول ویژگی بخوانید.

به عنوان مثال ، برنامه ای را در نظر بگیرید که دارای یک ماژول On Demand برای ضبط و ارسال پیام های تصویری با استفاده از دوربین دستگاه است ، و این ماژول در تقاضا split="pictureMessages" را در آشکار خود مشخص می کند. نمونه زیر از SplitInstallManager برای درخواست ماژول pictureMessages (به همراه یک ماژول اضافی برای برخی از فیلترهای تبلیغاتی) استفاده می کند:

کاتلین

// 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 ->  ... }

جاوا

// 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 -> { ... });

هنگامی که برنامه شما یک ماژول On Demand را درخواست می کند ، کتابخانه ارائه ویژگی Play از یک استراتژی "آتش و برای همیشه" استفاده می کند. یعنی درخواست بارگیری ماژول را به سیستم عامل ارسال می کند ، اما نظارت بر این ندارد که آیا نصب موفق شده است یا خیر. برای حرکت کاربر به جلو پس از نصب یا رسیدگی به خطاها ، حتماً وضعیت درخواست را کنترل کنید.

توجه: درخواست ماژول ویژگی ای که قبلاً روی دستگاه نصب شده است ، اشکالی ندارد. در صورت تشخیص ماژول در حال حاضر ، API فوراً درخواست را تکمیل می کند. علاوه بر این ، پس از نصب یک ماژول ، Google Play آن را به طور خودکار به روز می کند. یعنی وقتی نسخه جدیدی از بسته نرم افزاری برنامه خود را بارگذاری می کنید ، این پلتفرم تمام APK های نصب شده را که متعلق به برنامه شما هستند ، به روز می کند. برای اطلاعات بیشتر ، مدیریت به روزرسانی های برنامه را بخوانید.

برای دسترسی به کد و منابع ماژول ، برنامه شما باید SplitCompat را فعال کند . توجه داشته باشید که SplitCompat برای برنامه های فوری Android مورد نیاز نیست.

نصب ماژول های روی تقاضا را به تعویق بیندازید

اگر نیازی به برنامه خود ندارید تا بلافاصله یک ماژول On Demand را بارگیری و نصب کنید ، می توانید نصب را برای زمان برنامه در پس زمینه تعویق کنید. به عنوان مثال ، اگر می خواهید برخی از مطالب تبلیغاتی را برای راه اندازی بعدی برنامه خود بارگذاری کنید.

همانطور که در زیر آمده است ، می توانید ماژول ای را که بعداً با استفاده از روش deferredInstall() بارگیری می شود ، مشخص کنید. و بر خلاف SplitInstallManager.startInstall() ، برنامه شما برای شروع درخواست نصب معوق ، نیازی به حضور در پیش زمینه ندارد.

کاتلین

// 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"))

جاوا

// 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() گوش دهید. قبل از اینکه بتوانید به روزرسانی های درخواست نصب خود را شروع کنید ، شنونده را ثبت کنید و همانطور که در زیر آمده است ، شناسه جلسه را برای درخواست دریافت کنید.

کاتلین

// 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)

جاوا

// 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 باشد. برای پیشنهادات در مورد چگونگی رسیدگی به این موقعیت ها از دیدگاه کاربر ، دستورالعمل های UX ما را برای تحویل تقاضا بررسی کنید.

از نظر کد ، شما باید با استفاده از addOnFailureListener() بارگیری یا نصب ماژول را انجام دهید:

کاتلین

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.
                    }
                }
            }
        }
}

جاوا

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_exted این درخواست رد می شود زیرا حداقل یک درخواست موجود وجود دارد که در حال حاضر بارگیری می شود. همانطور که در نمونه بالا نشان داده شده است ، بررسی کنید که آیا درخواستی وجود دارد که هنوز در حال بارگیری هستند.
module_unailable Google Play قادر به یافتن ماژول درخواستی بر اساس نسخه نصب شده فعلی برنامه ، دستگاه و حساب Google Play کاربر نیست. اگر کاربر به ماژول دسترسی نداشته باشد ، به آنها اطلاع دهید.
invalid_request Google Play درخواست را دریافت کرد ، اما درخواست معتبر نیست. تأیید کنید که اطلاعات موجود در درخواست کامل و دقیق است.
Session_not_found جلسه ای برای شناسه جلسه معین یافت نشد. اگر می خواهید توسط شناسه جلسه آن ، وضعیت درخواست را رصد کنید ، حتماً شناسه جلسه صحیح باشد.
api_not_available کتابخانه تحویل ویژگی Play در دستگاه فعلی پشتیبانی نمی شود. یعنی دستگاه قادر به بارگیری و نصب ویژگی ها در صورت تقاضا نیست. برای دستگاه هایی که Android 4.4 (سطح API 20) یا پایین تر دارند ، باید ماژول های ویژگی را در زمان نصب با استفاده از dist:fusing Manifice Propertive قرار دهید. برای کسب اطلاعات بیشتر ، در مورد مانیفست ماژول ویژگی بخوانید.
network_error درخواست به دلیل خطای شبکه انجام نشد. کاربر را وادار به ایجاد یک اتصال شبکه یا تغییر در یک شبکه دیگر کند.
دسترسی_ برنامه به دلیل مجوزهای کافی قادر به ثبت درخواست نیست. این به طور معمول زمانی اتفاق می افتد که برنامه در پس زمینه باشد. هنگام بازگشت برنامه به پیش زمینه ، درخواست را امتحان کنید.
ناسازگار_ with_existing_session این درخواست شامل یک یا چند ماژول است که قبلاً درخواست شده اند اما هنوز نصب نشده اند. یا یک درخواست جدید ایجاد کنید که شامل ماژول هایی نیست که برنامه شما قبلاً درخواست کرده است ، یا صبر کنید تا همه ماژول های درخواست شده در حال حاضر قبل از تلاش مجدد درخواست ، نصب را به پایان برسانند.

به خاطر داشته باشید ، درخواست یک ماژول که قبلاً نصب شده است ، در خطایی برطرف نمی شود.

سرویس_د سرویس مسئول رسیدگی به این درخواست درگذشت. درخواست را دوباره امتحان کنید.

شما با این کد خطا ، وضعیت FAILED و شناسه جلسه -1 ، یک SplitInstallStateUpdatedListener SplitInstallSessionState دریافت می کند.

ناکافی_نه این دستگاه ذخیره رایگان کافی برای نصب ماژول ویژگی ندارد. به کاربر اطلاع دهید که ذخیره کافی برای نصب این ویژگی ندارند.
Splitcompat_Verification_error ، splitcompat_emulation_error ، splitcompat_copy_error Splitcompat نمی تواند ماژول ویژگی را بارگیری کند. این خطاها باید به طور خودکار پس از شروع مجدد برنامه بعدی خود را حل کنند.
play_store_not_found برنامه Play Store روی دستگاه نصب نشده است. به کاربر اطلاع دهید که برنامه Play Store برای بارگیری این ویژگی لازم است.
app_not_owned این برنامه توسط Google Play نصب نشده است و ویژگی آن قابل بارگیری نیست. این خطا فقط می تواند برای نصب های معوق رخ دهد. اگر می خواهید کاربر برنامه را در Google Play به دست آورد ، از startInstall() استفاده کنید که می تواند تأیید کاربر لازم را بدست آورد.
INTERNAL_ERROR خطای داخلی در فروشگاه بازی رخ داده است. درخواست را دوباره امتحان کنید.

اگر کاربر درخواست بارگیری یک ماژول On Demand و خطایی دارد ، در نظر بگیرید که گفتگوی را نشان می دهد که دو گزینه برای کاربر فراهم می کند: دوباره امتحان کنید (که دوباره درخواست را امتحان می کند) و لغو (که درخواست را رها می کند). برای پشتیبانی بیشتر ، شما همچنین باید پیوند کمک را ارائه دهید که کاربران را به مرکز راهنمای Google Play هدایت می کند.

به روزرسانی های حالت را کنترل کنید

پس از ثبت نام شنونده و ثبت نام جلسه را برای درخواست خود ضبط کنید ، از StateUpdatedListener.onStateUpdate() استفاده کنید تا تغییرات حالت را انجام دهید ، همانطور که در زیر آمده است.

کاتلین

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.
            }
        }
    }
}

جاوا

@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.
        }
    }
}

حالت های احتمالی درخواست نصب شما در جدول زیر توضیح داده شده است.

حالت درخواست توضیحات اقدام پیشنهادی
در انتظار درخواست پذیرفته شده است و بارگیری باید به زودی آغاز شود. برای ارائه بازخورد کاربر در مورد بارگیری ، اجزای UI را مانند نوار پیشرفت اولیه اولیه کنید.
نیاز به_ USER_CONFIRATION بارگیری نیاز به تأیید کاربر دارد. در صورت نصب برنامه از طریق Google Play ، این وضعیت معمولاً این وضعیت رخ می دهد. کاربر را مجبور به تأیید بارگیری ویژگی از طریق Google Play کنید. برای کسب اطلاعات بیشتر ، در مورد نحوه به دست آوردن تأیید کاربر به بخش بروید.
در حال دانلود بارگیری در حال انجام است. اگر نوار پیشرفت برای بارگیری را ارائه می دهید ، از روشهای SplitInstallSessionState.bytesDownloaded() و SplitInstallSessionState.totalBytesToDownload() استفاده کنید تا UI را به روز کنید (به نمونه کد بالای این جدول مراجعه کنید).
بارگیری شده دستگاه ماژول را بارگیری کرده است اما نصب هنوز آغاز نشده است. برنامه ها باید SplitCompat را قادر به دسترسی به ماژول های بارگیری شده و از دیدن این حالت کنند. این امر برای دسترسی به کد و منابع ماژول ویژگی مورد نیاز است.
نصب دستگاه در حال حاضر ماژول را نصب می کند. نوار پیشرفت را به روز کنید. این حالت به طور معمول کوتاه است.
نصب شده ماژول روی دستگاه نصب شده است. برای ادامه سفر کاربر به کد و منبع در ماژول دسترسی پیدا کنید .

اگر ماژول برای یک برنامه فوری Android در Android 8.0 (API سطح 26) یا بالاتر است ، برای به روزرسانی اجزای برنامه با ماژول جدید باید از splitInstallHelper استفاده کنید.

ناموفق درخواست قبل از نصب ماژول روی دستگاه انجام نشد. کاربر را وادار کنید که درخواست را دوباره امتحان کند یا آن را لغو کند.
لغو دستگاه در حال لغو درخواست است. برای کسب اطلاعات بیشتر ، در مورد نحوه لغو درخواست نصب به بخش بروید.
لغو شده درخواست لغو شده است.

تأیید کاربر را بدست آورید

در برخی موارد ، Google Play ممکن است قبل از رضایت از درخواست بارگیری ، به تأیید کاربر نیاز داشته باشد. به عنوان مثال ، اگر برنامه شما توسط Google Play نصب نشده است یا اگر در حال تلاش برای بارگیری بزرگ از طریق داده های موبایل هستید. در چنین مواردی ، وضعیت گزارش های درخواست REQUIRES_USER_CONFIRMATION ، و برنامه شما قبل از اینکه دستگاه بتواند ماژول ها را در درخواست بارگیری و نصب کند ، باید تأیید کاربر را بدست آورد. برای به دست آوردن تأیید ، برنامه شما باید کاربر را به شرح زیر سوق دهد:

کاتلین

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

جاوا

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

شما می توانید یک پرتاب نتیجه فعالیت را با استفاده از قرارداد Builtin ActivityResultContracts.StartIntentSenderForResult ثبت کنید. به API های نتیجه فعالیت مراجعه کنید.

وضعیت درخواست بسته به پاسخ کاربر به روز می شود:

  • اگر کاربر تأیید را بپذیرد ، وضعیت درخواست در PENDING تغییر می کند و بارگیری می شود.
  • اگر کاربر تأیید را انکار کند ، وضعیت درخواست به CANCELED تغییر می کند.
  • اگر کاربر قبل از نابودی گفتگو ، انتخابی را انجام ندهد ، وضعیت درخواست همانطور که REQUIRES_USER_CONFIRMATION باقی می ماند. برنامه شما می تواند کاربر را دوباره درخواست کند تا درخواست را تکمیل کند.

برای دریافت پاسخ به تماس با پاسخ کاربر ، می توانید همانطور که در زیر آمده است ، فعالیت ResultCallback را نادیده بگیرید.

کاتلین

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.
    }
}

جاوا

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() را با استفاده از شناسه جلسه درخواست فراخوانی کند.

کاتلین

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

جاوا

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

به ماژول های دسترسی

برای دسترسی به کد و منابع از یک ماژول بارگیری شده پس از بارگیری ، برنامه شما باید کتابخانه Splitcompat را هم برای برنامه و هم برای هر فعالیت در ماژول های ویژگی بارگیری برنامه شما فعال کند.

با این حال ، باید توجه داشته باشید که این سکو محدودیت های زیر را برای دسترسی به محتوای یک ماژول ، برای مدتی (روزها ، در بعضی موارد) پس از بارگیری ماژول تجربه می کند:

  • این پلتفرم نمی تواند هیچ ورودی مانیفست جدیدی را که توسط ماژول معرفی شده است ، اعمال کند.
  • این پلتفرم نمی تواند به منابع ماژول برای اجزای UI سیستم ، مانند اعلان ها دسترسی پیدا کند. اگر نیاز به استفاده از چنین منابع بلافاصله دارید ، شامل آن منابع موجود در ماژول پایه برنامه خود را در نظر بگیرید.

SplitCompat را فعال کنید

برای دسترسی به برنامه خود به کد و منابع از یک ماژول بارگیری شده ، باید با استفاده از تنها یکی از روشهای شرح داده شده در بخش های زیر ، SplitCompat را فعال کنید.

پس از فعال کردن SPLITCOMPAT برای برنامه خود ، باید برای هر فعالیت در ماژول های ویژگی مورد نظر خود ، SplitCompat را نیز فعال کنید .

در مانیفست SplitCompatapplication را اعلام کنید

ساده ترین راه برای فعال کردن SplitCompat اعلام SplitCompatApplication به عنوان زیر کلاس Application در مانیفست برنامه شما است ، همانطور که در زیر آمده است:

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

پس از نصب برنامه بر روی یک دستگاه ، می توانید به طور خودکار به کد و منابع از ماژول های ویژگی بارگیری شده دسترسی پیدا کنید.

فراخوانی Splitcompat در زمان اجرا

همچنین می توانید SplitCompat را در فعالیت ها یا خدمات خاص در زمان اجرا فعال کنید. فعال کردن SplitCompat از این طریق برای راه اندازی فعالیت های موجود در ماژول های ویژگی لازم است. برای انجام این کار ، همانطور که در زیر مشاهده می شود ، ضمیمه attachBaseContext را نادیده بگیرید.

اگر یک کلاس برنامه سفارشی دارید ، به جای آن ، به منظور فعال کردن SplitCompat برای برنامه خود ، همانطور که در زیر آمده است ، SplitCompatApplication گسترش دهید:

کاتلین

class MyApplication : SplitCompatApplication() {
    ...
}

جاوا

public class MyApplication extends SplitCompatApplication {
    ...
}

SplitCompatApplication به سادگی از ContextWrapper.attachBaseContext() غافل می شود تا شامل SplitCompat.install(Context applicationContext) باشد. اگر نمی خواهید کلاس Application شما SplitCompatApplication گسترش دهد ، می توانید روش attachBaseContext() را به صورت دستی نادیده بگیرید ، به شرح زیر:

کاتلین

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

جاوا

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

اگر ماژول On Demand شما با هر دو برنامه فوری و برنامه های نصب شده سازگار است ، می توانید به صورت زیر به صورت شرط از Splitcompat استفاده کنید ، به شرح زیر:

کاتلین

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

جاوا

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

SplitCompat را برای فعالیت های ماژول فعال کنید

پس از فعال کردن SPLITCOMPAT برای برنامه پایه خود ، باید برای هر فعالیتی که برنامه شما در یک ماژول ویژگی بارگیری می کند ، SplitCompat را فعال کنید. برای انجام این کار ، از روش SplitCompat.installActivity() استفاده کنید ، به شرح زیر:

کاتلین

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

جاوا

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

اجزای دسترسی تعریف شده در ماژول های ویژگی

فعالیتی را که در یک ماژول ویژگی تعریف شده است شروع کنید

شما می توانید فعالیت های تعریف شده در ماژول های ویژگی را با استفاده از startActivity() پس از فعال کردن splitcompat انجام دهید.

کاتلین

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

جاوا

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

اولین پارامتر setClassName نام بسته برنامه است و پارامتر دوم نام کلاس کامل فعالیت است.

هنگامی که در یک ماژول ویژگی فعالیتی دارید که در صورت تقاضا بارگیری کرده اید ، باید SplitCompat را در فعالیت فعال کنید .

یک سرویس تعریف شده در یک ماژول ویژگی را شروع کنید

شما می توانید خدمات تعریف شده در ماژول های ویژگی را با استفاده از startService() پس از فعال کردن SplitCompat راه اندازی کنید.

کاتلین

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

جاوا

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

یک مؤلفه تعریف شده در یک ماژول ویژگی را صادر کنید

شما نباید اجزای آندروید صادر شده را در ماژول های اختیاری قرار دهید.

سیستم ساخت ، ورودی های آشکار برای همه ماژول ها را در ماژول پایه ادغام می کند. اگر یک ماژول اختیاری حاوی یک مؤلفه صادر شده باشد ، حتی قبل از نصب ماژول در دسترس خواهد بود و می تواند در هنگام فراخوانی از برنامه دیگر ، به دلیل کدهای گمشده باعث خرابی شود.

این برای مؤلفه های داخلی مشکلی ندارد. آنها فقط توسط برنامه قابل دسترسی هستند ، بنابراین برنامه می تواند قبل از دسترسی به مؤلفه ، ماژول را نصب کند .

اگر به یک مؤلفه صادر شده نیاز دارید و می خواهید محتوای آن در یک ماژول اختیاری باشد ، در نظر بگیرید که الگوی پروکسی را اجرا کنید. شما می توانید این کار را با اضافه کردن یک مؤلفه صادر شده پروکسی در پایه انجام دهید. در صورت دسترسی ، مؤلفه پروکسی می تواند حضور ماژول حاوی محتوا را بررسی کند. اگر ماژول موجود باشد ، مؤلفه پروکسی می تواند مؤلفه داخلی را از ماژول از طریق یک Intent شروع کند و قصد را از برنامه تماس گیرنده بازگرداند. اگر ماژول موجود نباشد ، مؤلفه می تواند ماژول را بارگیری کند یا یک پیام خطای مناسب را به برنامه تماس گیرنده برگرداند.

به کد و منابع ماژول های نصب شده دسترسی پیدا کنید

اگر SplipCompat را برای زمینه برنامه کاربردی پایه خود و فعالیت های موجود در ماژول ویژگی خود فعال کنید ، می توانید پس از نصب ماژول اختیاری ، از کد و منابع از یک ماژول ویژگی استفاده کنید که گویی بخشی از APK پایه است.

کد دسترسی به یک ماژول متفاوت

از یک ماژول به کد پایه دسترسی پیدا کنید

کدی که در داخل ماژول پایه شما قرار دارد می تواند مستقیماً توسط ماژول های دیگر استفاده شود. شما نیازی به انجام کار خاصی ندارید. فقط کلاسهای مورد نیاز خود را وارد کرده و استفاده کنید.

دسترسی به کد ماژول از ماژول دیگر

یک شی یا کلاس در داخل یک ماژول نمی تواند مستقیماً از ماژول دیگری به طور مستقیم دسترسی داشته باشد ، اما با استفاده از بازتاب می توان به طور غیرمستقیم به آن دسترسی پیدا کرد.

به دلیل هزینه های عملکرد تأمل ، باید نسبت به این که چند بار اتفاق می افتد احتیاط کنید. برای موارد استفاده پیچیده ، از چارچوب های تزریق وابستگی مانند Dagger 2 برای تضمین یک تماس بازتاب واحد در طول عمر برنامه استفاده کنید.

برای ساده کردن تعامل با شی پس از لحظه ، توصیه می شود رابط در ماژول پایه و اجرای آن در ماژول ویژگی تعریف کنید. به عنوان مثال:

کاتلین

// 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();

جاوا

// 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();

دسترسی به منابع و دارایی ها از یک ماژول متفاوت

پس از نصب یک ماژول ، می توانید به روش استاندارد ، با دو احتیاط به منابع و دارایی های موجود در ماژول دسترسی پیدا کنید:

  • اگر از یک ماژول متفاوت به یک منبع دسترسی پیدا کنید ، ماژول به شناسه منابع دسترسی نخواهد داشت ، اگرچه هنوز هم می توان به این منبع با نام دسترسی پیدا کرد. توجه داشته باشید که بسته ای که برای مراجعه به منبع استفاده می شود ، بسته ای از ماژول است که در آن منبع تعریف شده است.
  • اگر می خواهید به دارایی ها یا منابعی که در یک ماژول جدید نصب شده از یک ماژول نصب شده متفاوت از برنامه خود وجود دارد ، دسترسی پیدا کنید ، باید این کار را با استفاده از زمینه برنامه انجام دهید. زمینه مؤلفه ای که در تلاش برای دسترسی به منابع است ، هنوز به روز نمی شود. از طرف دیگر ، شما می توانید آن مؤلفه (به عنوان مثال فعالیت فراخوانی. ) را بازآفرینی کنید یا پس از نصب ماژول ویژگی ، SplitCompat را دوباره نصب کنید .

با استفاده از تحویل در صورت تقاضا ، کد بومی را در یک برنامه بارگیری کنید

توصیه می کنیم هنگام استفاده از تحویل در صورت تقاضا از ماژول های ویژگی ، از relinker استفاده کنید. Relinker پس از نصب یک ماژول ویژگی ، مسئله بارگیری کتابخانه های بومی را برطرف می کند. در نکات Android JNI می توانید در مورد Relinker اطلاعات بیشتری کسب کنید.

کد بومی را از یک ماژول اختیاری بارگیری کنید

پس از نصب تقسیم ، توصیه می کنیم کد بومی آن را از طریق Relinker بارگیری کنید. برای برنامه های فوری باید از این روش خاص استفاده کنید.

اگر از System.loadLibrary() استفاده می کنید تا کد بومی خود را بارگیری کنید و کتابخانه بومی خود به کتابخانه دیگر در ماژول وابستگی دارد ، ابتدا باید آن کتابخانه دیگر را به صورت دستی بارگیری کنید. اگر از Relinker استفاده می کنید ، عملیات معادل Relinker.recursively().loadLibrary() .

اگر از dlopen() در کد بومی استفاده می کنید تا یک کتابخانه تعریف شده در یک ماژول اختیاری را بارگیری کنید ، با مسیرهای نسبی کتابخانه کار نمی کند. بهترین راه حل بازیابی مسیر مطلق کتابخانه از کد جاوا از طریق ClassLoader.findLibrary() و سپس استفاده از آن در تماس dlopen() است. این کار را قبل از وارد کردن کد بومی انجام دهید یا از یک تماس JNI از کد بومی خود به جاوا استفاده کنید.

دسترسی به برنامه های فوری Android نصب شده

پس از گزارش ماژول برنامه فوری Android ، می توانید با استفاده از یک زمینه برنامه تازه INSTALLED به کد و منابع آن دسترسی پیدا کنید. زمینه ای که برنامه شما قبل از نصب ماژول ایجاد می کند (به عنوان مثال ، موردی که قبلاً در یک متغیر ذخیره شده است) حاوی محتوای ماژول جدید نیست. اما یک زمینه تازه انجام می شود - به عنوان مثال می توان با استفاده از createPackageContext به دست آورد.

کاتلین

// 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
            }
        }
    }
}

جاوا

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

برنامه های فوری Android در Android 8.0 و بالاتر

هنگام درخواست ماژول On Demand برای یک برنامه فوری Android در Android 8.0 (API سطح 26) و بالاتر ، پس از گزارش درخواست INSTALLED ، باید برنامه را با زمینه ماژول جدید از طریق تماس برای SplitInstallHelper.updateAppInfo(Context context) به روز کنید. در غیر این صورت ، برنامه هنوز از کد و منابع ماژول آگاه نیست. پس از بروزرسانی ابرداده برنامه ، باید با فراخوانی یک Handler جدید ، محتویات ماژول را در طول رویداد اصلی موضوع بعدی بارگذاری کنید ، همانطور که در زیر آمده است:

کاتلین

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
                        ...
                    }
                }
            }
        }
    }
}

جاوا

@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) استفاده کنید ، همانطور که در زیر آمده است:

کاتلین

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

جاوا

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

محدودیت های شناخته شده

  • استفاده از WebView Android در فعالیتی که به منابع یا دارایی ها از یک ماژول اختیاری دسترسی پیدا می کند ، امکان پذیر نیست. این به دلیل ناسازگاری بین WebView و SplitCompat در سطح API ANDROID 28 و پایین است.
  • شما نمی توانید اشیاء Android ApplicationInfo ، محتوای آنها یا اشیاء موجود در برنامه شما را ذخیره کنید. شما همیشه باید این اشیاء را در صورت نیاز از زمینه برنامه واکشی کنید. ذخیره چنین اشیاء می تواند باعث شود برنامه هنگام نصب ماژول ویژگی خراب شود.

ماژول های نصب شده را مدیریت کنید

برای بررسی اینکه ماژول های ویژگی در حال حاضر روی دستگاه نصب شده اند ، می توانید با SplitInstallManager.getInstalledModules() تماس بگیرید ، که یک Set<String> از نام ماژول های نصب شده را باز می گرداند ، همانطور که در شکل زیر نشان داده شده است.

کاتلین

val installedModules: Set<String> = splitInstallManager.installedModules

جاوا

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

ماژول های را حذف نصب کنید

همانطور که در شکل زیر آمده است ، می توانید با استفاده از SplitInstallManager.deferredUninstall(List<String> moduleNames) ، ماژول ها را با استفاده از splistinstallmanager.deferreduninstall (لیست <string>) درخواست کنید.

کاتلین

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

جاوا

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

حذف ماژول بلافاصله رخ نمی دهد. یعنی دستگاه در صورت لزوم برای صرفه جویی در فضای ذخیره سازی ، آنها را در پس زمینه حذف می کند. شما می توانید تأیید کنید که دستگاه با استفاده از SplitInstallManager.getInstalledModules() و بازرسی از نتیجه ، یک ماژول را حذف کرده است ، همانطور که در بخش قبلی توضیح داده شده است.

منابع اضافی زبان را بارگیری کنید

با استفاده از بسته های برنامه ، دستگاه ها فقط کد و منابع مورد نیاز خود را برای اجرای برنامه شما بارگیری می کنند. بنابراین ، برای منابع زبان ، دستگاه کاربر فقط منابع زبان برنامه شما را بارگیری می کند که با یک یا چند زبان که در حال حاضر در تنظیمات دستگاه انتخاب شده است مطابقت دارد.

اگر می خواهید برنامه شما به منابع اضافی زبان دسترسی داشته باشد-برای مثال ، برای اجرای یک انتخاب کننده زبان درون برنامه ، می توانید از کتابخانه تحویل ویژگی Play برای بارگیری آنها در صورت تقاضا استفاده کنید. این فرآیند شبیه به بارگیری یک ماژول ویژگی است ، همانطور که در شکل زیر نشان داده شده است.

کاتلین

// 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)

جاوا

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

این درخواست به گونه ای انجام می شود که گویی درخواست ماژول ویژگی است. یعنی شما می توانید وضعیت درخواست را مانند آنچه که به طور معمول انجام می دهید نظارت کنید .

اگر برنامه شما بلافاصله به منابع زبان اضافی احتیاج ندارد ، می توانید همانطور که در زیر آمده است ، نصب را برای هنگام برنامه در پس زمینه انجام دهید.

کاتلین

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

جاوا

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

به منابع زبان بارگیری شده دسترسی پیدا کنید

برای دستیابی به منابع زبانی بارگیری شده ، برنامه شما باید روش SplitCompat.installActivity() را در روش attachBaseContext() هر فعالیتی که نیاز به دسترسی به آن منابع دارد ، همانطور که در زیر آمده است ، اجرا کند.

کاتلین

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

جاوا

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

برای هر فعالیتی که می خواهید از منابع زبانی استفاده کنید ، برنامه شما بارگیری کرده است ، زمینه پایه را به روز کرده و یک مکان جدید را از طریق Configuration آن تنظیم کنید:

کاتلین

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

جاوا

@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() استفاده کنید.

کاتلین

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

جاوا

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

منابع زبانی اضافی را حذف کنید

مشابه ماژول های ویژگی ، می توانید منابع اضافی را در هر زمان حذف کنید. قبل از درخواست حذف ، ممکن است ابتدا بخواهید تعیین کنید که در حال حاضر کدام زبان ها نصب شده اند ، به شرح زیر است.

کاتلین

val installedLanguages: Set<String> = splitInstallManager.installedLanguages

جاوا

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

سپس می توانید با استفاده از روش deferredLanguageUninstall() ، همانطور که در زیر آمده است ، تصمیم بگیرید که چه زبانهایی را حذف کنید.

کاتلین

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

جاوا

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

نصب ماژول تست محلی

کتابخانه تحویل ویژگی Play به شما امکان می دهد بدون اتصال به فروشگاه Play ، توانایی برنامه خود را در انجام موارد زیر آزمایش کنید:

در این صفحه نحوه استقرار APK های تقسیم شده برنامه خود به دستگاه تست خود توضیح داده شده است که تحویل ویژگی بازی به طور خودکار از آن دسته از APK ها برای شبیه سازی درخواست ، بارگیری و نصب ماژول ها از فروشگاه Play استفاده می کند.

اگرچه نیازی به تغییر در منطق برنامه خود ندارید ، اما باید شرایط زیر را برآورده کنید:

  • آخرین نسخه bundletool را بارگیری و نصب کنید. برای ساختن مجموعه جدیدی از APK های قابل نصب از بسته نرم افزاری برنامه خود bundletool نیاز دارید.

مجموعه ای از apks را بسازید

اگر قبلاً این کار را نکرده اید ، آپ های تقسیم شده برنامه خود را به شرح زیر بسازید:

  1. با استفاده از یکی از روشهای زیر یک بسته برنامه برای برنامه خود بسازید:
  2. برای تولید مجموعه ای از APK ها برای کلیه تنظیمات دستگاه با دستور زیر bundletool استفاده کنید:

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

پرچم --local-testing شامل داده های متا در مانیفست های APKS شما است که به کتابخانه تحویل ویژگی 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 ها و استقرار آنها در دستگاه تست خود استفاده می کنید ، این شامل ابرداده است که به کتابخانه تحویل ویژگی بازی دستور می دهد تا به طور خودکار تماس های API برنامه خود را برای فراخوانی FakeSplitInstallManager ، به جای SplitInstallManager ، تغییر دهد.

FakeSplitInstallManager شامل یک پرچم بولی است که می توانید دفعه بعد که برنامه شما برای نصب یک ماژول درخواست می کند ، خطای شبکه را شبیه سازی کنید. برای دسترسی به FakeSplitInstallManager در تست های خود ، می توانید نمونه ای از آن را با استفاده از FakeSplitInstallManagerFactory ، همانطور که در زیر آمده است ، دریافت کنید:

کاتلین

// 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)

جاوا

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