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

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

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

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

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

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

กำหนดค่าโมดูลใหม่สำหรับการนำส่งตามคำขอ

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

หากต้องการเพิ่มโมดูลฟีเจอร์ลงในโปรเจ็กต์แอปโดยใช้ 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 สำหรับ Android Instant App

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

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

คุณระบุโมดูลที่จะดาวน์โหลดในภายหลังได้โดยใช้เมธอด 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

พื้นที่เก็บข้อมูลไม่เพียงพอ อุปกรณ์มีพื้นที่เก็บข้อมูลไม่เพียงพอที่จะติดตั้งโมดูลฟีเจอร์ แจ้งให้ผู้ใช้ทราบว่ามีพื้นที่เก็บข้อมูลไม่เพียงพอที่จะติดตั้งฟีเจอร์นี้
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 ด้วย

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

หลังจากลงทะเบียน Listener และบันทึกรหัสเซสชันสําหรับคําขอแล้ว ให้ใช้ 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 Library สำหรับทั้งแอปและแต่ละกิจกรรมในโมดูลฟีเจอร์ที่แอปของคุณ ดาวน์โหลด

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

  • แพลตฟอร์มใช้รายการไฟล์ 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 พื้นฐานเมื่อติดตั้งโมดูลที่ไม่บังคับแล้ว

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

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

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

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

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

คุณควรระมัดระวังความถี่ในการดำเนินการนี้ เนื่องจากค่าใช้จ่ายด้านประสิทธิภาพ ของการสะท้อน สำหรับกรณีการใช้งานที่ซับซ้อน ให้ใช้เฟรมเวิร์กการแทรกการอ้างอิง เช่น 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() ในโค้ดเนทีฟเพื่อโหลดไลบรารีที่กำหนดไว้ใน โมดูลที่ไม่บังคับ จะใช้กับเส้นทางไลบรารีแบบสัมพัทธ์ไม่ได้ วิธีแก้ไขที่ดีที่สุดคือการดึงเส้นทางแบบสัมบูรณ์ของไลบรารีจากโค้ด Java ผ่าน ClassLoader.findLibrary() แล้วใช้ในคำสั่งเรียก dlopen() ดำเนินการนี้ก่อนป้อนโค้ดเนทีฟหรือใช้การเรียก JNI จากโค้ดเนทีฟไปยัง Java

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

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

Kotlin

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

Java

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

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

เมื่อขอโมดูลตามต้องการสำหรับ Instant App ของ 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

  • ขอและตรวจสอบการติดตั้งโมดูล
  • จัดการข้อผิดพลาดในการติดตั้ง
  • ใช้ SplitCompat เพื่อเข้าถึงโมดูล

หน้านี้อธิบายวิธีติดตั้งใช้งาน 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
    

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

ทำให้แอปใช้งานได้ในอุปกรณ์

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

bundletool install-apks --apks my_app.apks

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

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

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

FakeSplitInstallManager มีค่าสถานะบูลีนที่คุณเปิดใช้เพื่อ จำลองข้อผิดพลาดของเครือข่ายในครั้งถัดไปที่แอปขอติดตั้งโมดูลได้ หากต้องการเข้าถึง 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);