กำหนดค่าการแสดงโฆษณาแบบออนดีมานด์

โมดูลฟีเจอร์ช่วยให้คุณแยกฟีเจอร์และทรัพยากรบางอย่างออกจากโมดูลฐานของแอปและรวมไว้ใน App Bundle ได้ ตัวอย่างเช่น ผู้ใช้สามารถดาวน์โหลดและติดตั้งคอมโพเนนต์เหล่านั้นตามต้องการภายหลังผ่าน Play Feature Delivery หลังจากที่ติดตั้ง APK พื้นฐานของแอปแล้ว

ตัวอย่างเช่น ลองพิจารณาแอปรับส่งข้อความที่มีฟังก์ชันการทำงานสำหรับการจับภาพและส่งข้อความรูปภาพ แต่มีผู้ใช้เพียงไม่กี่เปอร์เซ็นต์เท่านั้นที่ส่งข้อความรูปภาพ การใส่ข้อความรูปภาพเป็นโมดูล ฟีเจอร์ที่ดาวน์โหลดได้จึงเป็นเรื่องที่เหมาะสม วิธีนี้จะทำให้การดาวน์โหลดแอปครั้งแรกมีขนาดเล็กลงสำหรับผู้ใช้ทุกคน และมีเพียงผู้ใช้ที่ส่งข้อความรูปภาพเท่านั้นที่ต้องดาวน์โหลดคอมโพเนนต์เพิ่มเติมดังกล่าว

โปรดทราบว่าการจัดโมดูลประเภทนี้ต้องใช้ความพยายามมากขึ้นและอาจต้องมีการรีแฟกทอริงโค้ดที่มีอยู่ของแอป ดังนั้นโปรดพิจารณาอย่างรอบคอบว่าฟีเจอร์ใดของแอปจะได้รับประโยชน์มากที่สุดจากการเปิดให้ผู้ใช้ใช้งานได้ตามต้องการ อ่านแนวทางปฏิบัติแนะนำเกี่ยวกับ UX สำหรับการนำส่งตามคำขอเพื่อให้เข้าใจกรณีการใช้งานและหลักเกณฑ์ที่เหมาะสมที่สุดสำหรับฟีเจอร์แบบออนดีมานด์

หากต้องการค่อยๆ แยกฟีเจอร์ของแอปออกเป็นโมดูลเมื่อเวลาผ่านไป โดยไม่ต้องเปิดใช้ตัวเลือกการนำส่งขั้นสูง เช่น การนำส่งตามคำขอ ให้กำหนดค่าการนำส่งเมื่อติดตั้งแทน

หน้านี้จะช่วยคุณเพิ่มโมดูลฟีเจอร์ลงในโปรเจ็กต์แอปและกำหนดค่าสำหรับการนำส่งแบบออนดีมานด์ ก่อนเริ่มต้น โปรดตรวจสอบว่าคุณใช้ Android Studio 3.5 ขึ้นไปและปลั๊กอิน Android Gradle 3.5.0 ขึ้นไป

กำหนดค่าโมดูลใหม่สำหรับการนำส่งแบบออนดีมานด์

วิธีที่ง่ายที่สุดในการสร้างโมดูลฟีเจอร์ใหม่คือการใช้ Android Studio 3.5 ขึ้นไป เนื่องจากโมดูลฟีเจอร์มีความจำเป็นต้องใช้โมดูลแอปพื้นฐานอยู่แล้ว คุณจึงเพิ่มโมดูลฟีเจอร์ลงในโปรเจ็กต์แอปที่มีอยู่ได้เท่านั้น

หากต้องการเพิ่มโมดูลฟีเจอร์ลงในโปรเจ็กต์แอปโดยใช้ Android Studio ให้ทําดังนี้

  1. เปิดโปรเจ็กต์แอปใน IDE หากยังไม่ได้ดำเนินการ
  2. เลือกไฟล์ > ใหม่ > โมดูลใหม่จากแถบเมนู
  3. ในกล่องโต้ตอบสร้างข้อบังคับใหม่ ให้เลือกข้อบังคับฟีเจอร์แบบไดนามิก แล้วคลิกถัดไป
  4. ในส่วนกำหนดค่าโมดูลใหม่ ให้ทำดังนี้
    1. เลือกโมดูลแอปพลิเคชันพื้นฐานสําหรับโปรเจ็กต์แอปจากเมนูแบบเลื่อนลง
    2. ระบุชื่อโมดูล IDE จะใช้ชื่อนี้เพื่อระบุข้อบังคับเป็นโปรเจ็กต์ย่อย Gradle ในไฟล์การตั้งค่า Gradle เมื่อคุณสร้าง App Bundle ทาง Gradle จะใช้องค์ประกอบสุดท้ายของชื่อโปรเจ็กต์ย่อยเพื่อแทรกแอตทริบิวต์ <manifest split> ในไฟล์ Manifest ของโมดูลฟีเจอร์
    3. ระบุชื่อแพ็กเกจของโมดูล โดยค่าเริ่มต้น Android Studio จะแนะนำชื่อแพ็กเกจที่รวมชื่อแพ็กเกจรูทของโมดูลพื้นฐานเข้ากับชื่อโมดูลที่คุณระบุไว้ในขั้นตอนก่อนหน้า
    4. เลือกระดับ API ขั้นต่ำที่คุณต้องการให้โมดูลรองรับ ค่านี้ควรตรงกับค่าของโมดูลพื้นฐาน
  5. คลิกถัดไป
  6. ในส่วนตัวเลือกการดาวน์โหลดโมดูล ให้ทำดังนี้

    1. ระบุชื่อข้อบังคับโดยใช้อักขระได้สูงสุด 50 ตัว แพลตฟอร์มจะใช้ชื่อนี้เพื่อระบุข้อบังคับต่อผู้ใช้ เช่น เมื่อยืนยันว่าผู้ใช้ต้องการดาวน์โหลดข้อบังคับหรือไม่ ด้วยเหตุนี้ โมดูลพื้นฐานของแอปจึงต้องมีชื่อโมดูลเป็นทรัพยากรสตริง ซึ่งคุณแปลได้ เมื่อสร้างโมดูลโดยใช้ Android Studio IDE จะเพิ่มทรัพยากรสตริงลงในโมดูลพื้นฐานให้คุณและแทรกรายการต่อไปนี้ในไฟล์ Manifest ของโมดูลฟีเจอร์

      <dist:module
          ...
          dist:title="@string/feature_title">
      </dist:module>
      
    2. ในเมนูแบบเลื่อนลงในส่วนการรวมเวลาติดตั้ง ให้เลือกไม่รวมข้อบังคับในเวลาติดตั้ง Android Studio จะแทรกข้อมูลต่อไปนี้ในไฟล์ Manifest ของโมดูลเพื่อให้สอดคล้องกับตัวเลือกของคุณ

      <dist:module ... >
        <dist:delivery>
            <dist:on-demand/>
        </dist:delivery>
      </dist:module>
      
    3. เลือกช่องข้างการผสานหากต้องการให้โมดูลนี้พร้อมใช้งานในอุปกรณ์ที่ใช้ Android 4.4 (API ระดับ 20) หรือต่ำกว่า และรวมอยู่ใน APK หลายรายการ ซึ่งหมายความว่าคุณสามารถเปิดใช้ลักษณะการทำงานแบบออนดีมานด์สําหรับข้อบังคับนี้ และปิดใช้การผสานเพื่อยกเว้นข้อบังคับนี้จากอุปกรณ์ที่ไม่รองรับการดาวน์โหลดและการติดตั้ง APK แบบแยก Android Studio จะแทรกข้อมูลต่อไปนี้ในไฟล์ Manifest ของโมดูลเพื่อให้สอดคล้องกับตัวเลือกของคุณ

      <dist:module ...>
          <dist:fusing dist:include="true | false" />
      </dist:module>
      
  7. คลิกเสร็จสิ้น

