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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

کاتلین

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

جدول زیر حالت‌های خطایی را که برنامه شما ممکن است نیاز به مدیریت آنها داشته باشد، شرح می‌دهد:

کد خطا توضیحات اقدام پیشنهادی
محدودیت جلسات فعال از حد مجاز فراتر رفت درخواست رد می‌شود زیرا حداقل یک درخواست موجود وجود دارد که در حال حاضر در حال دانلود است. بررسی کنید که آیا درخواست‌هایی وجود دارند که هنوز در حال دانلود باشند، همانطور که در نمونه بالا نشان داده شده است.
ماژول_غیرقابل_دسترس گوگل پلی قادر به یافتن ماژول درخواستی بر اساس نسخه نصب شده فعلی برنامه، دستگاه و حساب گوگل پلی کاربر نیست. اگر کاربر به ماژول دسترسی ندارد، به او اطلاع دهید.
درخواست_نامعتبر گوگل پلی درخواست را دریافت کرد، اما درخواست معتبر نیست. تأیید کنید که اطلاعات موجود در درخواست کامل و دقیق است.
جلسه پیدا نشد جلسه‌ای برای شناسه جلسه داده شده یافت نشد. اگر می‌خواهید وضعیت یک درخواست را با استفاده از شناسه جلسه آن رصد کنید، مطمئن شوید که شناسه جلسه صحیح است.
API_NOT_AVAILABLE کتابخانه ارائه ویژگی‌های Play در دستگاه فعلی پشتیبانی نمی‌شود. یعنی دستگاه قادر به دانلود و نصب ویژگی‌ها بر اساس تقاضا نیست. برای دستگاه‌هایی که اندروید ۴.۴ (سطح API 20) یا پایین‌تر را اجرا می‌کنند، باید ماژول‌های ویژگی را در زمان نصب با استفاده از ویژگی dist:fusing manifest اضافه کنید. برای کسب اطلاعات بیشتر، درباره ماژول ویژگی manifest بخوانید.
خطای شبکه درخواست به دلیل خطای شبکه انجام نشد. از کاربر بخواهید که یا اتصال شبکه برقرار کند یا به شبکه دیگری تغییر دهد.
دسترسی_ممنوع به دلیل مجوزهای ناکافی، برنامه قادر به ثبت درخواست نیست. این معمولاً زمانی اتفاق می‌افتد که برنامه در پس‌زمینه باشد. وقتی برنامه به پیش‌زمینه برمی‌گردد، درخواست را امتحان کنید.
ناسازگار با جلسه موجود این درخواست شامل یک یا چند ماژول است که قبلاً درخواست شده‌اند اما هنوز نصب نشده‌اند. یا یک درخواست جدید ایجاد کنید که شامل ماژول‌هایی که برنامه شما قبلاً درخواست کرده است، نباشد، یا قبل از تلاش مجدد برای درخواست، منتظر بمانید تا نصب همه ماژول‌های درخواستی فعلی به پایان برسد.

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

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

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

فضای ذخیره‌سازی ناکافی دستگاه فضای ذخیره‌سازی خالی کافی برای نصب ماژول ویژگی ندارد. به کاربر اطلاع دهید که فضای ذخیره‌سازی کافی برای نصب این ویژگی ندارد.
خطای تأیید تطبیق تقسیم، خطای شبیه‌سازی تطبیق تقسیم، خطای کپی تطبیق تقسیم SplitCompat نتوانست ماژول ویژگی را بارگیری کند. این خطاها باید پس از راه‌اندازی مجدد بعدی برنامه، به‌طور خودکار برطرف شوند.
فروشگاه_بازیافت_نشد برنامه Play Store روی دستگاه نصب نشده است. به کاربر اطلاع دهید که برای دانلود این ویژگی، برنامه Play Store لازم است.
APP_NOT_OWNED این برنامه توسط گوگل پلی نصب نشده است و این ویژگی قابل دانلود نیست. این خطا فقط برای نصب‌های معوق ممکن است رخ دهد. اگر می‌خواهید کاربر برنامه را از گوگل پلی دریافت کند، از startInstall() استفاده کنید که می‌تواند تأیید لازم کاربر را دریافت کند.
خطای داخلی یک خطای داخلی در فروشگاه Play رخ داده است. درخواست را دوباره امتحان کنید.

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

