โมดูลฟีเจอร์ช่วยให้คุณแยกฟีเจอร์และทรัพยากรบางอย่างออกจากโมดูลฐานของแอปและรวมไว้ใน App Bundle ได้ ตัวอย่างเช่น ผู้ใช้สามารถดาวน์โหลดและติดตั้งคอมโพเนนต์เหล่านั้นภายหลังได้ตามต้องการผ่าน Play Feature Delivery หลังจากที่ติดตั้ง APK พื้นฐานของแอปแล้ว
ตัวอย่างเช่น ลองพิจารณาแอปรับส่งข้อความที่มีฟังก์ชันการทำงานสำหรับการจับภาพและส่งข้อความรูปภาพ แต่มีผู้ใช้เพียงไม่กี่เปอร์เซ็นต์เท่านั้นที่ส่งข้อความรูปภาพ การใส่ข้อความรูปภาพเป็นโมดูล ฟีเจอร์ที่ดาวน์โหลดได้จึงเป็นเรื่องที่เหมาะสม วิธีนี้จะทำให้การดาวน์โหลดแอปครั้งแรกมีขนาดเล็กลงสำหรับผู้ใช้ทุกคน และมีเพียงผู้ใช้ที่ส่งข้อความรูปภาพเท่านั้นที่ต้องดาวน์โหลดคอมโพเนนต์เพิ่มเติมดังกล่าว
โปรดทราบว่าการจัดโมดูลประเภทนี้ต้องใช้ความพยายามมากขึ้นและอาจต้องมีการรีแฟกทอริงโค้ดที่มีอยู่ของแอป ดังนั้นโปรดพิจารณาอย่างรอบคอบว่าฟีเจอร์ใดของแอปจะได้รับประโยชน์มากที่สุดจากการเปิดให้ผู้ใช้ใช้งานได้ตามต้องการ หากต้องการทำความเข้าใจกรณีการใช้งานและหลักเกณฑ์ที่เหมาะสมที่สุดสำหรับฟีเจอร์แบบออนดีมานด์ โปรดอ่านแนวทางปฏิบัติแนะนำเกี่ยวกับ UX สำหรับการนำส่งแบบออนดีมานด์
หากต้องการค่อยๆ แยกฟีเจอร์ของแอปออกเป็นโมดูลเมื่อเวลาผ่านไป โดยไม่ต้องเปิดใช้ตัวเลือกการนำส่งขั้นสูง เช่น การนำส่งตามคำขอ ให้กำหนดค่าการนำส่งเมื่อติดตั้งแทน
หน้านี้จะช่วยคุณเพิ่มโมดูลฟีเจอร์ลงในโปรเจ็กต์แอปและกำหนดค่าสำหรับการนำส่งแบบออนดีมานด์ ก่อนเริ่มต้น โปรดตรวจสอบว่าคุณใช้ Android Studio 3.5 ขึ้นไปและปลั๊กอิน Android Gradle 3.5.0 ขึ้นไป
กำหนดค่าโมดูลใหม่สำหรับการนำส่งแบบออนดีมานด์
วิธีที่ง่ายที่สุดในการสร้างโมดูลฟีเจอร์ใหม่คือการใช้ Android Studio 3.5 ขึ้นไป เนื่องจากโมดูลฟีเจอร์มีความจำเป็นต้องใช้โมดูลแอปพื้นฐานอยู่แล้ว คุณจึงเพิ่มโมดูลฟีเจอร์ลงในโปรเจ็กต์แอปที่มีอยู่ได้เท่านั้น
หากต้องการเพิ่มโมดูลฟีเจอร์ลงในโปรเจ็กต์แอปโดยใช้ Android Studio ให้ทําดังนี้
- เปิดโปรเจ็กต์แอปใน IDE หากยังไม่ได้ดำเนินการ
- เลือกไฟล์ > ใหม่ > โมดูลใหม่จากแถบเมนู
- ในกล่องโต้ตอบสร้างข้อบังคับใหม่ ให้เลือกข้อบังคับฟีเจอร์แบบไดนามิก แล้วคลิกถัดไป
- ในส่วนกำหนดค่าโมดูลใหม่ ให้ทำดังนี้
- เลือกโมดูลแอปพลิเคชันพื้นฐานสําหรับโปรเจ็กต์แอปจากเมนูแบบเลื่อนลง
- ระบุชื่อโมดูล IDE จะใช้ชื่อนี้เพื่อระบุข้อบังคับเป็นโปรเจ็กต์ย่อย Gradle ในไฟล์การตั้งค่า Gradle เมื่อคุณสร้าง App Bundle ทาง Gradle จะใช้องค์ประกอบสุดท้ายของชื่อโปรเจ็กต์ย่อยเพื่อแทรกแอตทริบิวต์
<manifest split>
ในไฟล์ Manifest ของโมดูลฟีเจอร์ - ระบุชื่อแพ็กเกจของโมดูล โดยค่าเริ่มต้น Android Studio จะแนะนำชื่อแพ็กเกจที่รวมชื่อแพ็กเกจรูทของโมดูลพื้นฐานเข้ากับชื่อโมดูลที่คุณระบุไว้ในขั้นตอนก่อนหน้า
- เลือกระดับ API ขั้นต่ำที่คุณต้องการให้โมดูลรองรับ ค่านี้ควรตรงกับค่าของโมดูลพื้นฐาน
- คลิกถัดไป
ในส่วนตัวเลือกการดาวน์โหลดโมดูล ให้ทำดังนี้
ระบุชื่อข้อบังคับโดยใช้อักขระได้สูงสุด 50 ตัว แพลตฟอร์มจะใช้ชื่อนี้เพื่อระบุข้อบังคับต่อผู้ใช้ เช่น เมื่อยืนยันว่าผู้ใช้ต้องการดาวน์โหลดข้อบังคับหรือไม่ ด้วยเหตุนี้ โมดูลพื้นฐานของแอปจึงต้องมีชื่อโมดูลเป็นทรัพยากรสตริง ซึ่งคุณแปลได้ เมื่อสร้างโมดูลโดยใช้ Android Studio IDE จะเพิ่มทรัพยากรสตริงลงในโมดูลพื้นฐานให้คุณและแทรกรายการต่อไปนี้ในไฟล์ Manifest ของโมดูลฟีเจอร์
<dist:module ... dist:title="@string/feature_title"> </dist:module>
ในเมนูแบบเลื่อนลงในส่วนการรวมเวลาติดตั้ง ให้เลือกไม่รวมข้อบังคับในเวลาติดตั้ง Android Studio จะแทรกข้อมูลต่อไปนี้ในไฟล์ Manifest ของโมดูลเพื่อให้สอดคล้องกับตัวเลือกของคุณ
<dist:module ... > <dist:delivery> <dist:on-demand/> </dist:delivery> </dist:module>
เลือกช่องข้างการผสานหากต้องการให้โมดูลนี้พร้อมใช้งานในอุปกรณ์ที่ใช้ Android 4.4 (API ระดับ 20) หรือต่ำกว่า และรวมอยู่ใน APK หลายรายการ ซึ่งหมายความว่าคุณสามารถเปิดใช้ลักษณะการทำงานแบบออนดีมานด์สําหรับข้อบังคับนี้ และปิดใช้การผสานเพื่อยกเว้นข้อบังคับนี้จากอุปกรณ์ที่ไม่รองรับการดาวน์โหลดและการติดตั้ง APK แบบแยก Android Studio จะแทรกข้อมูลต่อไปนี้ในไฟล์ Manifest ของโมดูลเพื่อให้สอดคล้องกับตัวเลือกของคุณ
<dist:module ...> <dist:fusing dist:include="true | false" /> </dist:module>
คลิกเสร็จสิ้น
หลังจาก Android Studio สร้างโมดูลเสร็จแล้ว ให้ตรวจสอบเนื้อหาของโมดูลด้วยตนเองจากแผงโปรเจ็กต์ (เลือกดู > หน้าต่างเครื่องมือ > โปรเจ็กต์จากแถบเมนู) โค้ด ทรัพยากร และองค์กรเริ่มต้นควรคล้ายกับของโมดูลแอปมาตรฐาน
ถัดไป คุณจะต้องติดตั้งใช้งานฟังก์ชันการทำงานแบบติดตั้งเมื่อต้องการโดยใช้ไลบรารี Play Feature Delivery
รวมไลบรารีการนำส่งฟีเจอร์ Play ไว้ในโปรเจ็กต์
ก่อนเริ่มต้น คุณต้องเพิ่มคลังการส่งฟีเจอร์ของ Play ลงในโปรเจ็กต์ก่อน
ขอข้อบังคับแบบออนดีมานด์
เมื่อแอปต้องใช้โมดูลฟีเจอร์ แอปจะขอโมดูลได้ขณะที่แสดงอยู่เบื้องหน้าผ่านคลาส SplitInstallManager
เมื่อส่งคำขอ แอปของคุณต้องระบุชื่อของโมดูลตามที่องค์ประกอบ split
ในไฟล์ Manifest ของโมดูลเป้าหมายกำหนด เมื่อคุณสร้างโมดูลฟีเจอร์โดยใช้ Android Studio ระบบการสร้างจะใช้ชื่อโมดูลที่คุณระบุเพื่อแทรกพร็อพเพอร์ตี้นี้ลงในไฟล์ Manifest ของโมดูล ณ เวลาคอมไพล์
ดูข้อมูลเพิ่มเติมได้ที่หัวข้อไฟล์ Manifest ของโมดูลฟีเจอร์
ตัวอย่างเช่น ลองพิจารณาแอปที่มีโมดูลแบบออนดีมานด์เพื่อจับภาพและส่งข้อความรูปภาพโดยใช้กล้องของอุปกรณ์ และโมดูลแบบออนดีมานด์นี้ระบุ split="pictureMessages"
ในไฟล์ Manifest ตัวอย่างต่อไปนี้ใช้ SplitInstallManager
เพื่อขอข้อบังคับ pictureMessages
(พร้อมกับข้อบังคับเพิ่มเติมสําหรับตัวกรองโปรโมชันบางรายการ)
Kotlin
// Creates an instance of SplitInstallManager. val splitInstallManager = SplitInstallManagerFactory.create(context) // Creates a request to install a module. val request = SplitInstallRequest .newBuilder() // You can download multiple on demand modules per // request by invoking the following method for each // module you want to install. .addModule("pictureMessages") .addModule("promotionalFilters") .build() splitInstallManager // Submits the request to install the module through the // asynchronous startInstall() task. Your app needs to be // in the foreground to submit the request. .startInstall(request) // You should also be able to gracefully handle // request state changes and errors. To learn more, go to // the section about how to Monitor the request state. .addOnSuccessListener { sessionId -> ... } .addOnFailureListener { exception -> ... }
Java
// Creates an instance of SplitInstallManager. SplitInstallManager splitInstallManager = SplitInstallManagerFactory.create(context); // Creates a request to install a module. SplitInstallRequest request = SplitInstallRequest .newBuilder() // You can download multiple on demand modules per // request by invoking the following method for each // module you want to install. .addModule("pictureMessages") .addModule("promotionalFilters") .build(); splitInstallManager // Submits the request to install the module through the // asynchronous startInstall() task. Your app needs to be // in the foreground to submit the request. .startInstall(request) // You should also be able to gracefully handle // request state changes and errors. To learn more, go to // the section about how to Monitor the request state. .addOnSuccessListener(sessionId -> { ... }) .addOnFailureListener(exception -> { ... });
เมื่อแอปขอโมดูลแบบออนดีมานด์ ไลบรารีการนำส่งฟีเจอร์ Play จะใช้กลยุทธ์ "ยิงแล้วลืม" กล่าวคือ เครื่องมือจะส่งคําขอดาวน์โหลดข้อบังคับไปยังแพลตฟอร์ม แต่ไม่ตรวจสอบว่าการติดตั้งสําเร็จหรือไม่ หากต้องการดำเนินการต่อในเส้นทางของผู้ใช้หลังจากการติดตั้งหรือจัดการข้อผิดพลาดอย่างราบรื่น โปรดตรวจสอบสถานะคำขอ
หมายเหตุ: คุณขอโมดูลฟีเจอร์ที่ติดตั้งในอุปกรณ์อยู่แล้วได้ API จะถือว่าคําขอเสร็จสมบูรณ์ทันทีหากตรวจพบว่ามีการติดตั้งโมดูลแล้ว นอกจากนี้ หลังจากติดตั้งโมดูลแล้ว Google Play จะอัปเดตโมดูลนั้นโดยอัตโนมัติ กล่าวคือ เมื่อคุณอัปโหลด App Bundle เวอร์ชันใหม่ แพลตฟอร์มจะอัปเดต APK ที่ติดตั้งทั้งหมดซึ่งเป็นของแอปคุณ ดูข้อมูลเพิ่มเติมได้ที่จัดการการอัปเดตแอป
หากต้องการเข้าถึงโค้ดและทรัพยากรของโมดูล แอปของคุณต้องเปิดใช้ SplitCompat โปรดทราบว่าคุณไม่จำเป็นต้องใช้ SplitCompat สำหรับ Instant App ของ Android
เลื่อนการติดตั้งโมดูลแบบออนดีมานด์
หากไม่ต้องการให้แอปดาวน์โหลดและติดตั้งข้อบังคับเฉพาะเมื่อมีความต้องการทันที คุณสามารถเลื่อนการติดตั้งไว้เมื่อแอปทำงานอยู่เบื้องหลัง เช่น หากต้องการโหลดเนื้อหาส่งเสริมการขายบางส่วนไว้ล่วงหน้าเพื่อเปิดตัวแอปในภายหลัง
คุณสามารถระบุโมดูลที่จะดาวน์โหลดในภายหลังได้โดยใช้วิธี deferredInstall()
ดังที่แสดงด้านล่าง และต่างจาก SplitInstallManager.startInstall()
ตรงที่แอปของคุณไม่จำเป็นต้องอยู่เบื้องหน้าเพื่อเริ่มส่งคำขอการติดตั้งแบบเลื่อนเวลา
Kotlin
// Requests an on demand module to be downloaded when the app enters // the background. You can specify more than one module at a time. splitInstallManager.deferredInstall(listOf("promotionalFilters"))
Java
// Requests an on demand module to be downloaded when the app enters // the background. You can specify more than one module at a time. splitInstallManager.deferredInstall(Arrays.asList("promotionalFilters"));
คำขอสำหรับการติดตั้งที่เลื่อนเวลาไว้จะดำเนินการอย่างดีที่สุดและคุณจะติดตามความคืบหน้าไม่ได้ ดังนั้นก่อนพยายามเข้าถึงโมดูลที่คุณระบุไว้สําหรับการติดตั้งที่เลื่อนออกไป คุณควรตรวจสอบว่าได้ติดตั้งโมดูลแล้ว หากต้องการใช้ข้อบังคับทันที ให้ใช้ SplitInstallManager.startInstall()
เพื่อขอข้อบังคับแทน ตามที่แสดงในส่วนก่อนหน้า
ตรวจสอบสถานะคำขอ
หากต้องการอัปเดตแถบความคืบหน้า เรียกใช้ Intent หลังจากการติดตั้ง หรือจัดการข้อผิดพลาดของคำขออย่างราบรื่น คุณต้องคอยฟังการอัปเดตสถานะจากงาน SplitInstallManager.startInstall()
แบบแอซิงโครนัส
ก่อนที่จะเริ่มรับข้อมูลอัปเดตสำหรับคำขอติดตั้ง ให้ลงทะเบียนโปรแกรมรับฟังและรับรหัสเซสชันสำหรับคำขอดังที่แสดงด้านล่าง
Kotlin
// Initializes a variable to later track the session ID for a given request. var mySessionId = 0 // Creates a listener for request status updates. val listener = SplitInstallStateUpdatedListener { state -> if (state.sessionId() == mySessionId) { // Read the status of the request to handle the state update. } } // Registers the listener. splitInstallManager.registerListener(listener) ... splitInstallManager .startInstall(request) // When the platform accepts your request to download // an on demand module, it binds it to the following session ID. // You use this ID to track further status updates for the request. .addOnSuccessListener { sessionId -> mySessionId = sessionId } // You should also add the following listener to handle any errors // processing the request. .addOnFailureListener { exception -> // Handle request errors. } // When your app no longer requires further updates, unregister the listener. splitInstallManager.unregisterListener(listener)
Java
// Initializes a variable to later track the session ID for a given request. int mySessionId = 0; // Creates a listener for request status updates. SplitInstallStateUpdatedListener listener = state -> { if (state.sessionId() == mySessionId) { // Read the status of the request to handle the state update. } }; // Registers the listener. splitInstallManager.registerListener(listener); ... splitInstallManager .startInstall(request) // When the platform accepts your request to download // an on demand module, it binds it to the following session ID. // You use this ID to track further status updates for the request. .addOnSuccessListener(sessionId -> { mySessionId = sessionId; }) // You should also add the following listener to handle any errors // processing the request. .addOnFailureListener(exception -> { // Handle request errors. }); // When your app no longer requires further updates, unregister the listener. splitInstallManager.unregisterListener(listener);
จัดการข้อผิดพลาดของคําขอ
โปรดทราบว่าการติดตั้งโมดูลฟีเจอร์แบบออนดีมานด์อาจไม่สำเร็จในบางครั้ง เช่นเดียวกับการติดตั้งแอปที่ไม่สำเร็จเสมอไป การติดตั้งไม่สำเร็จอาจเกิดจากปัญหาต่างๆ เช่น พื้นที่เก็บข้อมูลในอุปกรณ์เหลือน้อย ไม่ได้เชื่อมต่อเครือข่าย หรือผู้ใช้ไม่ได้ลงชื่อเข้าใช้ Google Play Store ดูคำแนะนำเกี่ยวกับวิธีจัดการสถานการณ์เหล่านี้อย่างราบรื่นจากมุมมองของผู้ใช้ได้ในหลักเกณฑ์ UX สำหรับการแสดงเนื้อหาแบบออนดีมานด์
ในแง่โค้ด คุณควรจัดการกับการดาวน์โหลดหรือติดตั้งโมดูลที่ไม่สําเร็จโดยใช้ addOnFailureListener()
ดังที่แสดงด้านล่าง
Kotlin
splitInstallManager .startInstall(request) .addOnFailureListener { exception -> when ((exception as SplitInstallException).errorCode) { SplitInstallErrorCode.NETWORK_ERROR -> { // Display a message that requests the user to establish a // network connection. } SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED -> checkForActiveDownloads() ... } } fun checkForActiveDownloads() { splitInstallManager // Returns a SplitInstallSessionState object for each active session as a List. .sessionStates .addOnCompleteListener { task -> if (task.isSuccessful) { // Check for active sessions. for (state in task.result) { if (state.status() == SplitInstallSessionStatus.DOWNLOADING) { // Cancel the request, or request a deferred installation. } } } } }
Java
splitInstallManager .startInstall(request) .addOnFailureListener(exception -> { switch (((SplitInstallException) exception).getErrorCode()) { case SplitInstallErrorCode.NETWORK_ERROR: // Display a message that requests the user to establish a // network connection. break; case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED: checkForActiveDownloads(); ... }); void checkForActiveDownloads() { splitInstallManager // Returns a SplitInstallSessionState object for each active session as a List. .getSessionStates() .addOnCompleteListener( task -> { if (task.isSuccessful()) { // Check for active sessions. for (SplitInstallSessionState state : task.getResult()) { if (state.status() == SplitInstallSessionStatus.DOWNLOADING) { // Cancel the request, or request a deferred installation. } } } }); }
ตารางด้านล่างอธิบายสถานะข้อผิดพลาดที่แอปอาจต้องจัดการ
รหัสข้อผิดพลาด | คำอธิบาย | การดำเนินการที่แนะนำ |
---|---|---|
ACTIVE_SESSIONS_LIMIT_EXCEEDED | คำขอถูกปฏิเสธเนื่องจากมีคำขออย่างน้อย 1 รายการที่กําลังดาวน์โหลดอยู่ | ตรวจสอบว่ามีคำขอใดที่ยังคงดาวน์โหลดอยู่หรือไม่ ดังที่แสดงในตัวอย่างด้านบน |
MODULE_UNAVAILABLE | Google Play ไม่พบโมดูลที่ขอตามเวอร์ชันที่ติดตั้งในปัจจุบันของแอป อุปกรณ์ และบัญชี Google Play ของผู้ใช้ | หากผู้ใช้ไม่มีสิทธิ์เข้าถึงข้อบังคับ โปรดแจ้งให้ผู้ใช้ทราบ |
INVALID_REQUEST | Google Play ได้รับคำขอแล้ว แต่คำขอไม่ถูกต้อง | ยืนยันว่าข้อมูลในคำขอนั้นถูกต้องและครบถ้วน |
SESSION_NOT_FOUND | ไม่พบเซสชันสำหรับรหัสเซสชันที่ระบุ | หากคุณพยายามตรวจสอบสถานะคำขอตามรหัสเซสชัน ให้ตรวจสอบว่ารหัสเซสชันถูกต้อง |
API_NOT_AVAILABLE | อุปกรณ์ปัจจุบันไม่รองรับคลังการส่งฟีเจอร์ของ Play กล่าวคือ อุปกรณ์ไม่สามารถดาวน์โหลดและติดตั้งฟีเจอร์ตามต้องการ | สําหรับอุปกรณ์ที่ใช้ Android 4.4 (API ระดับ 20) หรือต่ำกว่า คุณควรรวมข้อบังคับของฟีเจอร์ไว้ในเวลาติดตั้งโดยใช้dist:fusing พร็อพเพอร์ตี้ไฟล์ Manifest ดูข้อมูลเพิ่มเติมได้ที่ไฟล์ Manifest ของโมดูลฟีเจอร์
|
NETWORK_ERROR | คำขอล้มเหลวเนื่องจากข้อผิดพลาดเกี่ยวกับเครือข่าย | แจ้งให้ผู้ใช้เชื่อมต่อเครือข่ายหรือเปลี่ยนไปใช้เครือข่ายอื่น |
ACCESS_DENIED | แอปลงทะเบียนคําขอไม่ได้เนื่องจากมีสิทธิ์ไม่เพียงพอ | ซึ่งมักเกิดขึ้นเมื่อแอปทำงานอยู่เบื้องหลัง ลองส่งคำขอเมื่อแอปกลับมาอยู่เบื้องหน้า |
INCOMPATIBLE_WITH_EXISTING_SESSION | คำขอมีโมดูลอย่างน้อย 1 รายการที่ขอไปแล้ว แต่ยังติดตั้งไม่สำเร็จ | สร้างคำขอใหม่ที่ไม่มีโมดูลที่แอปของคุณขอไปแล้ว หรือรอให้โมดูลทั้งหมดที่ขอในขณะนี้ติดตั้งเสร็จก่อนส่งคำขออีกครั้ง
โปรดทราบว่าการขอโมดูลที่ติดตั้งไปแล้วจะไม่ทำให้เกิดข้อผิดพลาด |
SERVICE_DIED | บริการที่รับผิดชอบในการจัดการคําขอหยุดทํางาน | ลองส่งคำขออีกครั้ง
|
INSUFFICIENT_STORAGE | อุปกรณ์มีพื้นที่ว่างไม่เพียงพอที่จะติดตั้งข้อบังคับของฟีเจอร์ | แจ้งให้ผู้ใช้ทราบว่ามีพื้นที่เก็บข้อมูลไม่เพียงพอที่จะติดตั้งฟีเจอร์นี้ |
SPLITCOMPAT_VERIFICATION_ERROR, SPLITCOMPAT_EMULATION_ERROR, SPLITCOMPAT_COPY_ERROR | SplitCompat โหลดข้อบังคับของฟีเจอร์ไม่ได้ | ข้อผิดพลาดเหล่านี้ควรได้รับการแก้ไขโดยอัตโนมัติหลังจากแอปรีสตาร์ทครั้งถัดไป |
PLAY_STORE_NOT_FOUND | ไม่ได้ติดตั้งแอป Play Store บนอุปกรณ์ | แจ้งให้ผู้ใช้ทราบว่าต้องมีแอป Play Store เพื่อดาวน์โหลดฟีเจอร์นี้ |
APP_NOT_OWNED | Google Play ยังไม่ได้ติดตั้งแอปและดาวน์โหลดฟีเจอร์ไม่ได้ ข้อผิดพลาดนี้จะเกิดขึ้นกับการติดตั้งที่เลื่อนออกไปเท่านั้น | หากต้องการให้ผู้ใช้ซื้อแอปใน Google Play ให้ใช้ startInstall() ซึ่งสามารถรับการยืนยันของผู้ใช้ที่จำเป็น |
INTERNAL_ERROR | เกิดข้อผิดพลาดภายในใน Play Store | ลองส่งคำขออีกครั้ง |
หากผู้ใช้ขอดาวน์โหลดโมดูลแบบออนดีมานด์และเกิดข้อผิดพลาดขึ้น ให้พิจารณาแสดงกล่องโต้ตอบที่มี 2 ตัวเลือกให้ผู้ใช้ ได้แก่ ลองอีกครั้ง (ซึ่งจะพยายามส่งคำขออีกครั้ง) และยกเลิก (ซึ่งจะยกเลิกคำขอ) คุณควรระบุลิงก์ความช่วยเหลือที่นําผู้ใช้ไปยังศูนย์ช่วยเหลือของ Google Play ด้วยเพื่อรับการสนับสนุนเพิ่มเติม
จัดการการอัปเดตสถานะ
หลังจากลงทะเบียนโปรแกรมรับฟังและบันทึกรหัสเซสชันของคําขอแล้ว ให้ใช้ StateUpdatedListener.onStateUpdate()
เพื่อจัดการการเปลี่ยนแปลงสถานะ ดังที่แสดงด้านล่าง
Kotlin
override fun onStateUpdate(state : SplitInstallSessionState) { if (state.status() == SplitInstallSessionStatus.FAILED && state.errorCode() == SplitInstallErrorCode.SERVICE_DIED) { // Retry the request. return } if (state.sessionId() == mySessionId) { when (state.status()) { SplitInstallSessionStatus.DOWNLOADING -> { val totalBytes = state.totalBytesToDownload() val progress = state.bytesDownloaded() // Update progress bar. } SplitInstallSessionStatus.INSTALLED -> { // After a module is installed, you can start accessing its content or // fire an intent to start an activity in the installed module. // For other use cases, see access code and resources from installed modules. // If the request is an on demand module for an Android Instant App // running on Android 8.0 (API level 26) or higher, you need to // update the app context using the SplitInstallHelper API. } } } }
Java
@Override public void onStateUpdate(SplitInstallSessionState state) { if (state.status() == SplitInstallSessionStatus.FAILED && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) { // Retry the request. return; } if (state.sessionId() == mySessionId) { switch (state.status()) { case SplitInstallSessionStatus.DOWNLOADING: int totalBytes = state.totalBytesToDownload(); int progress = state.bytesDownloaded(); // Update progress bar. break; case SplitInstallSessionStatus.INSTALLED: // After a module is installed, you can start accessing its content or // fire an intent to start an activity in the installed module. // For other use cases, see access code and resources from installed modules. // If the request is an on demand module for an Android Instant App // running on Android 8.0 (API level 26) or higher, you need to // update the app context using the SplitInstallHelper API. } } }
สถานะที่เป็นไปได้สำหรับคำขอติดตั้งจะอธิบายไว้ในตารางด้านล่าง
สถานะคำขอ | คำอธิบาย | การดำเนินการที่แนะนำ |
---|---|---|
รอดำเนินการ | คำขอได้รับการยอมรับแล้ว และระบบจะเริ่มดาวน์โหลดในไม่ช้า | เริ่มต้นคอมโพเนนต์ UI เช่น แถบความคืบหน้า เพื่อแสดงความคิดเห็นเกี่ยวกับการดาวน์โหลดแก่ผู้ใช้ |
REQUIRES_USER_CONFIRMATION | การดาวน์โหลดต้องได้รับการยืนยันจากผู้ใช้ สถานะนี้มักเกิดขึ้นหากไม่ได้ติดตั้งแอปผ่าน Google Play | แจ้งให้ผู้ใช้ยืนยันการดาวน์โหลดฟีเจอร์ผ่าน Google Play ดูข้อมูลเพิ่มเติมได้ที่ส่วนเกี่ยวกับวิธีขอการยืนยันจากผู้ใช้ |
การดาวน์โหลด | อยู่ระหว่างการดาวน์โหลด | หากคุณระบุแถบความคืบหน้าสำหรับการดาวน์โหลด ให้ใช้เมธอด SplitInstallSessionState.bytesDownloaded() และ SplitInstallSessionState.totalBytesToDownload() เพื่ออัปเดต UI (ดูตัวอย่างโค้ดด้านบนตารางนี้) |
ดาวน์โหลดแล้ว | อุปกรณ์ดาวน์โหลดโมดูลแล้วแต่ยังไม่ได้เริ่มการติดตั้ง | แอปควรเปิดใช้ SplitCompat เพื่อเข้าถึงโมดูลที่ดาวน์โหลดมาและหลีกเลี่ยงการแสดงสถานะนี้ ซึ่งจําเป็นต่อการเข้าถึงโค้ดและทรัพยากรของข้อบังคับฟีเจอร์ |
กำลังติดตั้ง | อุปกรณ์กำลังติดตั้งโมดูล | อัปเดตแถบความคืบหน้า โดยปกติแล้วสถานะนี้จะอยู่ไม่นาน |
ติดตั้งแล้ว | ติดตั้งโมดูลในอุปกรณ์แล้ว | เข้าถึงรหัสและทรัพยากรในข้อบังคับเพื่อดำเนินการต่อในเส้นทางของผู้ใช้
หากโมดูลสำหรับแอป Instant ของ Android ที่ทำงานบน Android 8.0 (API ระดับ 26) ขึ้นไป คุณต้องใช้ |
FAILED | คำขอไม่สำเร็จก่อนที่จะติดตั้งโมดูลในอุปกรณ์ | แจ้งให้ผู้ใช้ลองส่งคำขออีกครั้งหรือยกเลิก |
การยกเลิก | อุปกรณ์กำลังยกเลิกคำขอ | ดูข้อมูลเพิ่มเติมได้ที่ส่วนเกี่ยวกับวิธียกเลิกคําขอติดตั้ง |
ยกเลิก | ยกเลิกคำขอแล้ว |
ขอการยืนยันจากผู้ใช้
ในบางกรณี Google Play อาจกำหนดให้ผู้ใช้ต้องยืนยันก่อนจึงจะดำเนินการตามคำขอดาวน์โหลดได้ เช่น Google Play ยังไม่ได้ติดตั้งแอปของคุณ หรือคุณพยายามดาวน์โหลดไฟล์ขนาดใหญ่ผ่านอินเทอร์เน็ตมือถือ ในกรณีเช่นนี้ สถานะสำหรับคำขอจะแสดงเป็น REQUIRES_USER_CONFIRMATION
และแอปของคุณจะต้องได้รับการยืนยันจากผู้ใช้ก่อน อุปกรณ์จึงจะดาวน์โหลดและติดตั้งโมดูลในคำขอได้ หากต้องการรับการยืนยัน แอปของคุณควรแจ้งให้ผู้ใช้ดำเนินการดังนี้
Kotlin
override fun onSessionStateUpdate(state: SplitInstallSessionState) { if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) { // Displays a confirmation for the user to confirm the request. splitInstallManager.startConfirmationDialogForResult( state, // an activity result launcher registered via registerForActivityResult activityResultLauncher) } ... }
Java
@Override void onSessionStateUpdate(SplitInstallSessionState state) { if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) { // Displays a confirmation for the user to confirm the request. splitInstallManager.startConfirmationDialogForResult( state, // an activity result launcher registered via registerForActivityResult activityResultLauncher); } ... }
คุณสามารถลงทะเบียนตัวเปิดใช้งานผลการค้นหากิจกรรมได้โดยใช้สัญญาในตัว
ActivityResultContracts.StartIntentSenderForResult
โปรดดู Activity Result API
สถานะคำขอจะอัปเดตตามคําตอบของผู้ใช้ ดังนี้
- หากผู้ใช้ยอมรับการยืนยัน สถานะคำขอจะเปลี่ยนเป็น
PENDING
และระบบจะดำเนินการดาวน์โหลดต่อ - หากผู้ใช้ปฏิเสธการยืนยัน สถานะคำขอจะเปลี่ยนเป็น
CANCELED
- หากผู้ใช้ไม่เลือกตัวเลือกก่อนที่กล่องโต้ตอบจะปิดลง สถานะคำขอจะยังคงเป็น
REQUIRES_USER_CONFIRMATION
แอปของคุณแจ้งให้ผู้ใช้ดำเนินการตามคำขอให้เสร็จสมบูรณ์อีกครั้งได้
หากต้องการรับการเรียกกลับพร้อมคําตอบของผู้ใช้ คุณสามารถลบล้าง ActivityResultCallback ดังที่แสดงด้านล่าง
Kotlin
registerForActivityResult(StartIntentSenderForResult()) { result: ActivityResult -> { // Handle the user's decision. For example, if the user selects "Cancel", // you may want to disable certain functionality that depends on the module. } }
Java
registerForActivityResult( new ActivityResultContracts.StartIntentSenderForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { // Handle the user's decision. For example, if the user selects "Cancel", // you may want to disable certain functionality that depends on the module. } });
ยกเลิกคำขอติดตั้ง
หากแอปจำเป็นต้องยกเลิกคำขอก่อนที่จะมีการติดตั้ง แอปสามารถเรียกใช้เมธอด cancelInstall()
โดยใช้รหัสเซสชันของคำขอ ดังที่แสดงด้านล่าง
Kotlin
splitInstallManager // Cancels the request for the given session ID. .cancelInstall(mySessionId)
Java
splitInstallManager // Cancels the request for the given session ID. .cancelInstall(mySessionId);
เข้าถึงโมดูล
หากต้องการเข้าถึงโค้ดและทรัพยากรจากโมดูลที่ดาวน์โหลดแล้วหลังจากดาวน์โหลด แอปของคุณจะต้องเปิดใช้ไลบรารี SplitCompat ทั้งสําหรับแอปและแต่ละกิจกรรมในโมดูลฟีเจอร์ที่แอปดาวน์โหลด
อย่างไรก็ตาม โปรดทราบว่าแพลตฟอร์มจะพบข้อจำกัดต่อไปนี้ในการเข้าถึงเนื้อหาของโมดูลเป็นระยะเวลาหนึ่ง (2-3 วันในบางกรณี) หลังจากดาวน์โหลดโมดูล
- แพลตฟอร์มใช้รายการ Manifest ใหม่ที่โมดูลนำเสนอไม่ได้
- แพลตฟอร์มเข้าถึงทรัพยากรของโมดูลสําหรับคอมโพเนนต์ UI ของระบบไม่ได้ เช่น การแจ้งเตือน หากต้องการใช้ทรัพยากรดังกล่าวทันที ให้พิจารณารวมทรัพยากรเหล่านั้นไว้ในโมดูลพื้นฐานของแอป
เปิดใช้ SplitCompat
หากต้องการให้แอปเข้าถึงโค้ดและทรัพยากรจากโมดูลที่ดาวน์โหลดมา คุณจะต้องเปิดใช้ SplitCompat โดยใช้วิธีใดวิธีหนึ่งตามที่อธิบายไว้ในส่วนต่อไปนี้
หลังจากเปิดใช้ SplitCompat สําหรับแอปแล้ว คุณยังต้องเปิดใช้ SplitCompat สําหรับกิจกรรมแต่ละรายการในข้อบังคับของฟีเจอร์ที่คุณต้องการให้แอปเข้าถึง
ประกาศ SplitCompatApplication ในไฟล์ Manifest
วิธีที่ง่ายที่สุดในการเปิดใช้ SplitCompat คือการประกาศ SplitCompatApplication
เป็นคลาสย่อย Application
ในไฟล์ Manifest ของแอป ดังที่แสดงด้านล่าง
<application
...
android:name="com.google.android.play.core.splitcompat.SplitCompatApplication">
</application>
หลังจากติดตั้งแอปในอุปกรณ์แล้ว คุณจะเข้าถึงโค้ดและทรัพยากรจากข้อบังคับของฟีเจอร์ที่ดาวน์โหลดโดยอัตโนมัติ
เรียกใช้ SplitCompat ขณะรันไทม์
นอกจากนี้ คุณยังเปิดใช้ SplitCompat ในกิจกรรมหรือบริการที่เฉพาะเจาะจงขณะรันไทม์ได้ด้วย
คุณต้องเปิดใช้ SplitCompat ด้วยวิธีนี้เพื่อเปิดใช้งานกิจกรรมที่รวมอยู่ในข้อบังคับของฟีเจอร์ โดยให้ลบล้าง attachBaseContext
ดังที่แสดงด้านล่าง
หากคุณมีคลาส Application ที่กําหนดเอง ให้คลาสนั้นขยายจาก SplitCompatApplication
แทน เพื่อเปิดใช้ SplitCompat สําหรับแอป ดังที่แสดงด้านล่าง
Kotlin
class MyApplication : SplitCompatApplication() { ... }
Java
public class MyApplication extends SplitCompatApplication { ... }
SplitCompatApplication
เพียงลบล้าง ContextWrapper.attachBaseContext()
เพื่อรวม SplitCompat.install(Context applicationContext)
หากไม่ต้องการให้คลาส Application
ขยายจาก SplitCompatApplication
คุณสามารถลบล้างเมธอด attachBaseContext()
ด้วยตนเองได้โดยทำดังนี้
Kotlin
override fun attachBaseContext(base: Context) { super.attachBaseContext(base) // Emulates installation of future on demand modules using SplitCompat. SplitCompat.install(this) }
Java
@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); // Emulates installation of future on demand modules using SplitCompat. SplitCompat.install(this); }
หากโมดูลแบบออนดีมานด์ของคุณเข้ากันได้กับทั้งแอปด่วนและแอปที่ติดตั้ง คุณจะเรียกใช้ SplitCompat แบบมีเงื่อนไขได้ ดังนี้
Kotlin
override fun attachBaseContext(base: Context) { super.attachBaseContext(base) if (!InstantApps.isInstantApp(this)) { SplitCompat.install(this) } }
Java
@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); if (!InstantApps.isInstantApp(this)) { SplitCompat.install(this); } }
เปิดใช้ SplitCompat สําหรับกิจกรรมของโมดูล
หลังจากเปิดใช้ SplitCompat สําหรับแอปฐานแล้ว คุณต้องเปิดใช้ SplitCompat สําหรับกิจกรรมแต่ละรายการที่แอปดาวน์โหลดในโมดูลฟีเจอร์ โดยให้ใช้วิธีการ SplitCompat.installActivity()
ดังนี้
Kotlin
override fun attachBaseContext(base: Context) { super.attachBaseContext(base) // Emulates installation of on demand modules using SplitCompat. SplitCompat.installActivity(this) }
Java
@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); // Emulates installation of on demand modules using SplitCompat. SplitCompat.installActivity(this); }
เข้าถึงคอมโพเนนต์ที่กําหนดไว้ในโมดูลฟีเจอร์
เริ่มกิจกรรมที่กําหนดไว้ในโมดูลฟีเจอร์
คุณสามารถเปิดใช้งานกิจกรรมที่กําหนดไว้ในโมดูลฟีเจอร์ได้โดยใช้ startActivity()
หลังจากที่เปิดใช้ SplitCompat
Kotlin
startActivity(Intent() .setClassName("com.package", "com.package.module.MyActivity") .setFlags(...))
Java
startActivity(new Intent() .setClassName("com.package", "com.package.module.MyActivity") .setFlags(...));
พารามิเตอร์แรกใน setClassName
คือชื่อแพ็กเกจของแอป และพารามิเตอร์ที่ 2 คือชื่อคลาสแบบเต็มของกิจกรรม
เมื่อคุณมีกิจกรรมในโมดูลฟีเจอร์ที่ดาวน์โหลดแบบออนดีมานด์ คุณต้องเปิดใช้ SplitCompat ในกิจกรรม
เริ่มบริการที่กําหนดไว้ในโมดูลฟีเจอร์
คุณสามารถเปิดใช้บริการที่กําหนดไว้ในโมดูลฟีเจอร์ได้โดยใช้ startService()
หลังจากที่เปิดใช้ SplitCompat
Kotlin
startService(Intent() .setClassName("com.package", "com.package.module.MyService") .setFlags(...))
Java
startService(new Intent() .setClassName("com.package", "com.package.module.MyService") .setFlags(...));
ส่งออกคอมโพเนนต์ที่กําหนดไว้ในโมดูลฟีเจอร์
คุณไม่ควรรวมคอมโพเนนต์ Android ที่ส่งออกไว้ในโมดูลที่ไม่บังคับ
ระบบบิลด์จะผสานรายการไฟล์ Manifest ของโมดูลทั้งหมดไว้ในโมดูลฐาน หากโมดูลที่ไม่บังคับมีคอมโพเนนต์ที่ส่งออก ผู้ใช้จะเข้าถึงได้แม้ว่าจะยังไม่ได้ติดตั้งโมดูลก็ตาม และอาจทำให้ระบบขัดข้องเนื่องจากไม่มีโค้ดเมื่อเรียกใช้จากแอปอื่น
ปัญหานี้จะไม่เกิดขึ้นกับคอมโพเนนต์ภายใน เนื่องจากมีเพียงแอปเท่านั้นที่เข้าถึงได้ แอปจึงตรวจสอบได้ว่าได้ติดตั้งโมดูลแล้วก่อนที่จะเข้าถึงคอมโพเนนต์
หากต้องการคอมโพเนนต์ที่ส่งออกและต้องการให้เนื้อหาของคอมโพเนนต์อยู่ในโมดูลที่ไม่บังคับ ให้พิจารณาใช้รูปแบบพร็อกซี
ซึ่งทำได้โดยการเพิ่มคอมโพเนนต์พร็อกซีที่ส่งออกในฐาน เมื่อเข้าถึง คอมโพเนนต์พร็อกซีจะตรวจสอบว่ามีโมดูลที่มีเนื้อหาหรือไม่ หากมีโมดูลอยู่ คอมโพเนนต์พร็อกซีจะเริ่มต้นคอมโพเนนต์ภายในจากโมดูลผ่าน Intent
ได้ โดยส่งต่อ Intent จากแอปที่เรียกใช้ หากไม่มีโมดูล คอมโพเนนต์จะดาวน์โหลดโมดูลหรือแสดงข้อความแสดงข้อผิดพลาดที่เหมาะสมไปยังแอปที่เรียกใช้ได้
เข้าถึงโค้ดและทรัพยากรจากโมดูลที่ติดตั้ง
หากเปิดใช้ SplitCompat สำหรับบริบทแอปพลิเคชันพื้นฐานและกิจกรรมในโมดูลฟีเจอร์ คุณจะใช้โค้ดและทรัพยากรจากโมดูลฟีเจอร์ได้ราวกับว่าเป็นส่วนหนึ่งของ APK พื้นฐาน เมื่อติดตั้งโมดูลที่ไม่บังคับแล้ว
เข้าถึงรหัสจากโมดูลอื่น
เข้าถึงโค้ดฐานจากโมดูล
โมดูลอื่นๆ สามารถใช้โค้ดที่อยู่ในโมดูลฐานได้โดยตรง คุณไม่จำเป็นต้องดำเนินการใดๆ เป็นพิเศษ เพียงนำเข้าและใช้คลาสที่ต้องการ
เข้าถึงโค้ดโมดูลจากโมดูลอื่น
วัตถุหรือคลาสภายในโมดูลจะเข้าถึงแบบคงที่จากโมดูลอื่นโดยตรงไม่ได้ แต่เข้าถึงได้แบบอ้อมโดยใช้รีเฟล็กชัน
คุณควรระวังความถี่ที่เหตุการณ์นี้เกิดขึ้นเนื่องจากต้นทุนด้านประสิทธิภาพของการสะท้อน สำหรับ Use Case ที่ซับซ้อน ให้ใช้เฟรมเวิร์กการฉีด Dependency เช่น Dagger 2 เพื่อรับประกันการเรียกใช้การสะท้อนเพียงครั้งเดียวตลอดอายุการใช้งานของแอปพลิเคชัน
เราขอแนะนําให้กําหนดอินเทอร์เฟซในโมดูลฐานและการใช้งานในโมดูลฟีเจอร์เพื่อลดความซับซ้อนของการโต้ตอบกับออบเจ็กต์หลังจากการสร้างอินสแตนซ์ เช่น
Kotlin
// In the base module interface MyInterface { fun hello(): String } // In the feature module object MyInterfaceImpl : MyInterface { override fun hello() = "Hello" } // In the base module, where we want to access the feature module code val stringFromModule = (Class.forName("com.package.module.MyInterfaceImpl") .kotlin.objectInstance as MyInterface).hello();
Java
// In the base module public interface MyInterface { String hello(); } // In the feature module public class MyInterfaceImpl implements MyInterface { @Override public String hello() { return "Hello"; } } // In the base module, where we want to access the feature module code String stringFromModule = ((MyInterface) Class.forName("com.package.module.MyInterfaceImpl").getConstructor().newInstance()).hello();
เข้าถึงทรัพยากรและชิ้นงานจากโมดูลอื่น
เมื่อติดตั้งโมดูลแล้ว คุณจะเข้าถึงทรัพยากรและชิ้นงานภายในโมดูลได้ด้วยวิธีมาตรฐาน โดยมีข้อจำกัด 2 ข้อดังนี้
- หากคุณเข้าถึงทรัพยากรจากโมดูลอื่น โมดูลนั้นจะไม่มีสิทธิ์เข้าถึงตัวระบุทรัพยากร แต่ยังคงเข้าถึงทรัพยากรได้โดยใช้ชื่อ โปรดทราบว่าแพ็กเกจที่จะใช้อ้างอิงทรัพยากรคือแพ็กเกจของโมดูลที่กําหนดทรัพยากร
- หากต้องการเข้าถึงชิ้นงานหรือทรัพยากรที่อยู่ในโมดูลที่ติดตั้งใหม่จากโมดูลที่ติดตั้งไว้แล้วของแอป คุณต้องดำเนินการโดยใช้บริบทแอปพลิเคชัน บริบทของคอมโพเนนต์ที่พยายามเข้าถึงทรัพยากรจะยังไม่ได้รับการอัปเดต หรือจะสร้างคอมโพเนนต์นั้นขึ้นมาใหม่ (เช่น การเรียกใช้ Activity.recreate()) หรือติดตั้ง SplitCompat อีกครั้งในคอมโพเนนต์นั้นหลังจากการติดตั้งข้อบังคับของฟีเจอร์ก็ได้
โหลดโค้ดเนทีฟในแอปโดยใช้การแสดงโฆษณาแบบออนดีมานด์
เราขอแนะนำให้ใช้ ReLinker เพื่อโหลดไลบรารีเนทีฟทั้งหมดเมื่อใช้การนำส่งโมดูลฟีเจอร์แบบออนดีมานด์ ReLinker จะแก้ไขปัญหาในการโหลดไลบรารีแบบเนทีฟหลังจากการติดตั้งข้อบังคับของฟีเจอร์ ดูข้อมูลเพิ่มเติมเกี่ยวกับ ReLinker ได้ในเคล็ดลับ JNI ของ Android
โหลดโค้ดเนทีฟจากโมดูลที่ไม่บังคับ
เมื่อติดตั้งส่วนแยกแล้ว เราขอแนะนำให้โหลดโค้ดเนทีฟผ่าน ReLinker สำหรับแอปด่วน คุณควรใช้วิธีการพิเศษนี้
หากคุณใช้ System.loadLibrary()
เพื่อโหลดโค้ดเนทีฟและไลบรารีเนทีฟของคุณต้องอาศัยไลบรารีอื่นในโมดูล คุณต้องโหลดไลบรารีอื่นนั้นด้วยตนเองก่อน
หากคุณใช้ ReLinker การดำเนินการที่เทียบเท่าคือ
Relinker.recursively().loadLibrary()
หากคุณใช้ dlopen()
ในโค้ดเนทีฟเพื่อโหลดไลบรารีที่กําหนดไว้ในข้อบังคับของข้อบังคับ dlopen()
จะไม่ทํางานกับเส้นทางไลบรารีแบบสัมพัทธ์
วิธีแก้ปัญหาที่ดีที่สุดคือการดึงข้อมูลเส้นทางแบบสัมบูรณ์ของไลบรารีจากโค้ด Java ผ่าน ClassLoader.findLibrary()
แล้วนำไปใช้ในคําเรียก dlopen()
ดำเนินการนี้ก่อนป้อนโค้ดเนทีฟหรือใช้การเรียก JNI จากโค้ดเนทีฟไปยัง Java
เข้าถึง Android Instant Apps ที่ติดตั้งไว้
หลังจากโมดูล Instant App ของ Android รายงานเป็น INSTALLED
คุณจะเข้าถึงโค้ดและทรัพยากรของโมดูลได้โดยใช้บริบทของแอปที่รีเฟรช บริบทที่แอปของคุณสร้างขึ้นก่อนการติดตั้งโมดูล (เช่น บริบทที่จัดเก็บไว้ในตัวแปรอยู่แล้ว) จะไม่มีเนื้อหาของโมดูลใหม่ แต่บริบทใหม่จะเพิ่มค่านี้ เช่น โดยใช้ createPackageContext
Kotlin
// Generate a new context as soon as a request for a new module // reports as INSTALLED. override fun onStateUpdate(state: SplitInstallSessionState ) { if (state.sessionId() == mySessionId) { when (state.status()) { ... SplitInstallSessionStatus.INSTALLED -> { val newContext = context.createPackageContext(context.packageName, 0) // If you use AssetManager to access your app’s raw asset files, you’ll need // to generate a new AssetManager instance from the updated context. val am = newContext.assets } } } }
Java
// Generate a new context as soon as a request for a new module // reports as INSTALLED. @Override public void onStateUpdate(SplitInstallSessionState state) { if (state.sessionId() == mySessionId) { switch (state.status()) { ... case SplitInstallSessionStatus.INSTALLED: Context newContext = context.createPackageContext(context.getPackageName(), 0); // If you use AssetManager to access your app’s raw asset files, you’ll need // to generate a new AssetManager instance from the updated context. AssetManager am = newContext.getAssets(); } } }
Android Instant Apps ใน Android 8.0 ขึ้นไป
เมื่อขอโมดูลแบบออนดีมานด์สําหรับแอป Instant ของ Android ใน Android 8.0 (API ระดับ 26) ขึ้นไป หลังจากคําขอติดตั้งรายงานเป็น INSTALLED
คุณต้องอัปเดตแอปด้วยบริบทของโมดูลใหม่ผ่านการเรียกใช้ SplitInstallHelper.updateAppInfo(Context context)
มิเช่นนั้นแอปจะยังไม่รู้จักโค้ดและทรัพยากรของโมดูล หลังจากอัปเดตข้อมูลเมตาของแอปแล้ว คุณควรโหลดเนื้อหาของโมดูลในระหว่างเหตุการณ์เธรดหลักครั้งถัดไปโดยการเรียกใช้ Handler
ใหม่ ดังที่แสดงด้านล่าง
Kotlin
override fun onStateUpdate(state: SplitInstallSessionState ) { if (state.sessionId() == mySessionId) { when (state.status()) { ... SplitInstallSessionStatus.INSTALLED -> { // You need to perform the following only for Android Instant Apps // running on Android 8.0 (API level 26) and higher. if (BuildCompat.isAtLeastO()) { // Updates the app’s context with the code and resources of the // installed module. SplitInstallHelper.updateAppInfo(context) Handler().post { // Loads contents from the module using AssetManager val am = context.assets ... } } } } } }
Java
@Override public void onStateUpdate(SplitInstallSessionState state) { if (state.sessionId() == mySessionId) { switch (state.status()) { ... case SplitInstallSessionStatus.INSTALLED: // You need to perform the following only for Android Instant Apps // running on Android 8.0 (API level 26) and higher. if (BuildCompat.isAtLeastO()) { // Updates the app’s context with the code and resources of the // installed module. SplitInstallHelper.updateAppInfo(context); new Handler().post(new Runnable() { @Override public void run() { // Loads contents from the module using AssetManager AssetManager am = context.getAssets(); ... } }); } } } }
โหลดไลบรารี C/C++
หากต้องการโหลดไลบรารี C/C++ จากโมดูลที่อุปกรณ์ดาวน์โหลดไว้ใน Instant App อยู่แล้ว ให้ใช้ SplitInstallHelper.loadLibrary(Context context, String libName)
ดังที่แสดงด้านล่าง
Kotlin
override fun onStateUpdate(state: SplitInstallSessionState) { if (state.sessionId() == mySessionId) { when (state.status()) { SplitInstallSessionStatus.INSTALLED -> { // Updates the app’s context as soon as a module is installed. val newContext = context.createPackageContext(context.packageName, 0) // To load C/C++ libraries from an installed module, use the following API // instead of System.load(). SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”) ... } } } }
Java
public void onStateUpdate(SplitInstallSessionState state) { if (state.sessionId() == mySessionId) { switch (state.status()) { case SplitInstallSessionStatus.INSTALLED: // Updates the app’s context as soon as a module is installed. Context newContext = context.createPackageContext(context.getPackageName(), 0); // To load C/C++ libraries from an installed module, use the following API // instead of System.load(). SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”); ... } } }
ข้อจำกัดที่ทราบ
- คุณไม่สามารถใช้ Android WebView ในกิจกรรมที่เข้าถึงทรัพยากรหรือชิ้นงานจากโมดูลที่ไม่บังคับ ปัญหานี้เกิดจากความไม่เข้ากันได้ระหว่าง WebView กับ SplitCompat ใน Android API ระดับ 28 หรือต่ำกว่า
- คุณไม่สามารถแคชออบเจ็กต์
ApplicationInfo
ของ Android, เนื้อหาของออบเจ็กต์เหล่านั้น หรือออบเจ็กต์ที่มีออบเจ็กต์ดังกล่าวภายในแอปของคุณได้ คุณควรดึงข้อมูลออบเจ็กต์เหล่านี้จากบริบทของแอปตามที่จำเป็นเสมอ การแคชออบเจ็กต์ดังกล่าวอาจทำให้แอปขัดข้องเมื่อติดตั้งโมดูลฟีเจอร์
จัดการโมดูลที่ติดตั้ง
หากต้องการตรวจสอบว่าขณะนี้มีการติดตั้งโมดูลฟีเจอร์ใดในอุปกรณ์บ้าง ให้เรียกใช้ SplitInstallManager.getInstalledModules()
ซึ่งจะแสดงผล Set<String>
ชื่อของโมดูลที่ติดตั้งไว้ดังที่แสดงด้านล่าง
Kotlin
val installedModules: Set<String> = splitInstallManager.installedModules
Java
Set<String> installedModules = splitInstallManager.getInstalledModules();
ถอนการติดตั้งโมดูล
คุณสามารถขอให้อุปกรณ์ถอนการติดตั้งโมดูลได้โดยเรียกใช้ SplitInstallManager.deferredUninstall(List<String> moduleNames)
ดังที่แสดงด้านล่าง
Kotlin
// Specifies two feature modules for deferred uninstall. splitInstallManager.deferredUninstall(listOf("pictureMessages", "promotionalFilters"))
Java
// Specifies two feature modules for deferred uninstall. splitInstallManager.deferredUninstall(Arrays.asList("pictureMessages", "promotionalFilters"));
การถอนการติดตั้งโมดูลจะไม่เกิดขึ้นทันที กล่าวคือ อุปกรณ์จะถอนการติดตั้งแอปเหล่านั้นในเบื้องหลังตามที่จำเป็นเพื่อประหยัดพื้นที่เก็บข้อมูล
คุณสามารถยืนยันว่าอุปกรณ์ได้ลบโมดูลแล้วโดยการเรียกใช้ SplitInstallManager.getInstalledModules()
และตรวจสอบผลลัพธ์ ตามที่อธิบายไว้ในส่วนก่อนหน้า
ดาวน์โหลดแหล่งข้อมูลภาษาเพิ่มเติม
เมื่อใช้ App Bundle อุปกรณ์จะดาวน์โหลดเฉพาะโค้ดและทรัพยากรที่จําเป็นต่อการใช้งานแอป ดังนั้นสําหรับทรัพยากรภาษา อุปกรณ์ของผู้ใช้จะดาวน์โหลดเฉพาะทรัพยากรภาษาของแอปที่ตรงกับภาษาอย่างน้อย 1 ภาษาที่เลือกไว้ในการตั้งค่าของอุปกรณ์
หากต้องการให้แอปเข้าถึงทรัพยากรด้านภาษาเพิ่มเติม เช่น เพื่อติดตั้งใช้งานเครื่องมือเลือกภาษาในแอป คุณสามารถใช้คลังการนำส่งฟีเจอร์ของ Play เพื่อดาวน์โหลดทรัพยากรดังกล่าวตามต้องการ กระบวนการนี้คล้ายกับการดาวน์โหลดโมดูลฟีเจอร์ดังที่แสดงด้านล่าง
Kotlin
// Captures the user’s preferred language and persists it // through the app’s SharedPreferences. sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply() ... // Creates a request to download and install additional language resources. val request = SplitInstallRequest.newBuilder() // Uses the addLanguage() method to include French language resources in the request. // Note that country codes are ignored. That is, if your app // includes resources for “fr-FR” and “fr-CA”, resources for both // country codes are downloaded when requesting resources for "fr". .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION))) .build() // Submits the request to install the additional language resources. splitInstallManager.startInstall(request)
Java
// Captures the user’s preferred language and persists it // through the app’s SharedPreferences. sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply(); ... // Creates a request to download and install additional language resources. SplitInstallRequest request = SplitInstallRequest.newBuilder() // Uses the addLanguage() method to include French language resources in the request. // Note that country codes are ignored. That is, if your app // includes resources for “fr-FR” and “fr-CA”, resources for both // country codes are downloaded when requesting resources for "fr". .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION))) .build(); // Submits the request to install the additional language resources. splitInstallManager.startInstall(request);
ระบบจะจัดการคำขอนี้เสมือนว่าเป็นคำขอสำหรับข้อบังคับของฟีเจอร์ กล่าวคือ คุณสามารถตรวจสอบสถานะคำขอได้ตามปกติ
หากแอปไม่จำเป็นต้องใช้ทรัพยากรภาษาเพิ่มเติมในทันที คุณสามารถเลื่อนการติดตั้งไว้เมื่อแอปทำงานอยู่เบื้องหลังได้ ดังที่แสดงด้านล่าง
Kotlin
splitInstallManager.deferredLanguageInstall( Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
Java
splitInstallManager.deferredLanguageInstall( Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
เข้าถึงแหล่งข้อมูลภาษาที่ดาวน์โหลด
หากต้องการเข้าถึงทรัพยากรภาษาที่ดาวน์โหลดไว้ แอปของคุณต้องเรียกใช้วิธี SplitCompat.installActivity()
ภายในวิธี attachBaseContext()
ของกิจกรรมแต่ละรายการที่ต้องเข้าถึงทรัพยากรเหล่านั้น ดังที่แสดงด้านล่าง
Kotlin
override fun attachBaseContext(base: Context) { super.attachBaseContext(base) SplitCompat.installActivity(this) }
Java
@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); SplitCompat.installActivity(this); }
สําหรับกิจกรรมแต่ละรายการที่คุณต้องการใช้ทรัพยากรภาษาที่แอปดาวน์โหลดมา ให้อัปเดตบริบทพื้นฐานและตั้งค่าภาษาใหม่ผ่านConfiguration
ดังนี้
Kotlin
override fun attachBaseContext(base: Context) { val configuration = Configuration() configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION))) val context = base.createConfigurationContext(configuration) super.attachBaseContext(context) SplitCompat.install(this) }
Java
@Override protected void attachBaseContext(Context base) { Configuration configuration = new Configuration(); configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION))); Context context = base.createConfigurationContext(configuration); super.attachBaseContext(context); SplitCompat.install(this); }
คุณต้องสร้างกิจกรรมอีกครั้งหลังจากติดตั้งภาษาใหม่และพร้อมใช้งานแล้วเพื่อให้การเปลี่ยนแปลงเหล่านี้มีผล คุณสามารถใช้เมธอด
Activity#recreate()
Kotlin
when (state.status()) { SplitInstallSessionStatus.INSTALLED -> { // Recreates the activity to load resources for the new language // preference. activity.recreate() } ... }
Java
switch (state.status()) { case SplitInstallSessionStatus.INSTALLED: // Recreates the activity to load resources for the new language // preference. activity.recreate(); ... }
ถอนการติดตั้งทรัพยากรภาษาเพิ่มเติม
คุณสามารถถอนการติดตั้งทรัพยากรเพิ่มเติมได้ทุกเมื่อ เช่นเดียวกับข้อบังคับของฟีเจอร์ ก่อนขอถอนการติดตั้ง คุณอาจต้องตรวจสอบก่อนว่ามีภาษาใดบ้างที่ติดตั้งอยู่ในขณะนี้ ดังนี้
Kotlin
val installedLanguages: Set<String> = splitInstallManager.installedLanguages
Java
Set<String> installedLanguages = splitInstallManager.getInstalledLanguages();
จากนั้นคุณเลือกภาษาที่จะถอนการติดตั้งได้โดยใช้วิธี deferredLanguageUninstall()
ดังที่แสดงด้านล่าง
Kotlin
splitInstallManager.deferredLanguageUninstall( Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
Java
splitInstallManager.deferredLanguageUninstall( Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
ทดสอบการติดตั้งโมดูลในเครื่อง
คลังการส่งฟีเจอร์ของ Play ช่วยให้คุณทดสอบความสามารถของแอปในการดำเนินการต่อไปนี้ได้โดยไม่ต้องเชื่อมต่อกับ Play Store
- ขอและตรวจสอบการติดตั้งโมดูล
- จัดการข้อผิดพลาดในการติดตั้ง
- ใช้
SplitCompat
เพื่อเข้าถึงข้อบังคับ
หน้านี้อธิบายวิธีทำให้ APK ที่แยกของแอปใช้งานได้ในอุปกรณ์ทดสอบเพื่อให้การนำส่งฟีเจอร์ Play ใช้ APK เหล่านั้นโดยอัตโนมัติเพื่อจำลองการขอ ดาวน์โหลด และติดตั้งโมดูลจาก Play Store
แม้ว่าคุณจะไม่ต้องทำการเปลี่ยนแปลงใดๆ กับตรรกะของแอป แต่ก็ต้องปฏิบัติตามข้อกำหนดต่อไปนี้
- ดาวน์โหลดและติดตั้ง
bundletool
เวอร์ชันล่าสุด คุณต้องมีbundletool
เพื่อสร้างชุด APK ที่ติดตั้งได้ชุดใหม่จากกลุ่มของแอป
สร้างชุด APK
หากยังไม่ได้สร้าง ให้สร้าง APK แบบแยกของแอป ดังนี้
- สร้าง App Bundle สําหรับแอปโดยใช้วิธีใดวิธีหนึ่งต่อไปนี้
- ใช้ Android Studio และปลั๊กอิน Android สำหรับ Gradle เพื่อสร้างและเซ็นชื่อ App Bundle ของ Android
- สร้าง App Bundle จากบรรทัดคำสั่ง
ใช้
bundletool
เพื่อสร้างชุดAPK สําหรับการกําหนดค่าอุปกรณ์ทั้งหมดด้วยคําสั่งต่อไปนี้bundletool build-apks --local-testing --bundle my_app.aab --output my_app.apks
Flag --local-testing
มี meta-data ในไฟล์ Manifest ของ APK ซึ่งจะแจ้งให้ Play Feature Delivery Library ทราบว่าให้ใช้ APK ที่แยกไว้ในพื้นที่เพื่อทดสอบการติดตั้งโมดูลฟีเจอร์โดยไม่ต้องเชื่อมต่อกับ Play Store
ติดตั้งใช้งานแอปในอุปกรณ์
หลังจากสร้างชุด APK โดยใช้ Flag --local-testing
แล้ว ให้ใช้ bundletool
เพื่อติดตั้งแอปเวอร์ชันพื้นฐานและโอน APK เพิ่มเติมไปยังพื้นที่เก็บข้อมูลในเครื่องของอุปกรณ์ คุณทําทั้ง 2 การดำเนินการได้ด้วยคำสั่งต่อไปนี้
bundletool install-apks --apks my_app.apks
เมื่อคุณเริ่มแอปและดำเนินการตามขั้นตอนของผู้ใช้เพื่อดาวน์โหลดและติดตั้งโมดูลฟีเจอร์จนเสร็จสมบูรณ์แล้ว ไลบรารีการนำส่งฟีเจอร์ของ Play จะใช้ APK ที่ bundletool
โอนไปยังพื้นที่เก็บข้อมูลในเครื่องของอุปกรณ์
จำลองข้อผิดพลาดเกี่ยวกับเครือข่าย
หากต้องการจำลองการติดตั้งโมดูลจาก Play Store ไลบรารีการนำส่งฟีเจอร์ Play จะใช้ตัวเลือกอื่นแทน SplitInstallManager
ซึ่งเรียกว่า FakeSplitInstallManager
เพื่อขอโมดูล เมื่อคุณใช้ bundletool
กับ Flag --local-testing
เพื่อสร้างชุด APK และนำไปติดตั้งในอุปกรณ์ทดสอบ bundletool
จะรวมข้อมูลเมตาที่สั่งให้คลังการนำส่งฟีเจอร์ของ Play เปลี่ยนการเรียก API ของแอปโดยอัตโนมัติเพื่อเรียกใช้ FakeSplitInstallManager
แทน SplitInstallManager
FakeSplitInstallManager
มี Flag แบบบูลีนที่คุณเปิดใช้เพื่อจำลองข้อผิดพลาดของเครือข่ายได้เมื่อแอปขอติดตั้งโมดูลครั้งถัดไป หากต้องการเข้าถึง FakeSplitInstallManager
ในการทดสอบ คุณสามารถรับอินสแตนซ์ของ FakeSplitInstallManager
โดยใช้ FakeSplitInstallManagerFactory
ดังที่แสดงด้านล่าง
Kotlin
// Creates an instance of FakeSplitInstallManager with the app's context. val fakeSplitInstallManager = FakeSplitInstallManagerFactory.create(context) // Tells Play Feature Delivery Library to force the next module request to // result in a network error. fakeSplitInstallManager.setShouldNetworkError(true)
Java
// Creates an instance of FakeSplitInstallManager with the app's context. FakeSplitInstallManager fakeSplitInstallManager = FakeSplitInstallManagerFactory.create(context); // Tells Play Feature Delivery Library to force the next module request to // result in a network error. fakeSplitInstallManager.setShouldNetworkError(true);