หลังจาก Android Studio สร้างโมดูลเสร็จแล้ว ให้ตรวจสอบเนื้อหาของโมดูลด้วยตนเองจากแผงโปรเจ็กต์ (เลือกดู > หน้าต่างเครื่องมือ > โปรเจ็กต์จากแถบเมนู) โค้ด ทรัพยากร และองค์กรเริ่มต้นควรคล้ายกับของโมดูลแอปมาตรฐาน

ถัดไป คุณจะต้องติดตั้งใช้งานฟังก์ชันการทำงานแบบติดตั้งเมื่อต้องการโดยใช้ไลบรารี Play Feature Delivery

รวมไลบรารีการนำส่งฟีเจอร์ Play ไว้ในโปรเจ็กต์

ก่อนเริ่มต้น คุณต้องเพิ่มคลังการส่งฟีเจอร์ของ Play ลงในโปรเจ็กต์ก่อน

ขอข้อบังคับในแบบออนดีมานด์

เมื่อแอปต้องใช้โมดูลฟีเจอร์ แอปจะขอโมดูลได้ขณะที่แสดงอยู่เบื้องหน้าผ่านคลาส SplitInstallManager เมื่อส่งคำขอ แอปของคุณต้องระบุชื่อของโมดูลตามที่องค์ประกอบ split ในไฟล์ Manifest ของโมดูลเป้าหมายกำหนด เมื่อคุณสร้างโมดูลฟีเจอร์โดยใช้ Android Studio ระบบการสร้างจะใช้ชื่อโมดูลที่คุณระบุเพื่อแทรกพร็อพเพอร์ตี้นี้ลงในไฟล์ Manifest ของโมดูล ณ เวลาคอมไพล์ ดูข้อมูลเพิ่มเติมได้ที่หัวข้อไฟล์ Manifest ของโมดูลฟีเจอร์

ตัวอย่างเช่น ลองพิจารณาแอปที่มีโมดูลแบบออนดีมานด์เพื่อจับภาพและส่งข้อความรูปภาพโดยใช้กล้องของอุปกรณ์ และโมดูลแบบออนดีมานด์นี้ระบุ split="pictureMessages" ในไฟล์ Manifest ตัวอย่างต่อไปนี้ใช้ SplitInstallManager เพื่อขอข้อบังคับ pictureMessages (พร้อมกับข้อบังคับเพิ่มเติมสําหรับตัวกรองโปรโมชันบางรายการ)

Kotlin

// Creates an instance of SplitInstallManager.
val splitInstallManager = SplitInstallManagerFactory.create(context)

// Creates a request to install a module.
val request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build()

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener { sessionId -> ... }
    .addOnFailureListener { exception ->  ... }

Java

// Creates an instance of SplitInstallManager.
SplitInstallManager splitInstallManager =
    SplitInstallManagerFactory.create(context);

// Creates a request to install a module.
SplitInstallRequest request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build();

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener(sessionId -> { ... })
    .addOnFailureListener(exception -> { ... });

เมื่อแอปขอโมดูลแบบออนดีมานด์ ไลบรารีการนำส่งฟีเจอร์ Play จะใช้กลยุทธ์ "ยิงแล้วลืม" กล่าวคือ เครื่องมือจะส่งคําขอดาวน์โหลดข้อบังคับไปยังแพลตฟอร์ม แต่ไม่ตรวจสอบว่าการติดตั้งสําเร็จหรือไม่ หากต้องการดำเนินการต่อในเส้นทางของผู้ใช้หลังจากการติดตั้งหรือจัดการข้อผิดพลาดอย่างราบรื่น โปรดตรวจสอบสถานะคำขอ

หมายเหตุ: คุณขอโมดูลฟีเจอร์ที่ติดตั้งในอุปกรณ์อยู่แล้วได้ API จะถือว่าคําขอเสร็จสมบูรณ์ทันทีหากตรวจพบว่ามีการติดตั้งโมดูลแล้ว นอกจากนี้ หลังจากติดตั้งโมดูลแล้ว Google Play จะอัปเดตโมดูลดังกล่าวโดยอัตโนมัติ กล่าวคือ เมื่อคุณอัปโหลด App Bundle เวอร์ชันใหม่ แพลตฟอร์มจะอัปเดต APK ที่ติดตั้งทั้งหมดซึ่งเป็นของแอปคุณ ดูข้อมูลเพิ่มเติมได้ที่จัดการการอัปเดตแอป

หากต้องการเข้าถึงโค้ดและทรัพยากรของโมดูล แอปของคุณต้องเปิดใช้ SplitCompat โปรดทราบว่าคุณไม่จำเป็นต้องใช้ SplitCompat สำหรับ Instant App ของ Android

เลื่อนการติดตั้งโมดูลแบบออนดีมานด์

หากไม่ต้องการให้แอปดาวน์โหลดและติดตั้งข้อบังคับเฉพาะเมื่อมีความต้องการทันที คุณสามารถเลื่อนการติดตั้งไว้เมื่อแอปทำงานอยู่เบื้องหลัง เช่น หากต้องการโหลดเนื้อหาส่งเสริมการขายบางส่วนไว้ล่วงหน้าเพื่อเปิดตัวแอปในภายหลัง

คุณสามารถระบุโมดูลที่จะดาวน์โหลดในภายหลังได้โดยใช้วิธี deferredInstall() ดังที่แสดงด้านล่าง และต่างจาก SplitInstallManager.startInstall() ตรงที่แอปของคุณไม่จำเป็นต้องอยู่เบื้องหน้าเพื่อเริ่มส่งคำขอการติดตั้งที่เลื่อนเวลาไว้

Kotlin

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(listOf("promotionalFilters"))

Java

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(Arrays.asList("promotionalFilters"));

คำขอสำหรับการติดตั้งที่เลื่อนเวลาไว้จะดำเนินการอย่างดีที่สุดและคุณจะติดตามความคืบหน้าไม่ได้ ดังนั้นก่อนพยายามเข้าถึงโมดูลที่คุณระบุไว้สําหรับการติดตั้งที่เลื่อนออกไป คุณควรตรวจสอบว่าได้ติดตั้งโมดูลแล้ว หากต้องการใช้ข้อบังคับทันที ให้ใช้ SplitInstallManager.startInstall() เพื่อขอข้อบังคับแทน ตามที่แสดงในส่วนก่อนหน้า