مدیریت به‌روزرسانی‌های وضعیت

پس از اینکه یک شنونده (listener) ثبت کردید و شناسه جلسه (session ID) را برای درخواست خود ثبت کردید، از 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.
        }
    }
}

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

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

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

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

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

در برخی موارد، گوگل پلی ممکن است قبل از پاسخگویی به درخواست دانلود، از کاربر تأییدیه بخواهد. برای مثال، اگر برنامه شما توسط گوگل پلی نصب نشده باشد یا اگر در حال تلاش برای دانلود حجم زیادی از طریق داده تلفن همراه هستید. در چنین مواردی، وضعیت درخواست 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 یک پرتاب‌کننده‌ی نتیجه‌ی فعالیت (activity result launcher) ثبت کنید. به APIهای نتیجه‌ی فعالیت (Activity Result APIs) مراجعه کنید.

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

  • اگر کاربر تأیید را بپذیرد، وضعیت درخواست به 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 در فایل manifest برنامه خود تعریف کنید، همانطور که در زیر نشان داده شده است:

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

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

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

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

اگر یک کلاس Application سفارشی دارید، آن را به جای SplitCompatApplication از 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 را برای هر activity که برنامه‌ی شما در یک ماژول feature دانلود می‌کند، فعال کنید. برای انجام این کار، از متد 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 نام پکیج برنامه و دومین پارامتر نام کامل کلاس activity است.

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

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

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

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

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

کدی که درون ماژول پایه شما قرار دارد، می‌تواند مستقیماً توسط ماژول‌های دیگر استفاده شود. لازم نیست کار خاصی انجام دهید؛ فقط کلاس‌های مورد نیاز خود را وارد (import) کرده و استفاده کنید.

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

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

به دلیل هزینه‌های عملکردی بازتاب، باید مراقب باشید که این اتفاق چند وقت یکبار رخ می‌دهد. برای موارد استفاده پیچیده، از چارچوب‌های تزریق وابستگی مانند 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 مشکلی را در بارگذاری کتابخانه‌های بومی پس از نصب یک ماژول ویژگی برطرف می‌کند. می‌توانید در نکات JNI اندروید درباره ReLinker بیشتر بدانید.

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

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

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

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

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

پس از اینکه یک ماژول برنامه فوری اندروید به عنوان INSTALLED گزارش شد، می‌توانید با استفاده از یک Context برنامه به‌روزرسانی شده به کد و منابع آن دسترسی پیدا کنید. Contextی که برنامه شما قبل از نصب یک ماژول ایجاد می‌کند (برای مثال، Context‌ای که از قبل در یک متغیر ذخیره شده است) حاوی محتوای ماژول جدید نیست. اما یک Context جدید حاوی محتوای ماژول جدید است - این را می‌توان به عنوان مثال با استفاده از 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();
        }
    }
}

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

هنگام درخواست یک ماژول درخواستی برای یک برنامه فوری اندروید در اندروید ۸.۰ (سطح API ۲۶) و بالاتر، پس از گزارش درخواست نصب به عنوان 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 App دانلود کرده است، بارگذاری کنید، از 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 در یک activity که به منابع یا دارایی‌ها از یک ماژول اختیاری دسترسی پیدا می‌کند، امکان‌پذیر نیست. این به دلیل ناسازگاری بین WebView و SplitCompat در Android API سطح ۲۸ و پایین‌تر است.
  • شما نمی‌توانید اشیاء 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 Feature Delivery Library) برای دانلود آنها در صورت تقاضا استفاده کنید. این فرآیند مشابه دانلود یک ماژول ویژگی است، همانطور که در زیر نشان داده شده است.

کاتلین

// 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() هر activity که نیاز به دسترسی به آن منابع دارد، اجرا کند، همانطور که در زیر نشان داده شده است.

کاتلین

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 خود را مجدداً ایجاد کنید. می‌توانید از متد 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 Feature Delivery به طور خودکار از آن فایل‌های 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 اطلاع می‌دهد که از APKهای تقسیم‌شده محلی برای آزمایش نصب ماژول‌های ویژگی، بدون اتصال به فروشگاه Play، استفاده کند.

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

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

bundletool install-apks --apks my_app.apks

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

شبیه‌سازی خطای شبکه

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