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

โมดูลฟีเจอร์ช่วยให้คุณแยกฟีเจอร์และทรัพยากรบางอย่างออกจากโมดูลพื้นฐานของแอปและรวมไว้ใน App Bundle ได้ ตัวอย่างเช่น การนำส่งฟีเจอร์ Play ช่วยให้ผู้ใช้ดาวน์โหลดและติดตั้งคอมโพเนนต์เหล่านั้นตามต้องการได้ในภายหลังหลังจากที่ติดตั้ง 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. เลือกช่องข้าง Fusing หากต้องการให้โมดูลนี้พร้อมใช้งานในอุปกรณ์ที่ใช้ Android 4.4 (ระดับ API 20) และต่ำกว่า และรวมอยู่ใน Multi-APK ซึ่งหมายความว่าคุณสามารถเปิดใช้ลักษณะการทำงานแบบออนดีมานด์สำหรับโมดูลนี้และปิดใช้การรวมเพื่อละเว้นโมดูลนี้จากอุปกรณ์ที่ไม่รองรับการดาวน์โหลดและติดตั้ง APK แยกได้ Android Studio จะแทรกข้อมูลต่อไปนี้ในไฟล์ Manifest ของโมดูลเพื่อแสดงตัวเลือกของคุณ

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

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

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

รวมไลบรารีการนำส่งฟีเจอร์ 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 โปรดทราบว่า Android Instant Apps ไม่จำเป็นต้องใช้ SplitCompat

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

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

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

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) หรือต่ำกว่า คุณควรใส่โมดูลฟีเจอร์ไว้ในเวลาติดตั้งโดยใช้พร็อพเพอร์ตี้ไฟล์ Manifest dist:fusing ดูข้อมูลเพิ่มเติมได้ที่ไฟล์ 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 ด้วย

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

หลังจากลงทะเบียน 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 เพื่ออัปเดตคอมโพเนนต์ของแอปด้วยโมดูลใหม่

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

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

ในบางกรณี 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 ในตัว ดู API ผลลัพธ์ของกิจกรรม

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

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

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

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

  • แพลตฟอร์มไม่สามารถใช้รายการไฟล์ 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);
}

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

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

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

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

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

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

คุณควรระมัดระวังความถี่ที่เกิดเหตุการณ์นี้เนื่องจากต้นทุนด้านประสิทธิภาพของการสะท้อน สำหรับกรณีการใช้งานที่ซับซ้อน ให้ใช้เฟรมเวิร์กการแทรกทรัพยากร 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

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

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

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

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

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

หลังจากโมดูล Android Instant App รายงานเป็น 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 ขึ้นไป

เมื่อขอโมดูลแบบออนดีมานด์สำหรับ Android Instant App ใน 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 ใหม่ที่ติดตั้งได้จาก App Bundle ของแอป

สร้างชุด 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 จะใช้ APK ที่ bundletool โอนไปยังพื้นที่เก็บข้อมูลในเครื่องของอุปกรณ์

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

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

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

Kotlin

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

Java

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