ตรวจสอบสถานะคำขอ

หากต้องการอัปเดตแถบความคืบหน้า เรียกใช้ Intent หลังจากการติดตั้ง หรือจัดการข้อผิดพลาดของคําขออย่างราบรื่น คุณต้องรอการอัปเดตสถานะจากงาน SplitInstallManager.startInstall() แบบแอซิงโครนัส ก่อนที่จะเริ่มรับข้อมูลอัปเดตสำหรับคำขอติดตั้ง ให้ลงทะเบียนโปรแกรมรับฟังและรับรหัสเซสชันสำหรับคำขอดังที่แสดงด้านล่าง

Kotlin

// Initializes a variable to later track the session ID for a given request.
var mySessionId = 0

// Creates a listener for request status updates.
val listener = SplitInstallStateUpdatedListener { state ->
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
}

// Registers the listener.
splitInstallManager.registerListener(listener)

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener { sessionId -> mySessionId = sessionId }
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener { exception ->
        // Handle request errors.
    }

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener)

Java

// Initializes a variable to later track the session ID for a given request.
int mySessionId = 0;

// Creates a listener for request status updates.
SplitInstallStateUpdatedListener listener = state -> {
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
};

// Registers the listener.
splitInstallManager.registerListener(listener);

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener(sessionId -> { mySessionId = sessionId; })
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener(exception -> {
        // Handle request errors.
    });

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener);

จัดการข้อผิดพลาดของคําขอ

โปรดทราบว่าการติดตั้งโมดูลฟีเจอร์แบบออนดีมานด์อาจไม่สำเร็จในบางครั้ง เช่นเดียวกับที่การติดตั้งแอปไม่สำเร็จเสมอไป การติดตั้งไม่สำเร็จอาจเกิดจากปัญหาต่างๆ เช่น พื้นที่เก็บข้อมูลในอุปกรณ์เหลือน้อย ไม่ได้เชื่อมต่อเครือข่าย หรือผู้ใช้ไม่ได้ลงชื่อเข้าใช้ Google Play Store ดูคำแนะนำเกี่ยวกับวิธีจัดการสถานการณ์เหล่านี้อย่างราบรื่นจากมุมมองของผู้ใช้ได้ในหลักเกณฑ์ UX สำหรับการแสดงเนื้อหาแบบออนดีมานด์

ในแง่โค้ด คุณควรจัดการกับการดาวน์โหลดหรือติดตั้งโมดูลที่ไม่สําเร็จโดยใช้ addOnFailureListener() ดังที่แสดงด้านล่าง

Kotlin

splitInstallManager
    .startInstall(request)
    .addOnFailureListener { exception ->
        when ((exception as SplitInstallException).errorCode) {
            SplitInstallErrorCode.NETWORK_ERROR -> {
                // Display a message that requests the user to establish a
                // network connection.
            }
            SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED -> checkForActiveDownloads()
            ...
        }
    }

fun checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .sessionStates
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                // Check for active sessions.
                for (state in task.result) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        }
}

Java

splitInstallManager
    .startInstall(request)
    .addOnFailureListener(exception -> {
        switch (((SplitInstallException) exception).getErrorCode()) {
            case SplitInstallErrorCode.NETWORK_ERROR:
                // Display a message that requests the user to establish a
                // network connection.
                break;
            case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED:
                checkForActiveDownloads();
            ...
    });

void checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .getSessionStates()
        .addOnCompleteListener( task -> {
            if (task.isSuccessful()) {
                // Check for active sessions.
                for (SplitInstallSessionState state : task.getResult()) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        });
}

ตารางด้านล่างอธิบายสถานะข้อผิดพลาดที่แอปอาจต้องจัดการ

รหัสข้อผิดพลาด คำอธิบาย การดำเนินการที่แนะนำ
ACTIVE_SESSIONS_LIMIT_EXCEEDED คำขอถูกปฏิเสธเนื่องจากมีคำขออย่างน้อย 1 รายการที่กําลังดาวน์โหลดอยู่ ตรวจสอบว่ามีคำขอใดที่ยังคงดาวน์โหลดอยู่หรือไม่ ดังที่แสดงในตัวอย่างด้านบน
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 ดูข้อมูลเพิ่มเติมได้ที่ไฟล์ Manifest ของโมดูลฟีเจอร์
NETWORK_ERROR คำขอล้มเหลวเนื่องจากข้อผิดพลาดเกี่ยวกับเครือข่าย แจ้งให้ผู้ใช้เชื่อมต่อเครือข่ายหรือเปลี่ยนไปใช้เครือข่ายอื่น
ACCESS_DENIED แอปลงทะเบียนคําขอไม่ได้เนื่องจากมีสิทธิ์ไม่เพียงพอ ซึ่งมักเกิดขึ้นเมื่อแอปทำงานอยู่เบื้องหลัง ลองส่งคำขอเมื่อแอปกลับมาอยู่เบื้องหน้า
INCOMPATIBLE_WITH_EXISTING_SESSION คำขอมีโมดูลอย่างน้อย 1 รายการที่ขอไปแล้ว แต่ยังติดตั้งไม่สำเร็จ สร้างคำขอใหม่ที่ไม่มีโมดูลที่แอปขอไปแล้ว หรือรอให้โมดูลทั้งหมดที่ขอในขณะนี้ติดตั้งเสร็จก่อนส่งคำขออีกครั้ง

โปรดทราบว่าการขอโมดูลที่ติดตั้งไปแล้วจะไม่ทำให้เกิดข้อผิดพลาด

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 ยังไม่ได้ติดตั้งแอปและดาวน์โหลดฟีเจอร์ไม่ได้ ข้อผิดพลาดนี้จะเกิดขึ้นกับการติดตั้งที่เลื่อนออกไปเท่านั้น หากต้องการให้ผู้ใช้ดาวน์โหลดแอปใน Google Play ให้ใช้ startInstall() ซึ่งสามารถรับการยืนยันของผู้ใช้ที่จำเป็น
INTERNAL_ERROR เกิดข้อผิดพลาดภายใน Play Store ลองส่งคำขออีกครั้ง

หากผู้ใช้ส่งคำขอดาวน์โหลดโมดูลแบบออนดีมานด์และเกิดข้อผิดพลาดขึ้น ให้พิจารณาแสดงกล่องโต้ตอบที่มี 2 ตัวเลือกให้ผู้ใช้ ได้แก่ ลองอีกครั้ง (ซึ่งจะพยายามส่งคำขออีกครั้ง) และยกเลิก (ซึ่งจะยกเลิกคำขอ) คุณควรระบุลิงก์ความช่วยเหลือที่นําผู้ใช้ไปยังศูนย์ช่วยเหลือของ Google Play ด้วยเพื่อรับการสนับสนุนเพิ่มเติม

จัดการการอัปเดตสถานะ

หลังจากลงทะเบียนโปรแกรมรับฟังและบันทึกรหัสเซสชันของคําขอแล้ว ให้ใช้ StateUpdatedListener.onStateUpdate() เพื่อจัดการการเปลี่ยนแปลงสถานะ ดังที่แสดงด้านล่าง

Kotlin

override fun onStateUpdate(state : SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIED) {
       // Retry the request.
       return
    }
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.DOWNLOADING -> {
              val totalBytes = state.totalBytesToDownload()
              val progress = state.bytesDownloaded()
              // Update progress bar.
            }
            SplitInstallSessionStatus.INSTALLED -> {

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

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

Java

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

            case SplitInstallSessionStatus.INSTALLED:

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

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

สถานะที่เป็นไปได้สำหรับคำขอติดตั้งจะอธิบายไว้ในตารางด้านล่าง

สถานะคำขอ คำอธิบาย การดำเนินการที่แนะนำ
รอดำเนินการ คำขอได้รับการยอมรับแล้ว และระบบจะเริ่มดาวน์โหลดในไม่ช้า เริ่มต้นคอมโพเนนต์ UI เช่น แถบความคืบหน้า เพื่อแสดงความคิดเห็นเกี่ยวกับการดาวน์โหลดแก่ผู้ใช้
REQUIRES_USER_CONFIRMATION การดาวน์โหลดต้องได้รับการยืนยันจากผู้ใช้ สถานะนี้มักเกิดขึ้นหากไม่ได้ติดตั้งแอปผ่าน Google Play แจ้งให้ผู้ใช้ยืนยันการดาวน์โหลดฟีเจอร์ผ่าน Google Play ดูข้อมูลเพิ่มเติมได้ที่ส่วนเกี่ยวกับวิธีขอการยืนยันจากผู้ใช้
การดาวน์โหลด อยู่ระหว่างการดาวน์โหลด หากคุณระบุแถบความคืบหน้าสำหรับการดาวน์โหลด ให้ใช้เมธอด SplitInstallSessionState.bytesDownloaded() และ SplitInstallSessionState.totalBytesToDownload() เพื่ออัปเดต UI (ดูตัวอย่างโค้ดด้านบนตารางนี้)
ดาวน์โหลดแล้ว อุปกรณ์ดาวน์โหลดโมดูลแล้วแต่ยังไม่ได้เริ่มการติดตั้ง แอปควรเปิดใช้ SplitCompat เพื่อเข้าถึงโมดูลที่ดาวน์โหลดมาและหลีกเลี่ยงการแสดงสถานะนี้ ซึ่งจําเป็นต่อการเข้าถึงโค้ดและทรัพยากรของข้อบังคับฟีเจอร์
กำลังติดตั้ง อุปกรณ์กำลังติดตั้งโมดูล อัปเดตแถบความคืบหน้า โดยปกติแล้วสถานะนี้จะอยู่ไม่นาน
ติดตั้งแล้ว ติดตั้งโมดูลในอุปกรณ์แล้ว เข้าถึงรหัสและทรัพยากรในข้อบังคับเพื่อดำเนินการต่อในเส้นทางของผู้ใช้

หากโมดูลสําหรับ Android Instant App ที่ทํางานบน Android 8.0 (API ระดับ 26) ขึ้นไป คุณต้องใช้ splitInstallHelper เพื่ออัปเดตคอมโพเนนต์แอปด้วยโมดูลใหม่

FAILED คำขอไม่สำเร็จก่อนที่จะติดตั้งโมดูลในอุปกรณ์ แจ้งให้ผู้ใช้ลองส่งคำขออีกครั้งหรือยกเลิก
การยกเลิก อุปกรณ์กำลังยกเลิกคำขอ ดูข้อมูลเพิ่มเติมได้ที่ส่วนเกี่ยวกับวิธียกเลิกคําขอติดตั้ง
ยกเลิก ยกเลิกคำขอแล้ว

ขอการยืนยันจากผู้ใช้

ในบางกรณี Google Play อาจกำหนดให้ผู้ใช้ต้องยืนยันก่อนจึงจะดำเนินการตามคำขอดาวน์โหลดได้ เช่น Google Play ยังไม่ได้ติดตั้งแอปของคุณ หรือคุณพยายามดาวน์โหลดไฟล์ขนาดใหญ่ผ่านอินเทอร์เน็ตมือถือ ในกรณีเช่นนี้ สถานะสำหรับคำขอจะแสดงเป็น REQUIRES_USER_CONFIRMATION และแอปของคุณจะต้องได้รับการยืนยันจากผู้ใช้ก่อน อุปกรณ์จึงจะดาวน์โหลดและติดตั้งโมดูลในคำขอได้ หากต้องการรับการยืนยัน แอปของคุณควรแจ้งให้ผู้ใช้ดำเนินการดังนี้

Kotlin

override fun onSessionStateUpdate(state: SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          // an activity result launcher registered via registerForActivityResult
          activityResultLauncher)
    }
    ...
 }

Java

@Override void onSessionStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          // an activity result launcher registered via registerForActivityResult
          activityResultLauncher);
    }
    ...
 }

คุณสามารถลงทะเบียนตัวเปิดใช้งานผลการค้นหากิจกรรมได้โดยใช้สัญญาในตัว ActivityResultContracts.StartIntentSenderForResult โปรดดู Activity Result API

สถานะคำขอจะอัปเดตตามคําตอบของผู้ใช้ ดังนี้

  • หากผู้ใช้ยอมรับการยืนยัน สถานะคำขอจะเปลี่ยนเป็นPENDING และระบบจะดำเนินการดาวน์โหลดต่อ
  • หากผู้ใช้ปฏิเสธการยืนยัน สถานะคำขอจะเปลี่ยนเป็น CANCELED
  • หากผู้ใช้ไม่เลือกตัวเลือกก่อนที่กล่องโต้ตอบจะปิดลง สถานะคำขอจะยังคงเป็น REQUIRES_USER_CONFIRMATION แอปของคุณแจ้งให้ผู้ใช้ดำเนินการตามคำขอให้เสร็จสมบูรณ์อีกครั้งได้

หากต้องการรับการเรียกกลับพร้อมคําตอบของผู้ใช้ คุณสามารถลบล้าง ActivityResultCallback ดังที่แสดงด้านล่าง

Kotlin

registerForActivityResult(StartIntentSenderForResult()) { result: ActivityResult -> {
        // Handle the user's decision. For example, if the user selects "Cancel",
        // you may want to disable certain functionality that depends on the module.
    }
}

Java

registerForActivityResult(
    new ActivityResultContracts.StartIntentSenderForResult(),
    new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            // Handle the user's decision. For example, if the user selects "Cancel",
            // you may want to disable certain functionality that depends on the module.
        }
    });

ยกเลิกคำขอติดตั้ง

หากแอปจำเป็นต้องยกเลิกคำขอก่อนที่จะมีการติดตั้ง แอปสามารถเรียกใช้เมธอด cancelInstall() โดยใช้รหัสเซสชันของคำขอ ดังที่แสดงด้านล่าง

Kotlin

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

Java

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

เข้าถึงโมดูล

หากต้องการเข้าถึงโค้ดและทรัพยากรจากโมดูลที่ดาวน์โหลดแล้วหลังจากดาวน์โหลด แอปของคุณจะต้องเปิดใช้ไลบรารี SplitCompat ทั้งสําหรับแอปและแต่ละกิจกรรมในโมดูลฟีเจอร์ที่แอปดาวน์โหลด

อย่างไรก็ตาม โปรดทราบว่าแพลตฟอร์มจะพบข้อจำกัดต่อไปนี้ในการเข้าถึงเนื้อหาของโมดูลเป็นระยะเวลาหนึ่ง (2-3 วันในบางกรณี) หลังจากดาวน์โหลดโมดูล

  • แพลตฟอร์มจะใช้รายการไฟล์ Manifest ใหม่ที่โมดูลนำเสนอไม่ได้
  • แพลตฟอร์มเข้าถึงทรัพยากรของโมดูลสำหรับคอมโพเนนต์ UI ของระบบไม่ได้ เช่น การแจ้งเตือน หากต้องการใช้ทรัพยากรดังกล่าวทันที ให้พิจารณารวมทรัพยากรเหล่านั้นไว้ในโมดูลพื้นฐานของแอป

เปิดใช้ SplitCompat

หากต้องการให้แอปเข้าถึงโค้ดและทรัพยากรจากโมดูลที่ดาวน์โหลดมา คุณจะต้องเปิดใช้ SplitCompat โดยใช้วิธีใดวิธีหนึ่งตามที่อธิบายไว้ในส่วนต่อไปนี้

หลังจากเปิดใช้ SplitCompat สําหรับแอปแล้ว คุณยังต้องเปิดใช้ SplitCompat สําหรับกิจกรรมแต่ละรายการในข้อบังคับของฟีเจอร์ที่คุณต้องการให้แอปเข้าถึงด้วย

ประกาศ SplitCompatApplication ในไฟล์ Manifest

วิธีที่ง่ายที่สุดในการเปิดใช้ SplitCompat คือการประกาศ SplitCompatApplication เป็นคลาสย่อย Application ในไฟล์ Manifest ของแอป ดังที่แสดงด้านล่าง

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

หลังจากติดตั้งแอปในอุปกรณ์แล้ว คุณจะเข้าถึงโค้ดและทรัพยากรจากข้อบังคับของฟีเจอร์ที่ดาวน์โหลดโดยอัตโนมัติ

เรียกใช้ SplitCompat ขณะรันไทม์

นอกจากนี้ คุณยังเปิดใช้ SplitCompat ในกิจกรรมหรือบริการที่เฉพาะเจาะจงขณะรันไทม์ได้ด้วย คุณต้องเปิดใช้ SplitCompat ด้วยวิธีนี้เพื่อเปิดใช้งานกิจกรรมที่รวมอยู่ในข้อบังคับของฟีเจอร์ โดยให้ลบล้าง attachBaseContext ดังที่แสดงด้านล่าง

หากคุณมีคลาส Application ที่กําหนดเอง ให้คลาสนั้นขยายจาก SplitCompatApplication แทน เพื่อเปิดใช้ SplitCompat สําหรับแอป ดังที่แสดงด้านล่าง

Kotlin

class MyApplication : SplitCompatApplication() {
    ...
}

Java

public class MyApplication extends SplitCompatApplication {
    ...
}

SplitCompatApplication เพียงแค่ลบล้าง ContextWrapper.attachBaseContext()เพื่อรวม SplitCompat.install(Context applicationContext) หากไม่ต้องการให้คลาส Application ขยายจาก SplitCompatApplication คุณสามารถลบล้างเมธอด attachBaseContext() ด้วยตนเองได้โดยทำดังนี้

Kotlin

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

Java

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

หากโมดูลแบบออนดีมานด์ของคุณเข้ากันได้กับทั้งแอปด่วนและแอปที่ติดตั้ง คุณจะเรียกใช้ SplitCompat แบบมีเงื่อนไขได้ ดังนี้

Kotlin

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

Java

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

เปิดใช้ SplitCompat สําหรับกิจกรรมของโมดูล

หลังจากเปิดใช้ SplitCompat สําหรับแอปฐานแล้ว คุณต้องเปิดใช้ SplitCompat สําหรับกิจกรรมแต่ละรายการที่แอปดาวน์โหลดในโมดูลฟีเจอร์ โดยให้ใช้วิธีการ SplitCompat.installActivity() ดังนี้

Kotlin

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

Java

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

เข้าถึงคอมโพเนนต์ที่กําหนดไว้ในโมดูลฟีเจอร์

เริ่มกิจกรรมที่กําหนดไว้ในโมดูลฟีเจอร์

คุณสามารถเปิดใช้งานกิจกรรมที่กําหนดไว้ในโมดูลฟีเจอร์ได้โดยใช้ startActivity() หลังจากที่เปิดใช้ SplitCompat

Kotlin

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

Java

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

พารามิเตอร์แรกใน setClassName คือชื่อแพ็กเกจของแอป และพารามิเตอร์ที่ 2 คือชื่อคลาสแบบเต็มของกิจกรรม

เมื่อคุณมีกิจกรรมในโมดูลฟีเจอร์ที่ดาวน์โหลดแบบออนดีมานด์ คุณต้องเปิดใช้ SplitCompat ในกิจกรรม

เริ่มบริการที่กําหนดไว้ในโมดูลฟีเจอร์

คุณสามารถเปิดใช้บริการที่กําหนดไว้ในโมดูลฟีเจอร์ได้โดยใช้ startService() หลังจากที่เปิดใช้ SplitCompat

Kotlin

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

Java

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

ส่งออกคอมโพเนนต์ที่กําหนดไว้ในโมดูลฟีเจอร์

คุณไม่ควรรวมคอมโพเนนต์ Android ที่ส่งออกไว้ในโมดูลที่ไม่บังคับ

ระบบบิลด์จะผสานรายการไฟล์ Manifest ของโมดูลทั้งหมดไว้ในโมดูลฐาน หากโมดูลที่ไม่บังคับมีคอมโพเนนต์ที่ส่งออก ผู้ใช้จะเข้าถึงได้แม้ว่าจะยังไม่ได้ติดตั้งโมดูลก็ตาม และอาจทำให้ระบบขัดข้องเนื่องจากไม่มีโค้ดเมื่อเรียกใช้จากแอปอื่น

ปัญหานี้จะไม่เกิดขึ้นกับคอมโพเนนต์ภายใน เนื่องจากมีเพียงแอปเท่านั้นที่เข้าถึงได้ แอปจึงตรวจสอบได้ว่าได้ติดตั้งโมดูลแล้วก่อนที่จะเข้าถึงคอมโพเนนต์

หากต้องการคอมโพเนนต์ที่ส่งออกและต้องการให้เนื้อหาของคอมโพเนนต์อยู่ในโมดูลที่ไม่บังคับ ให้ลองใช้รูปแบบพร็อกซี ซึ่งทำได้โดยการเพิ่มคอมโพเนนต์พร็อกซีที่ส่งออกในฐาน เมื่อเข้าถึง คอมโพเนนต์พร็อกซีจะตรวจสอบว่ามีโมดูลที่มีเนื้อหาหรือไม่ หากมีโมดูลอยู่ คอมโพเนนต์พร็อกซีจะเริ่มต้นคอมโพเนนต์ภายในจากโมดูลผ่าน Intent ได้ โดยส่งต่อ Intent จากแอปที่เรียกใช้ หากไม่มีโมดูล คอมโพเนนต์จะดาวน์โหลดโมดูลหรือแสดงข้อความแสดงข้อผิดพลาดที่เหมาะสมไปยังแอปที่เรียกใช้ได้

เข้าถึงโค้ดและทรัพยากรจากโมดูลที่ติดตั้ง

หากเปิดใช้ SplitCompat สำหรับบริบทแอปพลิเคชันพื้นฐานและกิจกรรมในโมดูลฟีเจอร์ คุณจะใช้โค้ดและทรัพยากรจากโมดูลฟีเจอร์ได้ราวกับว่าเป็นส่วนหนึ่งของ APK พื้นฐาน เมื่อติดตั้งโมดูลที่ไม่บังคับแล้ว

เข้าถึงรหัสจากโมดูลอื่น

เข้าถึงโค้ดฐานจากโมดูล

โมดูลอื่นๆ สามารถใช้โค้ดที่อยู่ในโมดูลฐานได้โดยตรง คุณไม่จำเป็นต้องดำเนินการใดๆ เป็นพิเศษ เพียงนำเข้าและใช้คลาสที่ต้องการ

เข้าถึงโค้ดโมดูลจากโมดูลอื่น

วัตถุหรือคลาสภายในโมดูลจะเข้าถึงแบบคงที่จากโมดูลอื่นโดยตรงไม่ได้ แต่เข้าถึงได้แบบอ้อมโดยใช้รีเฟล็กชัน

คุณควรระวังความถี่ที่เหตุการณ์นี้เกิดขึ้น เนื่องจากต้นทุนด้านประสิทธิภาพของการสะท้อน สำหรับ Use Case ที่ซับซ้อน ให้ใช้เฟรมเวิร์กการฉีด Dependency เช่น Dagger 2 เพื่อรับประกันการเรียกใช้การสะท้อนเพียงครั้งเดียวตลอดอายุการใช้งานของแอปพลิเคชัน

เราขอแนะนําให้กําหนดอินเทอร์เฟซในโมดูลฐานและการใช้งานในโมดูลฟีเจอร์เพื่อลดความซับซ้อนของการโต้ตอบกับออบเจ็กต์หลังจากการสร้างอินสแตนซ์ เช่น

Kotlin

// In the base module
interface MyInterface {
  fun hello(): String
}

// In the feature module
object MyInterfaceImpl : MyInterface {
  override fun hello() = "Hello"
}

// In the base module, where we want to access the feature module code
val stringFromModule = (Class.forName("com.package.module.MyInterfaceImpl")
    .kotlin.objectInstance as MyInterface).hello();

Java

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

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

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

เข้าถึงแหล่งข้อมูลและชิ้นงานจากโมดูลอื่น

เมื่อติดตั้งโมดูลแล้ว คุณจะเข้าถึงทรัพยากรและชิ้นงานภายในโมดูลได้ด้วยวิธีมาตรฐาน โดยมีข้อจำกัด 2 ข้อดังนี้

  • หากคุณเข้าถึงทรัพยากรจากโมดูลอื่น โมดูลนั้นจะไม่มีสิทธิ์เข้าถึงตัวระบุทรัพยากร แต่ยังคงเข้าถึงทรัพยากรโดยใช้ชื่อได้ โปรดทราบว่าแพ็กเกจที่จะใช้อ้างอิงทรัพยากรคือแพ็กเกจของโมดูลที่กําหนดทรัพยากร
  • หากต้องการเข้าถึงชิ้นงานหรือทรัพยากรที่อยู่ในโมดูลที่ติดตั้งใหม่จากโมดูลที่ติดตั้งไว้แล้วของแอป คุณต้องดำเนินการโดยใช้บริบทแอปพลิเคชัน บริบทของคอมโพเนนต์ที่พยายามเข้าถึงทรัพยากรจะยังไม่ได้รับการอัปเดต หรือจะสร้างคอมโพเนนต์นั้นขึ้นมาใหม่ (เช่น การเรียกใช้ Activity.recreate()) หรือติดตั้ง SplitCompat อีกครั้งในคอมโพเนนต์นั้นหลังจากการติดตั้งข้อบังคับของฟีเจอร์ก็ได้

โหลดโค้ดเนทีฟในแอปโดยใช้การแสดงโฆษณาแบบออนดีมานด์

เราขอแนะนำให้ใช้ ReLinker เพื่อโหลดไลบรารีเนทีฟทั้งหมดเมื่อใช้การนำส่งโมดูลฟีเจอร์แบบออนดีมานด์ ReLinker จะแก้ไขปัญหาในการโหลดไลบรารีแบบเนทีฟหลังจากการติดตั้งข้อบังคับของฟีเจอร์ ดูข้อมูลเพิ่มเติมเกี่ยวกับ ReLinker ได้ในเคล็ดลับ JNI ของ Android

โหลดโค้ดเนทีฟจากโมดูลที่ไม่บังคับ

เมื่อติดตั้งส่วนแยกแล้ว เราขอแนะนำให้โหลดโค้ดเนทีฟผ่าน ReLinker สำหรับแอปด่วน คุณควรใช้วิธีการพิเศษนี้

หากคุณใช้ System.loadLibrary() เพื่อโหลดโค้ดเนทีฟและไลบรารีเนทีฟของคุณต้องอาศัยไลบรารีอื่นในโมดูล คุณต้องโหลดไลบรารีอื่นนั้นด้วยตนเองก่อน หากคุณใช้ ReLinker การดำเนินการที่เทียบเท่าคือ Relinker.recursively().loadLibrary()

หากคุณใช้ dlopen() ในโค้ดเนทีฟเพื่อโหลดไลบรารีที่กําหนดไว้ในข้อบังคับของข้อบังคับ dlopen() จะไม่ทํางานกับเส้นทางไลบรารีแบบสัมพัทธ์ วิธีแก้ปัญหาที่ดีที่สุดคือการดึงข้อมูลเส้นทางแบบสัมบูรณ์ของไลบรารีจากโค้ด Java ผ่าน ClassLoader.findLibrary() แล้วนำไปใช้ในคําเรียก dlopen() ดำเนินการนี้ก่อนป้อนโค้ดเนทีฟหรือใช้การเรียก JNI จากโค้ดเนทีฟไปยัง Java

เข้าถึง Android Instant Apps ที่ติดตั้งไว้

หลังจากโมดูล Instant App ของ Android รายงานเป็น INSTALLED คุณจะเข้าถึงโค้ดและทรัพยากรของโมดูลได้โดยใช้บริบทของแอปที่รีเฟรช บริบทที่แอปของคุณสร้างขึ้นก่อนการติดตั้งโมดูล (เช่น บริบทที่จัดเก็บไว้ในตัวแปรอยู่แล้ว) จะไม่มีเนื้อหาของโมดูลใหม่ แต่บริบทใหม่จะเพิ่มได้ ซึ่งทำได้โดยใช้ createPackageContext ตัวอย่างเช่น

Kotlin

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

Java

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

Android Instant Apps ใน Android 8.0 ขึ้นไป

เมื่อขอโมดูลแบบออนดีมานด์สําหรับแอป Instant ของ Android ใน Android 8.0 (API ระดับ 26) ขึ้นไป หลังจากคําขอติดตั้งรายงานเป็น INSTALLED คุณต้องอัปเดตแอปด้วยบริบทของโมดูลใหม่ผ่านการเรียกใช้ SplitInstallHelper.updateAppInfo(Context context) มิเช่นนั้นแอปจะยังไม่รู้จักโค้ดและทรัพยากรของโมดูล หลังจากอัปเดตข้อมูลเมตาของแอปแล้ว คุณควรโหลดเนื้อหาของโมดูลในระหว่างเหตุการณ์เธรดหลักครั้งถัดไปโดยการเรียกใช้ Handler ใหม่ ดังที่แสดงด้านล่าง

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                // You need to perform the following only for Android Instant Apps
                // running on Android 8.0 (API level 26) and higher.
                if (BuildCompat.isAtLeastO()) {
                    // Updates the app’s context with the code and resources of the
                    // installed module.
                    SplitInstallHelper.updateAppInfo(context)
                    Handler().post {
                        // Loads contents from the module using AssetManager
                        val am = context.assets
                        ...
                    }
                }
            }
        }
    }
}

Java

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
            // You need to perform the following only for Android Instant Apps
            // running on Android 8.0 (API level 26) and higher.
            if (BuildCompat.isAtLeastO()) {
                // Updates the app’s context with the code and resources of the
                // installed module.
                SplitInstallHelper.updateAppInfo(context);
                new Handler().post(new Runnable() {
                    @Override public void run() {
                        // Loads contents from the module using AssetManager
                        AssetManager am = context.getAssets();
                        ...
                    }
                });
            }
        }
    }
}

โหลดไลบรารี C/C++

หากต้องการโหลดไลบรารี C/C++ จากโมดูลที่อุปกรณ์ดาวน์โหลดไว้ใน Instant App อยู่แล้ว ให้ใช้ SplitInstallHelper.loadLibrary(Context context, String libName) ดังที่แสดงด้านล่าง

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.INSTALLED -> {
                // Updates the app’s context as soon as a module is installed.
                val newContext = context.createPackageContext(context.packageName, 0)
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”)
                ...
            }
        }
    }
}

Java

public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.INSTALLED:
                // Updates the app’s context as soon as a module is installed.
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”);
                ...
        }
    }
}

ข้อจำกัดที่ทราบ

  • คุณไม่สามารถใช้ Android WebView ในกิจกรรมที่เข้าถึงทรัพยากรหรือชิ้นงานจากโมดูลที่ไม่บังคับ ปัญหานี้เกิดจากความไม่เข้ากันได้ระหว่าง WebView กับ SplitCompat ใน Android API ระดับ 28 หรือต่ำกว่า
  • คุณไม่สามารถแคชออบเจ็กต์ ApplicationInfo ของ Android, เนื้อหาของออบเจ็กต์เหล่านั้น หรือออบเจ็กต์ที่มีออบเจ็กต์ดังกล่าวภายในแอปของคุณได้ คุณควรดึงข้อมูลออบเจ็กต์เหล่านี้จากบริบทของแอปตามที่จำเป็นเสมอ การแคชออบเจ็กต์ดังกล่าวอาจทำให้แอปขัดข้องเมื่อติดตั้งโมดูลฟีเจอร์

จัดการโมดูลที่ติดตั้ง

หากต้องการตรวจสอบว่าขณะนี้มีการติดตั้งโมดูลฟีเจอร์ใดในอุปกรณ์บ้าง ให้เรียกใช้ SplitInstallManager.getInstalledModules() ซึ่งจะแสดงผล Set<String> ชื่อของโมดูลที่ติดตั้งไว้ดังที่แสดงด้านล่าง

Kotlin

val installedModules: Set<String> = splitInstallManager.installedModules

Java

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

ถอนการติดตั้งโมดูล

คุณสามารถขอให้อุปกรณ์ถอนการติดตั้งโมดูลได้โดยเรียกใช้ SplitInstallManager.deferredUninstall(List<String> moduleNames) ดังที่แสดงด้านล่าง

Kotlin

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

Java

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

การถอนการติดตั้งโมดูลจะไม่เกิดขึ้นทันที กล่าวคือ อุปกรณ์จะถอนการติดตั้งแอปในเบื้องหลังตามที่จำเป็นเพื่อประหยัดพื้นที่เก็บข้อมูล คุณสามารถยืนยันว่าอุปกรณ์ได้ลบโมดูลแล้วโดยการเรียกใช้ SplitInstallManager.getInstalledModules() และตรวจสอบผลลัพธ์ตามที่อธิบายไว้ในส่วนก่อนหน้า

ดาวน์โหลดแหล่งข้อมูลภาษาเพิ่มเติม

เมื่อใช้ App Bundle อุปกรณ์จะดาวน์โหลดเฉพาะโค้ดและทรัพยากรที่จําเป็นต่อการใช้งานแอป ดังนั้นสําหรับทรัพยากรภาษา อุปกรณ์ของผู้ใช้จะดาวน์โหลดเฉพาะทรัพยากรภาษาของแอปที่ตรงกับภาษาอย่างน้อย 1 ภาษาที่เลือกไว้ในการตั้งค่าของอุปกรณ์

หากต้องการให้แอปเข้าถึงทรัพยากรด้านภาษาเพิ่มเติม เช่น เพื่อติดตั้งใช้งานเครื่องมือเลือกภาษาในแอป คุณสามารถใช้คลังการนำส่งฟีเจอร์ของ Play เพื่อดาวน์โหลดทรัพยากรดังกล่าวตามต้องการ กระบวนการนี้คล้ายกับการดาวน์โหลดโมดูลฟีเจอร์ดังที่แสดงด้านล่าง

Kotlin

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply()
...

// Creates a request to download and install additional language resources.
val request = SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build()

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request)

Java

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply();
...

// Creates a request to download and install additional language resources.
SplitInstallRequest request =
    SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build();

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request);

ระบบจะจัดการคำขอนี้เสมือนว่าเป็นคำขอสำหรับข้อบังคับของฟีเจอร์ กล่าวคือ คุณสามารถตรวจสอบสถานะคำขอได้ตามปกติ

หากแอปไม่จำเป็นต้องใช้ทรัพยากรภาษาเพิ่มเติมในทันที คุณสามารถเลื่อนการติดตั้งไว้เมื่อแอปทำงานอยู่เบื้องหลังได้ ดังที่แสดงด้านล่าง

Kotlin

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

Java

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

เข้าถึงแหล่งข้อมูลภาษาที่ดาวน์โหลด

หากต้องการเข้าถึงทรัพยากรภาษาที่ดาวน์โหลดไว้ แอปของคุณต้องเรียกใช้วิธี SplitCompat.installActivity() ภายในวิธี attachBaseContext() ของกิจกรรมแต่ละรายการที่ต้องเข้าถึงทรัพยากรเหล่านั้น ดังที่แสดงด้านล่าง

Kotlin

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

Java

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

สําหรับกิจกรรมแต่ละรายการที่คุณต้องการใช้ทรัพยากรภาษาที่แอปดาวน์โหลดมา ให้อัปเดตบริบทพื้นฐานและตั้งค่าภาษาใหม่ผ่านConfiguration ดังนี้

Kotlin

override fun attachBaseContext(base: Context) {
  val configuration = Configuration()
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
  val context = base.createConfigurationContext(configuration)
  super.attachBaseContext(context)
  SplitCompat.install(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
  Configuration configuration = new Configuration();
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
  Context context = base.createConfigurationContext(configuration);
  super.attachBaseContext(context);
  SplitCompat.install(this);
}

คุณต้องสร้างกิจกรรมอีกครั้งหลังจากติดตั้งภาษาใหม่และพร้อมใช้งานแล้วเพื่อให้การเปลี่ยนแปลงเหล่านี้มีผล คุณสามารถใช้เมธอด Activity#recreate()

Kotlin

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

Java

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

ถอนการติดตั้งแหล่งข้อมูลภาษาเพิ่มเติม

คุณสามารถถอนการติดตั้งทรัพยากรเพิ่มเติมได้ทุกเมื่อ เช่นเดียวกับข้อบังคับของฟีเจอร์ ก่อนขอถอนการติดตั้ง คุณอาจต้องตรวจสอบก่อนว่ามีภาษาใดบ้างที่ติดตั้งอยู่ในปัจจุบัน ดังนี้

Kotlin

val installedLanguages: Set<String> = splitInstallManager.installedLanguages

Java

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

จากนั้นคุณเลือกภาษาที่จะถอนการติดตั้งได้โดยใช้วิธี deferredLanguageUninstall() ดังที่แสดงด้านล่าง

Kotlin

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

Java

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

ทดสอบการติดตั้งโมดูลในเครื่อง

คลังการส่งฟีเจอร์ของ Play ช่วยให้คุณทดสอบความสามารถของแอปในการดำเนินการต่อไปนี้ได้โดยไม่ต้องเชื่อมต่อกับ Play Store

หน้านี้อธิบายวิธีทำให้ APK ที่แยกของแอปใช้งานได้ในอุปกรณ์ทดสอบเพื่อให้การนำส่งฟีเจอร์ Play ใช้ APK เหล่านั้นโดยอัตโนมัติเพื่อจำลองการขอ ดาวน์โหลด และติดตั้งโมดูลจาก Play Store

แม้ว่าคุณจะไม่ต้องทำการเปลี่ยนแปลงใดๆ กับตรรกะของแอป แต่ก็ต้องปฏิบัติตามข้อกำหนดต่อไปนี้

  • ดาวน์โหลดและติดตั้ง bundletool เวอร์ชันล่าสุด คุณต้องมี bundletool เพื่อสร้างชุด APK ที่ติดตั้งได้ชุดใหม่จากกลุ่มของแอป

สร้างชุด APK

หากยังไม่ได้สร้าง ให้สร้าง APK แบบแยกของแอป ดังนี้

  1. สร้าง App Bundle สําหรับแอปโดยใช้วิธีใดวิธีหนึ่งต่อไปนี้
  2. ใช้ bundletool เพื่อสร้างชุดAPK สําหรับการกําหนดค่าอุปกรณ์ทั้งหมดด้วยคําสั่งต่อไปนี้

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

Flag --local-testing มี meta-data ในไฟล์ Manifest ของ APK ซึ่งจะแจ้งให้ Play Feature Delivery Library ทราบว่าให้ใช้ APK ที่แยกในเครื่องเพื่อทดสอบการติดตั้งโมดูลฟีเจอร์โดยไม่ต้องเชื่อมต่อกับ Play Store

ติดตั้งใช้งานแอปในอุปกรณ์

หลังจากสร้างชุด APK โดยใช้ Flag --local-testing แล้ว ให้ใช้ bundletool เพื่อติดตั้งแอปเวอร์ชันพื้นฐานและโอน APK เพิ่มเติมไปยังพื้นที่เก็บข้อมูลในเครื่องของอุปกรณ์ คุณทําทั้ง 2 การดำเนินการได้ด้วยคำสั่งต่อไปนี้

bundletool install-apks --apks my_app.apks

เมื่อคุณเริ่มแอปและดำเนินการตามขั้นตอนของผู้ใช้เพื่อดาวน์โหลดและติดตั้งโมดูลฟีเจอร์จนเสร็จสมบูรณ์แล้ว ไลบรารีการนำส่งฟีเจอร์ของ Play จะใช้ APK ที่ bundletool โอนไปยังพื้นที่เก็บข้อมูลในเครื่องของอุปกรณ์

จำลองข้อผิดพลาดเกี่ยวกับเครือข่าย

หากต้องการจำลองการติดตั้งโมดูลจาก Play Store ไลบรารีการนำส่งฟีเจอร์ Play จะใช้ตัวเลือกอื่นแทน SplitInstallManager ซึ่งเรียกว่า FakeSplitInstallManager เพื่อขอโมดูล เมื่อคุณใช้ bundletool กับ Flag --local-testing เพื่อสร้างชุด APK และนำไปติดตั้งในอุปกรณ์ทดสอบ bundletool จะมีข้อมูลเมตาที่สั่งให้คลังการนำส่งฟีเจอร์ของ Play เปลี่ยนการเรียก API ของแอปโดยอัตโนมัติเพื่อเรียกใช้ FakeSplitInstallManager แทน SplitInstallManager

FakeSplitInstallManager มี Flag แบบบูลีนที่คุณเปิดใช้เพื่อจำลองข้อผิดพลาดของเครือข่ายได้เมื่อแอปขอติดตั้งโมดูลครั้งถัดไป หากต้องการเข้าถึง FakeSplitInstallManager ในการทดสอบ ให้รับอินสแตนซ์ของ FakeSplitInstallManager โดยใช้ FakeSplitInstallManagerFactory ดังที่แสดงด้านล่าง

Kotlin

// Creates an instance of FakeSplitInstallManager with the app's context.
val fakeSplitInstallManager = FakeSplitInstallManagerFactory.create(context)
// Tells Play Feature Delivery Library to force the next module request to
// result in a network error.
fakeSplitInstallManager.setShouldNetworkError(true)

Java

// Creates an instance of FakeSplitInstallManager with the app's context.
FakeSplitInstallManager fakeSplitInstallManager =
    FakeSplitInstallManagerFactory.create(context);
// Tells Play Feature Delivery Library to force the next module request to
// result in a network error.
fakeSplitInstallManager.setShouldNetworkError(true);