ไฟล์สำหรับขยายของ APK

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

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

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

ภาพรวม

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

  • ไฟล์เสริมหลักคือไฟล์เสริมหลักสำหรับทรัพยากรเพิ่มเติมที่แอปของคุณต้องการ
  • ไฟล์เสริมแพตช์เป็นไฟล์ที่ไม่บังคับและมีไว้สำหรับการอัปเดตเล็กๆ น้อยๆ ของไฟล์เสริมหลัก

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

อย่างไรก็ตาม แม้ว่าการอัปเดตแอปจะต้องการเพียงไฟล์เสริมแพตช์ใหม่ คุณก็ยังต้องอัปโหลด APK ใหม่ที่มี versionCode ที่อัปเดตแล้วในไฟล์ Manifest (Play Console ไม่อนุญาตให้อัปโหลดไฟล์เสริมไปยัง APK ที่มีอยู่)

หมายเหตุ: ไฟล์เสริมแพตช์มีความหมายเหมือนกับไฟล์เสริมหลัก คุณสามารถใช้แต่ละไฟล์ได้ตามต้องการ

รูปแบบชื่อไฟล์

ไฟล์เสริมแต่ละไฟล์ที่คุณอัปโหลดสามารถเป็นรูปแบบใดก็ได้ที่คุณเลือก (ZIP, PDF, MP4 ฯลฯ) นอกจากนี้ คุณยังใช้เครื่องมือ JOBB เพื่อห่อหุ้มและเข้ารหัสชุด ไฟล์ทรัพยากรและแพตช์ที่ตามมาสำหรับชุดนั้นได้ด้วย ไม่ว่าจะเป็นไฟล์ประเภทใด Google Play จะถือว่าเป็น Blob แบบไบนารีที่ทึบแสงและเปลี่ยนชื่อไฟล์โดยใช้รูปแบบต่อไปนี้

[main|patch].<expansion-version>.<package-name>.obb

โดยมีองค์ประกอบ 3 อย่าง ได้แก่

main หรือ patch
ระบุว่าไฟล์เป็นไฟล์เสริมหลักหรือไฟล์เสริมแพตช์ APK แต่ละรายการจะมีได้ เฉพาะไฟล์หลัก 1 ไฟล์และไฟล์แพตช์ 1 ไฟล์เท่านั้น
<expansion-version>
นี่คือจำนวนเต็มที่ตรงกับรหัสเวอร์ชันของ APK ที่มีการเชื่อมโยงส่วนขยายเป็นครั้งแรก (ตรงกับค่า android:versionCode ของแอป)

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

<package-name>
ชื่อแพ็กเกจของแอปในรูปแบบ Java

ตัวอย่างเช่น สมมติว่า APK เวอร์ชันของคุณคือ 314159 และชื่อแพ็กเกจคือ com.example.app หากคุณอัปโหลดไฟล์เสริมหลัก ระบบจะเปลี่ยนชื่อไฟล์เป็น

main.314159.com.example.app.obb

ตำแหน่งของพื้นที่เก็บข้อมูล

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

เมธอด getObbDir() จะแสดงผลตำแหน่งที่เฉพาะเจาะจง สำหรับไฟล์ส่วนขยายในรูปแบบต่อไปนี้

<shared-storage>/Android/obb/<package-name>/
  • <shared-storage> คือเส้นทางไปยังพื้นที่เก็บข้อมูลที่ใช้ร่วมกัน ซึ่งพร้อมใช้งานจาก getExternalStorageDirectory()
  • <package-name> คือชื่อแพ็กเกจสไตล์ Java ของแอป ซึ่งดูได้จาก getPackageName()

สำหรับแต่ละแอป จะมีไฟล์เสริมไม่เกิน 2 ไฟล์ในไดเรกทอรีนี้ ไฟล์หนึ่งคือไฟล์เสริมหลัก และอีกไฟล์คือไฟล์เสริมแพตช์ (หากจำเป็น) ระบบจะเขียนทับเวอร์ชันก่อนหน้าเมื่อคุณอัปเดตแอปด้วยไฟล์สำหรับขยายใหม่ ตั้งแต่ Android 4.4 (API ระดับ 19) เป็นต้นมา แอปสามารถอ่านOBBไฟล์เสริมได้โดยไม่ต้องมีสิทธิ์เข้าถึงที่เก็บข้อมูลภายนอก อย่างไรก็ตาม การใช้งาน Android 6.0 (ระดับ API 23) ขึ้นไปบางอย่างยังคงต้องใช้สิทธิ์ ดังนั้นคุณจะต้องประกาศสิทธิ์ READ_EXTERNAL_STORAGE ในไฟล์ Manifest ของแอปและขอสิทธิ์ที่ รันไทม์ดังนี้

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

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

Kotlin

val obb = File(obb_filename)
var open_failed = false

try {
    BufferedReader(FileReader(obb)).also { br ->
        ReadObbFile(br)
    }
} catch (e: IOException) {
    open_failed = true
}

if (open_failed) {
    // request READ_EXTERNAL_STORAGE permission before reading OBB file
    ReadObbFileWithPermission()
}

Java

File obb = new File(obb_filename);
 boolean open_failed = false;

 try {
     BufferedReader br = new BufferedReader(new FileReader(obb));
     open_failed = false;
     ReadObbFile(br);
 } catch (IOException e) {
     open_failed = true;
 }

 if (open_failed) {
     // request READ_EXTERNAL_STORAGE permission before reading OBB file
     ReadObbFileWithPermission();
 }

หากต้องคลายเนื้อหาของไฟล์สำหรับขยาย อย่าลบ OBBไฟล์สำหรับขยายในภายหลัง และอย่าบันทึกข้อมูลที่คลายแล้ว ในไดเรกทอรีเดียวกัน คุณควรบันทึกไฟล์ที่คลายการแพ็กแล้วในไดเรกทอรีที่ระบุโดย getExternalFilesDir() อย่างไรก็ตาม หากเป็นไปได้ คุณควรใช้รูปแบบไฟล์ส่วนขยายที่ช่วยให้คุณอ่านจาก ไฟล์ได้โดยตรงแทนที่จะต้องแตกข้อมูล เช่น เราได้จัดเตรียมไลบรารี โปรเจ็กต์ที่ชื่อ APK Expansion Zip Library ซึ่งอ่านข้อมูลโดยตรง จากไฟล์ ZIP

ข้อควรระวัง: ผู้ใช้และแอปอื่นๆ สามารถอ่านไฟล์ที่บันทึกไว้ในพื้นที่เก็บข้อมูลที่ใช้ร่วมกันได้ ซึ่งแตกต่างจากไฟล์ APK

เคล็ดลับ: หากแพ็กเกจไฟล์สื่อเป็น ZIP คุณสามารถใช้การเรียกการเล่นสื่อ ในไฟล์ที่มีการควบคุมออฟเซ็ตและความยาว (เช่น MediaPlayer.setDataSource() และ SoundPool.load()) โดยไม่ต้อง คลายแพ็กเกจ ZIP หากต้องการให้การดำเนินการนี้ได้ผล คุณต้องไม่บีบอัดไฟล์สื่อเพิ่มเติมเมื่อสร้างแพ็กเกจ ZIP ตัวอย่างเช่น เมื่อใช้เครื่องมือ zip คุณควรใช้ตัวเลือก -n เพื่อระบุนามสกุลไฟล์ที่ไม่ควร บีบอัด:
zip -n .mp4;.ogg main_expansion media_files

กระบวนการดาวน์โหลด

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

กระบวนการดาวน์โหลดจากระดับสูงมีลักษณะดังนี้

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

    หาก Google Play ดาวน์โหลดไฟล์สำหรับขยายไม่ได้ ระบบจะดาวน์โหลดเฉพาะ APK

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

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

รายการตรวจสอบการพัฒนา

ต่อไปนี้คือสรุปงานที่คุณควรทำเพื่อใช้ไฟล์สำหรับขยายกับแอป

  1. ก่อนอื่นให้พิจารณาว่าขนาดการดาวน์โหลดที่บีบอัดของแอปจำเป็นต้องมีขนาดมากกว่า 100 MB หรือไม่ พื้นที่เป็นสิ่งสำคัญและคุณควรทำให้ขนาดการดาวน์โหลดทั้งหมดมีขนาดเล็กที่สุดเท่าที่จะทำได้ หากแอปใช้พื้นที่มากกว่า 100 MB เพื่อให้มีเนื้อหากราฟิกหลายเวอร์ชันสำหรับความหนาแน่นของหน้าจอหลายระดับ ให้ลองเผยแพร่ APK หลายรายการแทน โดยแต่ละ APK จะมีเฉพาะเนื้อหาที่จำเป็นสำหรับหน้าจอที่กำหนดเป้าหมายไว้เท่านั้น หากต้องการผลลัพธ์ที่ดีที่สุดเมื่อ เผยแพร่ไปยัง Google Play ให้อัปโหลด Android App Bundle ซึ่ง มีโค้ดและทรัพยากรที่คอมไพล์แล้วทั้งหมดของแอป แต่จะเลื่อนการสร้างและลงนาม APK ไปยัง Google Play
  2. พิจารณาทรัพยากรของแอปที่จะแยกออกจาก APK แล้วแพ็กเกจไว้ใน ไฟล์เพื่อใช้เป็นไฟล์เสริมหลัก

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

  3. พัฒนาแอปให้ใช้ทรัพยากรจากไฟล์ส่วนขยายในตำแหน่งพื้นที่เก็บข้อมูลที่ใช้ร่วมกันของอุปกรณ์

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

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

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

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

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

เมื่อพัฒนาแอปเสร็จแล้ว ให้ทำตามคำแนะนำเพื่อทดสอบ ไฟล์เสริม

กฎและข้อจำกัด

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

  1. ไฟล์เสริมแต่ละไฟล์ต้องมีขนาดไม่เกิน 2 GB
  2. หากต้องการดาวน์โหลดไฟล์ส่วนขยายจาก Google Play ผู้ใช้ต้องดาวน์โหลดแอปจาก Google Play Google Play จะไม่ ระบุ URL สำหรับไฟล์ส่วนขยายหากมีการติดตั้งแอปด้วยวิธีอื่น
  3. เมื่อดาวน์โหลดจากภายในแอป URL ที่ Google Play ระบุสำหรับแต่ละไฟล์จะไม่ซ้ำกันสำหรับการดาวน์โหลดแต่ละครั้ง และแต่ละ URL จะหมดอายุหลังจากที่มอบให้แอปของคุณไม่นาน
  4. หากอัปเดตแอปด้วย APK ใหม่หรืออัปโหลด APK หลายรายการสำหรับแอปเดียวกัน คุณจะเลือกไฟล์เสริมที่อัปโหลดไว้สำหรับ APK ก่อนหน้าได้ ชื่อของไฟล์ส่วนขยายจะไม่เปลี่ยนแปลง โดยจะยังคงเป็นเวอร์ชันที่ APK ได้รับ ซึ่งเป็นเวอร์ชันที่ไฟล์เชื่อมโยงด้วยในตอนแรก
  5. หากใช้ไฟล์เสริมร่วมกับ APK หลายรายการเพื่อ ระบุไฟล์เสริมที่แตกต่างกันสำหรับอุปกรณ์ต่างๆ คุณยังคงต้องอัปโหลด APK แยกต่างหาก สำหรับอุปกรณ์แต่ละเครื่องเพื่อระบุค่า versionCode ที่ไม่ซ้ำกันและประกาศตัวกรองที่แตกต่างกันสำหรับ แต่ละ APK
  6. คุณไม่สามารถออกการอัปเดตแอปได้โดยการเปลี่ยนไฟล์เสริมเพียงอย่างเดียว คุณต้องอัปโหลด APK ใหม่เพื่ออัปเดตแอป หากการเปลี่ยนแปลงเกี่ยวข้องกับเนื้อหาในไฟล์เสริมเท่านั้น คุณสามารถอัปเดต APK ได้โดยเปลี่ยนversionCode (และอาจเปลี่ยนversionName ด้วย)
  7. อย่าบันทึกข้อมูลอื่นๆ ลงในobb/ ไดเรกทอรี หากต้องคลายแพ็กข้อมูลบางอย่าง ให้บันทึกข้อมูลนั้นไปยังตำแหน่งที่ getExternalFilesDir() ระบุ
  8. อย่าลบหรือเปลี่ยนชื่อ.obbไฟล์เสริม (เว้นแต่คุณจะ ทำการอัปเดต) การทำเช่นนี้จะทำให้ Google Play (หรือแอปของคุณเอง) ดาวน์โหลดไฟล์เสริมซ้ำๆ
  9. เมื่ออัปเดตไฟล์สำหรับขยายด้วยตนเอง คุณต้องลบไฟล์สำหรับขยายก่อนหน้า

การดาวน์โหลดไฟล์สำหรับขยาย

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

ตรรกะพื้นฐานที่คุณต้องใช้ในการดาวน์โหลดไฟล์เสริมมีดังนี้

  1. เมื่อแอปเริ่มต้น ให้มองหาไฟล์เสริมในตำแหน่งที่จัดเก็บข้อมูลที่ใช้ร่วมกัน (ในไดเรกทอรี Android/obb/<package-name>/)
    1. หากมีไฟล์เสริมอยู่ แสดงว่าคุณพร้อมแล้วและแอปจะดำเนินการต่อได้
    2. หากไม่มีไฟล์สำหรับขยาย ให้ทำดังนี้
      1. ส่งคำขอโดยใช้การอนุญาตให้ใช้สิทธิแอปของ Google Play เพื่อรับชื่อ ขนาด และ URL ของไฟล์เสริมของแอป
      2. ใช้ URL ที่ Google Play ระบุเพื่อดาวน์โหลดไฟล์สำหรับขยายและบันทึก ไฟล์สำหรับขยาย คุณต้องบันทึกไฟล์ไปยังตำแหน่งพื้นที่เก็บข้อมูลที่ใช้ร่วมกัน (Android/obb/<package-name>/) และใช้ชื่อไฟล์ที่ตรงกันทุกประการตามที่ระบุ ในการตอบกลับของ Google Play

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

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

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

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

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

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

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

เกี่ยวกับคลังตัวดาวน์โหลด

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

หากต้องการใช้การดาวน์โหลดไฟล์สำหรับขยายโดยใช้ไลบรารี Downloader คุณเพียงแค่ต้องทำดังนี้

  • ขยายServiceคลาสย่อยพิเศษและBroadcastReceiverคลาสย่อยที่แต่ละคลาสต้องการโค้ดจากคุณเพียงไม่กี่บรรทัด
  • เพิ่มตรรกะบางอย่างลงในกิจกรรมหลักเพื่อตรวจสอบว่าได้ดาวน์โหลดไฟล์ส่วนขยายแล้วหรือไม่ หากยังไม่ได้ดาวน์โหลด ให้เรียกใช้กระบวนการดาวน์โหลดและแสดง UI ความคืบหน้า
  • ใช้ อินเทอร์เฟซ Callback ที่มีเมธอด 2-3 รายการในกิจกรรมหลักที่ รับข้อมูลอัปเดตเกี่ยวกับความคืบหน้าในการดาวน์โหลด

ส่วนต่อไปนี้จะอธิบายวิธีตั้งค่าแอปโดยใช้ไลบรารีโปรแกรมดาวน์โหลด

การเตรียมพร้อมเพื่อใช้ไลบรารี Downloader

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

ก่อนอื่น ให้เปิด เครื่องมือจัดการ Android SDK (เครื่องมือ > เครื่องมือจัดการ SDK) แล้วเลือกแท็บSDK Tools เพื่อเลือกและดาวน์โหลดในส่วนลักษณะที่ปรากฏและพฤติกรรม > การตั้งค่าระบบ > Android SDK

  • แพ็กเกจไลบรารีการอนุญาตให้ใช้สิทธิของ Google Play
  • แพ็กเกจไลบรารีการขยาย APK ของ Google Play

สร้างโมดูลไลบรารีใหม่สำหรับไลบรารีการยืนยันใบอนุญาตและไลบรารีโปรแกรมดาวน์โหลด สำหรับแต่ละคลัง

  1. เลือก File > New > New Module
  2. ในหน้าต่างสร้างโมดูลใหม่ ให้เลือกไลบรารี Android แล้วเลือกถัดไป
  3. ระบุชื่อแอป/ไลบรารี เช่น "ไลบรารีใบอนุญาตของ Google Play" และ "ไลบรารีโปรแกรมดาวน์โหลดของ Google Play" เลือกระดับ SDK ขั้นต่ำ แล้วเลือก เสร็จสิ้น
  4. เลือก File > Project Structure
  5. เลือกแท็บคุณสมบัติ แล้วป้อนไลบรารีจากไดเรกทอรี <sdk>/extras/google/ ในที่เก็บ ไลบรารี (play_licensing/ สำหรับไลบรารีการยืนยันใบอนุญาตหรือ play_apk_expansion/downloader_library/ สำหรับไลบรารีตัวดาวน์โหลด)
  6. เลือกตกลงเพื่อสร้างโมดูลใหม่

หมายเหตุ: ไลบรารีโปรแกรมดาวน์โหลดขึ้นอยู่กับไลบรารีการยืนยัน ใบอนุญาต อย่าลืมเพิ่มไลบรารีการยืนยันใบอนุญาต ลงในพร็อพเพอร์ตี้โปรเจ็กต์ของไลบรารีตัวดาวน์โหลด

หรืออัปเดตโปรเจ็กต์จากบรรทัดคำสั่งให้รวมไลบรารี

  1. เปลี่ยนไดเรกทอรีเป็นไดเรกทอรี <sdk>/tools/
  2. เรียกใช้ android update project ด้วยตัวเลือก --library เพื่อเพิ่มทั้ง LVL และไลบรารี Downloader ลงในโปรเจ็กต์ เช่น
    android update project --path ~/Android/MyApp \
    --library ~/android_sdk/extras/google/market_licensing \
    --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
    

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

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

การประกาศสิทธิ์ของผู้ใช้

หากต้องการดาวน์โหลดไฟล์เสริม ไลบรารี Downloader ต้องมีสิทธิ์หลายอย่างที่คุณต้องประกาศในไฟล์ Manifest ของแอป โดยมีรายละเอียดดังนี้

<manifest ...>
    <!-- Required to access Google Play Licensing -->
    <uses-permission android:name="com.android.vending.CHECK_LICENSE" />

    <!-- Required to download files from Google Play -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Required to keep CPU alive while downloading files
        (NOT to keep screen awake) -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <!-- Required to poll the state of the network connection
        and respond to changes -->
    <uses-permission
        android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- Required to check whether Wi-Fi is enabled -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

    <!-- Required to read and write the expansion files on shared storage -->
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

หมายเหตุ: โดยค่าเริ่มต้น ไลบรารีโปรแกรมดาวน์โหลดต้องใช้ API ระดับ 4 แต่ไลบรารี Zip ของไฟล์ขยาย APK ต้องใช้ API ระดับ 5

การติดตั้งใช้งานบริการดาวน์โหลด

หากต้องการดาวน์โหลดในเบื้องหลัง ไลบรารี Downloader จะมีคลาสย่อยของตัวเอง Service ที่เรียกว่า DownloaderService ซึ่งคุณควรขยาย นอกเหนือจากการดาวน์โหลดไฟล์เสริมให้คุณแล้ว DownloaderService ยังทำสิ่งต่อไปนี้ได้ด้วย

  • ลงทะเบียน BroadcastReceiver ที่รอฟังการเปลี่ยนแปลงการเชื่อมต่อเครือข่ายของอุปกรณ์ (CONNECTIVITY_ACTION การออกอากาศ) เพื่อหยุดการดาวน์โหลดชั่วคราวเมื่อจำเป็น (เช่น เนื่องจากการเชื่อมต่อขาดหาย) และ ดำเนินการดาวน์โหลดต่อเมื่อเป็นไปได้ (มีการเชื่อมต่อ)
  • ตั้งเวลาปลุก RTC_WAKEUP เพื่อลองดาวน์โหลดอีกครั้งในกรณีที่ระบบปิดบริการ
  • สร้าง Notification ที่กำหนดเองซึ่งแสดงความคืบหน้าในการดาวน์โหลด รวมถึงข้อผิดพลาดหรือการเปลี่ยนแปลงสถานะ
  • อนุญาตให้แอปหยุดชั่วคราวและดาวน์โหลดต่อด้วยตนเอง
  • ยืนยันว่าได้ติดตั้งและพร้อมใช้งานพื้นที่เก็บข้อมูลที่แชร์แล้ว ยืนยันว่าไม่มีไฟล์อยู่แล้ว และยืนยันว่ามีพื้นที่เพียงพอ ทั้งหมดนี้ก่อนที่จะดาวน์โหลดไฟล์ส่วนขยาย จากนั้นจะแจ้งให้ผู้ใช้ทราบ หากข้อมูลใดไม่เป็นความจริง

สิ่งที่คุณต้องทำคือสร้างคลาสในแอปที่ขยายคลาส DownloaderService และแทนที่ 3 วิธีเพื่อระบุรายละเอียดแอปที่เฉพาะเจาะจง

getPublicKey()
ต้องแสดงผลสตริงที่เป็นคีย์สาธารณะ RSA ที่เข้ารหัส Base64 สำหรับบัญชีผู้เผยแพร่แอป ของคุณ ซึ่งดูได้จากหน้าโปรไฟล์ใน Play Console (ดูการตั้งค่าสำหรับการอนุญาตให้ใช้สิทธิ)
getSALT()
ฟังก์ชันนี้ต้องแสดงผลอาร์เรย์ของไบต์แบบสุ่มที่การให้สิทธิ์ Policy ใช้เพื่อ สร้าง Obfuscator Salt ช่วยให้มั่นใจได้ว่าSharedPreferences ไฟล์ที่ผ่านการปกปิดซึ่งบันทึกข้อมูลการอนุญาตให้ใช้สิทธิของคุณจะมีความเฉพาะตัวและค้นหาไม่ได้
getAlarmReceiverClassName()
ต้องแสดงชื่อคลาสของ BroadcastReceiver ใน แอปของคุณที่ควรรับการปลุกซึ่งบ่งชี้ว่าควร รีสตาร์ทการดาวน์โหลด (ซึ่งอาจเกิดขึ้นหากบริการดาวน์โหลดหยุดทำงานโดยไม่คาดคิด)

ตัวอย่างเช่น นี่คือการติดตั้งใช้งาน DownloaderService ที่สมบูรณ์

Kotlin

// You must use the public key belonging to your publisher account
const val BASE64_PUBLIC_KEY = "YourLVLKey"
// You should also modify this salt
val SALT = byteArrayOf(
        1, 42, -12, -1, 54, 98, -100, -12, 43, 2,
        -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
)

class SampleDownloaderService : DownloaderService() {

    override fun getPublicKey(): String = BASE64_PUBLIC_KEY

    override fun getSALT(): ByteArray = SALT

    override fun getAlarmReceiverClassName(): String = SampleAlarmReceiver::class.java.name
}

Java

public class SampleDownloaderService extends DownloaderService {
    // You must use the public key belonging to your publisher account
    public static final String BASE64_PUBLIC_KEY = "YourLVLKey";
    // You should also modify this salt
    public static final byte[] SALT = new byte[] { 1, 42, -12, -1, 54, 98,
            -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
    };

    @Override
    public String getPublicKey() {
        return BASE64_PUBLIC_KEY;
    }

    @Override
    public byte[] getSALT() {
        return SALT;
    }

    @Override
    public String getAlarmReceiverClassName() {
        return SampleAlarmReceiver.class.getName();
    }
}

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

อย่าลืมประกาศบริการในไฟล์ Manifest โดยทำดังนี้

<app ...>
    <service android:name=".SampleDownloaderService" />
    ...
</app>

การติดตั้งใช้งานเครื่องรับสัญญาณเตือน

DownloaderService จะตั้งเวลาปลุก RTC_WAKEUP ซึ่งจะส่ง Intent ไปยัง BroadcastReceiver ในแอปของคุณ เพื่อตรวจสอบความคืบหน้าของการดาวน์โหลดไฟล์และรีสตาร์ทการดาวน์โหลดหากจำเป็น คุณต้องกำหนด BroadcastReceiver เพื่อเรียก API จากไลบรารีโปรแกรมดาวน์โหลดที่ตรวจสอบสถานะการดาวน์โหลดและรีสตาร์ทหากจำเป็น

คุณเพียงแค่ต้องลบล้างเมธอด onReceive() เพื่อเรียกใช้ DownloaderClientMarshaller.startDownloadServiceIfRequired()

เช่น

Kotlin

class SampleAlarmReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    context,
                    intent,
                    SampleDownloaderService::class.java
            )
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }
    }
}

Java

public class SampleAlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(context,
                intent, SampleDownloaderService.class);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
    }
}

โปรดทราบว่านี่คือคลาสที่คุณต้องส่งคืนชื่อ ในเมธอด getAlarmReceiverClassName() ของบริการ (ดูส่วนก่อนหน้า)

อย่าลืมประกาศตัวรับในไฟล์ Manifest

<app ...>
    <receiver android:name=".SampleAlarmReceiver" />
    ...
</app>

เริ่มดาวน์โหลด

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

การเริ่มดาวน์โหลดโดยใช้ไลบรารีตัวดาวน์โหลดต้องมีขั้นตอนต่อไปนี้

  1. ตรวจสอบว่าดาวน์โหลดไฟล์แล้วหรือไม่

    ไลบรารีโปรแกรมดาวน์โหลดมี API บางรายการในคลาส Helper เพื่อช่วยในกระบวนการนี้

    • getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
    • doesFileExist(Context c, String fileName, long fileSize)

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

    Kotlin

    fun expansionFilesDelivered(): Boolean {
        xAPKS.forEach { xf ->
            Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion).also { fileName ->
                if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false))
                    return false
            }
        }
        return true
    }

    Java

    boolean expansionFilesDelivered() {
        for (XAPKFile xf : xAPKS) {
            String fileName = Helpers.getExpansionAPKFileName(this, xf.isBase,
                xf.fileVersion);
            if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false))
                return false;
        }
        return true;
    }

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

    หากเมธอดนี้แสดงผลเป็นเท็จ แอปจะต้องเริ่มดาวน์โหลด

  2. เริ่มการดาวน์โหลดโดยเรียกใช้เมธอดแบบคงที่ DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass)

    เมธอดนี้ใช้พารามิเตอร์ต่อไปนี้

    • context: Context ของแอป
    • notificationClient: PendingIntent เพื่อเริ่มกิจกรรมหลัก ใช้ใน Notification ที่ DownloaderService สร้างขึ้นเพื่อแสดงความคืบหน้าในการดาวน์โหลด เมื่อผู้ใช้เลือกการแจ้งเตือน ระบบจะเรียกใช้ PendingIntent ที่คุณระบุไว้ที่นี่ และควรเปิดกิจกรรมที่แสดงความคืบหน้าในการดาวน์โหลด (โดยปกติจะเป็นกิจกรรมเดียวกันกับที่เริ่มการดาวน์โหลด)
    • serviceClass: ออบเจ็กต์ Class สำหรับการติดตั้งใช้งาน DownloaderService ซึ่งจำเป็นต่อการเริ่มบริการและเริ่มดาวน์โหลดหากจำเป็น

    เมธอดจะแสดงผลจำนวนเต็มที่ระบุว่าจำเป็นต้องดาวน์โหลดหรือไม่ ค่าที่เป็นไปได้มีดังนี้

    • NO_DOWNLOAD_REQUIRED: แสดงผลหากมีไฟล์อยู่แล้ว หรือกำลังดาวน์โหลดอยู่
    • LVL_CHECK_REQUIRED: แสดงผลหากต้องมีการยืนยันสิทธิ์การใช้งาน เพื่อรับ URL ของไฟล์เสริม
    • DOWNLOAD_REQUIRED: แสดงขึ้นหากระบบทราบ URL ของไฟล์เสริมแล้ว แต่ยังไม่ได้ดาวน์โหลด

    โดยพื้นฐานแล้วลักษณะการทำงานของ LVL_CHECK_REQUIRED และ DOWNLOAD_REQUIRED จะเหมือนกัน และโดยปกติแล้วคุณไม่จำเป็นต้องกังวลเกี่ยวกับค่าเหล่านี้ ในกิจกรรมหลักที่เรียก startDownloadServiceIfRequired() คุณสามารถตรวจสอบได้ง่ายๆ ว่าการตอบกลับเป็น NO_DOWNLOAD_REQUIRED หรือไม่ หากการตอบกลับนอกเหนือจาก NO_DOWNLOAD_REQUIRED, ไลบรารีโปรแกรมดาวน์โหลดจะเริ่มดาวน์โหลดและคุณควรปรับปรุง UI กิจกรรมเพื่อ แสดงความคืบหน้าในการดาวน์โหลด (ดูขั้นตอนถัดไป) หากการตอบกลับเป็น NO_DOWNLOAD_REQUIRED แสดงว่าไฟล์พร้อมใช้งานและแอปของคุณเริ่มทำงานได้

    เช่น

    Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // Check if expansion files are available before going any further
        if (!expansionFilesDelivered()) {
            val pendingIntent =
                    // Build an Intent to start this activity from the Notification
                    Intent(this, MainActivity::class.java).apply {
                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
                    }.let { notifierIntent ->
                        PendingIntent.getActivity(
                                this,
                                0,
                                notifierIntent,
                                PendingIntent.FLAG_UPDATE_CURRENT
                        )
                    }
    
    
            // Start the download service (if required)
            val startResult: Int = DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    this,
                    pendingIntent,
                    SampleDownloaderService::class.java
            )
            // If download has started, initialize this activity to show
            // download progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download
                // progress (next step)
                ...
                return
            } // If the download wasn't necessary, fall through to start the app
        }
        startApp() // Expansion files are available, start the app
    }

    Java

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // Check if expansion files are available before going any further
        if (!expansionFilesDelivered()) {
            // Build an Intent to start this activity from the Notification
            Intent notifierIntent = new Intent(this, MainActivity.getClass());
            notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                                    Intent.FLAG_ACTIVITY_CLEAR_TOP);
            ...
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
                    notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            // Start the download service (if required)
            int startResult =
                DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize this activity to show
            // download progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download
                // progress (next step)
                ...
                return;
            } // If the download wasn't necessary, fall through to start the app
        }
        startApp(); // Expansion files are available, start the app
    }
  3. เมื่อเมธอด startDownloadServiceIfRequired() แสดงผลอย่างอื่นที่ไม่ใช่ NO_DOWNLOAD_REQUIRED ให้สร้างอินสแตนซ์ของ IStub โดยเรียกใช้ DownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService) IStub จะเชื่อมโยงกิจกรรมของคุณกับบริการโปรแกรมดาวน์โหลด เพื่อให้กิจกรรมของคุณได้รับการเรียกกลับเกี่ยวกับความคืบหน้าในการดาวน์โหลด

    หากต้องการสร้างอินสแตนซ์ของ IStub โดยการเรียกใช้ CreateStub() คุณต้องส่ง การติดตั้งใช้งานอินเทอร์เฟซ IDownloaderClient และการติดตั้งใช้งาน DownloaderService ส่วนถัดไปเกี่ยวกับการรับความคืบหน้าในการดาวน์โหลดจะอธิบายอินเทอร์เฟซ IDownloaderClient ซึ่งโดยปกติแล้วคุณควรใช้ในคลาส Activity เพื่อให้คุณอัปเดต UI ของกิจกรรมได้เมื่อสถานะการดาวน์โหลดเปลี่ยนแปลง

    เราขอแนะนำให้คุณเรียก CreateStub() เพื่อสร้างอินสแตนซ์ของ IStub ในเมธอด onCreate() ของกิจกรรม หลังจากที่ startDownloadServiceIfRequired() เริ่มดาวน์โหลด

    ตัวอย่างเช่น ในตัวอย่างโค้ดก่อนหน้าสำหรับ onCreate() คุณสามารถตอบกลับผลลัพธ์ startDownloadServiceIfRequired() ได้ดังนี้

    Kotlin

            // Start the download service (if required)
            val startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    this@MainActivity,
                    pendingIntent,
                    SampleDownloaderService::class.java
            )
            // If download has started, initialize activity to show progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // Instantiate a member instance of IStub
                downloaderClientStub =
                        DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService::class.java)
                // Inflate layout that shows download progress
                setContentView(R.layout.downloader_ui)
                return
            }

    Java

            // Start the download service (if required)
            int startResult =
                DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize activity to show progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // Instantiate a member instance of IStub
                downloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
                        SampleDownloaderService.class);
                // Inflate layout that shows download progress
                setContentView(R.layout.downloader_ui);
                return;
            }

    หลังจากที่เมธอด onCreate() แสดงผลแล้ว กิจกรรมจะได้รับการเรียกไปยัง onResume() ซึ่งเป็นที่ที่คุณควรเรียก connect() ใน IStub โดยส่ง Context ของแอป ในทางกลับกัน คุณควรเรียกใช้ disconnect()ในแฮนเดิล onStop() ของกิจกรรม

    Kotlin

    override fun onResume() {
        downloaderClientStub?.connect(this)
        super.onResume()
    }
    
    override fun onStop() {
        downloaderClientStub?.disconnect(this)
        super.onStop()
    }

    Java

    @Override
    protected void onResume() {
        if (null != downloaderClientStub) {
            downloaderClientStub.connect(this);
        }
        super.onResume();
    }
    
    @Override
    protected void onStop() {
        if (null != downloaderClientStub) {
            downloaderClientStub.disconnect(this);
        }
        super.onStop();
    }

    การเรียกใช้ connect() ใน IStub จะเชื่อมโยงกิจกรรมของคุณกับ DownloaderService เพื่อให้กิจกรรมของคุณได้รับการเรียกกลับเกี่ยวกับการเปลี่ยนแปลงสถานะการดาวน์โหลดผ่านอินเทอร์เฟซ IDownloaderClient

รับความคืบหน้าการดาวน์โหลด

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

เมธอดอินเทอร์เฟซที่จำเป็นสำหรับ IDownloaderClient มีดังนี้

onServiceConnected(Messenger m)
หลังจากสร้างอินสแตนซ์ของ IStub ในกิจกรรมแล้ว คุณจะได้รับการเรียกไปยังเมธอดนี้ ซึ่งส่งออบเจ็กต์ Messenger ที่เชื่อมต่อกับอินสแตนซ์ของ DownloaderService หากต้องการส่งคำขอไปยังบริการ เช่น หยุดชั่วคราวและดาวน์โหลดต่อ คุณต้องเรียกใช้ DownloaderServiceMarshaller.CreateProxy() เพื่อรับอินเทอร์เฟซ IDownloaderService ที่เชื่อมต่อกับบริการ

การติดตั้งใช้งานที่แนะนําจะมีลักษณะดังนี้

Kotlin

private var remoteService: IDownloaderService? = null
...

override fun onServiceConnected(m: Messenger) {
    remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply {
        downloaderClientStub?.messenger?.also { messenger ->
            onClientUpdated(messenger)
        }
    }
}

Java

private IDownloaderService remoteService;
...

@Override
public void onServiceConnected(Messenger m) {
    remoteService = DownloaderServiceMarshaller.CreateProxy(m);
    remoteService.onClientUpdated(downloaderClientStub.getMessenger());
}

เมื่อIDownloaderServiceออบเจ็กต์เริ่มต้นแล้ว คุณจะส่งคำสั่งไปยัง บริการดาวน์โหลดได้ เช่น หยุดชั่วคราวและกลับมาดาวน์โหลดต่อ (requestPauseDownload() และ requestContinueDownload())

onDownloadStateChanged(int newState)
บริการดาวน์โหลดจะเรียกใช้ฟังก์ชันนี้เมื่อมีการเปลี่ยนแปลงสถานะการดาวน์โหลด เช่น เมื่อ การดาวน์โหลดเริ่มขึ้นหรือเสร็จสมบูรณ์

ค่า newState จะเป็นค่าใดค่าหนึ่งจากค่าที่เป็นไปได้หลายค่าที่ระบุไว้ใน โดยค่าคงที่ STATE_* ของคลาส IDownloaderClient

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

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

เคล็ดลับ: ดูตัวอย่างการเรียกกลับเหล่านี้ที่อัปเดต UI ความคืบหน้าในการดาวน์โหลด ได้ที่ SampleDownloaderActivity ในแอปตัวอย่างที่มาพร้อมกับ แพ็กเกจไฟล์ขยาย APK

เมธอดสาธารณะบางอย่างสำหรับอินเทอร์เฟซ IDownloaderService ที่คุณอาจพบว่ามีประโยชน์มีดังนี้

requestPauseDownload()
หยุดการดาวน์โหลดชั่วคราว
requestContinueDownload()
ดาวน์โหลดต่อ
setDownloadFlags(int flags)
กำหนดค่ากำหนดของผู้ใช้สำหรับประเภทเครือข่ายที่อนุญาตให้ดาวน์โหลดไฟล์ การใช้งานปัจจุบันรองรับฟีเจอร์ FLAGS_DOWNLOAD_OVER_CELLULAR เพียงรายการเดียว แต่คุณสามารถเพิ่มฟีเจอร์อื่นๆ ได้ โดยค่าเริ่มต้น ระบบจะไม่เปิดใช้ Flag นี้ ดังนั้นผู้ใช้ต้องใช้ Wi-Fi เพื่อดาวน์โหลด ไฟล์ส่วนขยาย คุณอาจต้องการระบุค่ากำหนดของผู้ใช้เพื่อเปิดใช้การดาวน์โหลดผ่าน เครือข่ายมือถือ ในกรณีนี้ คุณสามารถโทรหา

Kotlin

remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply {
    ...
    setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR)
}

Java

remoteService
    .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);

การใช้ APKExpansionPolicy

หากตัดสินใจสร้างบริการดาวน์โหลดของคุณเองแทนการใช้ Downloader Library ของ Google Play คุณควรใช้ APKExpansionPolicy ที่มีให้ใน License Verification Library คลาส APKExpansionPolicy เกือบจะเหมือนกับ ServerManagedPolicy (มีอยู่ใน ไลบรารีการตรวจสอบใบอนุญาตของ Google Play) แต่มีการจัดการเพิ่มเติมสำหรับส่วนเสริมการตอบกลับของไฟล์ การขยาย APK

หมายเหตุ: หากคุณใช้ ไลบรารี Downloader ตามที่อธิบายไว้ในส่วนก่อนหน้า ไลบรารีจะโต้ตอบกับ APKExpansionPolicy ทั้งหมด คุณจึงไม่ต้องใช้คลาสนี้โดยตรง

คลาสมีเมธอดที่จะช่วยให้คุณได้รับข้อมูลที่จำเป็นเกี่ยวกับไฟล์ส่วนขยายที่มีอยู่

  • getExpansionURLCount()
  • getExpansionURL(int index)
  • getExpansionFileName(int index)
  • getExpansionFileSize(int index)

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

การอ่านไฟล์เสริม

เมื่อบันทึกไฟล์เสริมของ APK ในอุปกรณ์แล้ว วิธีอ่านไฟล์จะขึ้นอยู่กับประเภทไฟล์ที่คุณใช้ ดังที่ได้กล่าวไว้ในภาพรวม ไฟล์ส่วนขยายอาจเป็นไฟล์ประเภทใดก็ได้ที่คุณต้องการ แต่ระบบจะเปลี่ยนชื่อไฟล์โดยใช้รูปแบบชื่อไฟล์ที่เฉพาะเจาะจงและบันทึกไว้ใน<shared-storage>/Android/obb/<package-name>/

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

หมายเหตุ: เมื่อแอปเริ่มต้น คุณควรตรวจสอบเสมอว่า พื้นที่เก็บข้อมูลภายนอกพร้อมใช้งานและอ่านได้หรือไม่โดยการเรียกใช้ getExternalStorageState() ซึ่งจะแสดงสตริงที่เป็นไปได้หลายรายการ ที่แสดงสถานะของพื้นที่เก็บข้อมูลภายนอก ค่าที่ส่งคืนต้องเป็น MEDIA_MOUNTED เพื่อให้แอปอ่านได้

การรับชื่อไฟล์

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

[main|patch].<expansion-version>.<package-name>.obb

หากต้องการทราบตำแหน่งและชื่อของไฟล์ส่วนขยาย คุณควรใช้วิธีการ getExternalStorageDirectory()และgetPackageName()เพื่อสร้างเส้นทางไปยังไฟล์

ต่อไปนี้คือวิธีที่คุณใช้ในแอปเพื่อรับอาร์เรย์ที่มีเส้นทางแบบสมบูรณ์ ไปยังทั้ง 2 ไฟล์ส่วนขยายได้

Kotlin

fun getAPKExpansionFiles(ctx: Context, mainVersion: Int, patchVersion: Int): Array<String> {
    val packageName = ctx.packageName
    val ret = mutableListOf<String>()
    if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
        // Build the full path to the app's expansion files
        val root = Environment.getExternalStorageDirectory()
        val expPath = File(root.toString() + EXP_PATH + packageName)

        // Check that expansion file path exists
        if (expPath.exists()) {
            if (mainVersion > 0) {
                val strMainPath = "$expPath${File.separator}main.$mainVersion.$packageName.obb"
                val main = File(strMainPath)
                if (main.isFile) {
                    ret += strMainPath
                }
            }
            if (patchVersion > 0) {
                val strPatchPath = "$expPath${File.separator}patch.$mainVersion.$packageName.obb"
                val main = File(strPatchPath)
                if (main.isFile) {
                    ret += strPatchPath
                }
            }
        }
    }
    return ret.toTypedArray()
}

Java

// The shared path to all app expansion files
private final static String EXP_PATH = "/Android/obb/";

static String[] getAPKExpansionFiles(Context ctx, int mainVersion,
      int patchVersion) {
    String packageName = ctx.getPackageName();
    Vector<String> ret = new Vector<String>();
    if (Environment.getExternalStorageState()
          .equals(Environment.MEDIA_MOUNTED)) {
        // Build the full path to the app's expansion files
        File root = Environment.getExternalStorageDirectory();
        File expPath = new File(root.toString() + EXP_PATH + packageName);

        // Check that expansion file path exists
        if (expPath.exists()) {
            if ( mainVersion > 0 ) {
                String strMainPath = expPath + File.separator + "main." +
                        mainVersion + "." + packageName + ".obb";
                File main = new File(strMainPath);
                if ( main.isFile() ) {
                        ret.add(strMainPath);
                }
            }
            if ( patchVersion > 0 ) {
                String strPatchPath = expPath + File.separator + "patch." +
                        mainVersion + "." + packageName + ".obb";
                File main = new File(strPatchPath);
                if ( main.isFile() ) {
                        ret.add(strPatchPath);
                }
            }
        }
    }
    String[] retArray = new String[ret.size()];
    ret.toArray(retArray);
    return retArray;
}

คุณเรียกใช้เมธอดนี้ได้โดยส่งแอปของคุณContext และเวอร์ชันของไฟล์เสริมที่ต้องการ

คุณกำหนดหมายเลขเวอร์ชันของไฟล์เสริมได้หลายวิธี วิธีง่ายๆ วิธีหนึ่งคือการบันทึกเวอร์ชันในไฟล์ SharedPreferences เมื่อการดาวน์โหลดเริ่มขึ้น โดยการค้นหาชื่อไฟล์เสริมด้วยเมธอด getExpansionFileName(int index) ของคลาส APKExpansionPolicy จากนั้นคุณจะรับรหัสเวอร์ชันได้โดยการอ่านไฟล์ SharedPreferences เมื่อต้องการเข้าถึงไฟล์ ส่วนขยาย

ดูข้อมูลเพิ่มเติมเกี่ยวกับการอ่านจาก Shared Storage ได้ที่เอกสารประกอบที่เก็บข้อมูล

การใช้ไลบรารี Zip สำหรับขยายของ APK

แพ็กเกจการขยาย APK ของ Google Market มีไลบรารีที่ชื่อว่า APK Expansion Zip Library (อยู่ใน <sdk>/extras/google/google_market_apk_expansion/zip_file/) ซึ่งเป็นไลบรารีที่ไม่บังคับ ที่จะช่วยให้คุณอ่านไฟล์สำหรับขยายได้ เมื่อบันทึกเป็นไฟล์ ZIP การใช้ไลบรารีนี้ช่วยให้คุณอ่านทรัพยากรจาก ไฟล์การขยาย ZIP เป็นระบบไฟล์เสมือนได้ง่ายๆ

ไลบรารี Zip ของไฟล์เสริม APK มีคลาสและ API ต่อไปนี้

APKExpansionSupport
มีวิธีการเข้าถึงชื่อไฟล์เสริมและไฟล์ ZIP ดังนี้
getAPKExpansionFiles()
วิธีการเดียวกันกับที่แสดงด้านบนซึ่งจะแสดงเส้นทางไฟล์ที่สมบูรณ์ไปยังทั้งไฟล์ส่วนขยาย
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
แสดงผล ZipResourceFile ที่แสดงถึงผลรวมของทั้งไฟล์หลักและ ไฟล์แพตช์ กล่าวคือ หากคุณระบุทั้ง mainVersion และ patchVersion ระบบจะแสดง ZipResourceFile ที่ให้สิทธิ์เข้าถึงแบบอ่านแก่ ข้อมูลทั้งหมด โดยจะผสานข้อมูลของไฟล์แพตช์ไว้เหนือไฟล์หลัก
ZipResourceFile
แสดงไฟล์ ZIP ในพื้นที่เก็บข้อมูลที่แชร์และดำเนินการทั้งหมดเพื่อจัดเตรียมระบบไฟล์เสมือน ตามไฟล์ ZIP คุณรับอินสแตนซ์ได้โดยใช้ APKExpansionSupport.getAPKExpansionZipFile() หรือใช้ ZipResourceFile โดยส่งเส้นทางไปยังไฟล์เสริม คลาสนี้มีเมธอดที่มีประโยชน์มากมาย แต่โดยทั่วไปแล้วคุณไม่จำเป็นต้องเข้าถึงเมธอดส่วนใหญ่ วิธีการสำคัญ 2 วิธีมีดังนี้
getInputStream(String assetPath)
ระบุ InputStream เพื่ออ่านไฟล์ภายในไฟล์ ZIP assetPath ต้องเป็นเส้นทางไปยังไฟล์ที่ต้องการ โดยสัมพันธ์กับ รูทของเนื้อหาไฟล์ ZIP
getAssetFileDescriptor(String assetPath)
ระบุ AssetFileDescriptor สำหรับไฟล์ภายใน ไฟล์ ZIP assetPath ต้องเป็นเส้นทางไปยังไฟล์ที่ต้องการ โดยสัมพันธ์กับ รูทของเนื้อหาไฟล์ ZIP ซึ่งจะเป็นประโยชน์สำหรับ Android API บางรายการที่ต้องใช้ AssetFileDescriptor เช่น API ของ MediaPlayer บางรายการ
APEZProvider
แอปส่วนใหญ่ไม่จำเป็นต้องใช้คลาสนี้ คลาสนี้กำหนด ContentProvider ที่จัดรูปแบบข้อมูลจากไฟล์ ZIP ผ่าน Content Provider Uri เพื่อให้สิทธิ์เข้าถึงไฟล์สำหรับ Android API บางรายการที่ คาดหวังการเข้าถึง Uri ไฟล์สื่อ เช่น ฟีเจอร์นี้มีประโยชน์หากคุณต้องการ เล่นวิดีโอที่มีVideoView.setVideoURI()

ข้ามการบีบอัดไฟล์สื่อเป็น ZIP

หากคุณใช้ไฟล์สำหรับขยายเพื่อจัดเก็บไฟล์สื่อ ไฟล์ ZIP จะยังช่วยให้คุณ ใช้การเรียกการเล่นสื่อของ Android ที่มีการควบคุมออฟเซ็ตและความยาว (เช่น MediaPlayer.setDataSource() และ SoundPool.load()) ได้ เพื่อให้ การดำเนินการนี้ใช้งานได้ คุณต้องไม่บีบอัดไฟล์สื่อเพิ่มเติมเมื่อสร้างแพ็กเกจ ZIP เช่น เมื่อใช้เครื่องมือ zip คุณควรใช้ตัวเลือก -n เพื่อระบุนามสกุลไฟล์ที่ไม่ควรบีบอัด

zip -n .mp4;.ogg main_expansion media_files

การอ่านจากไฟล์ ZIP

เมื่อใช้ไลบรารี Zip ของไฟล์เสริม APK การอ่านไฟล์จาก ZIP มักจะต้องมีสิ่งต่อไปนี้

Kotlin

// Get a ZipResourceFile representing a merger of both the main and patch files
val expansionFile =
        APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion)

// Get an input stream for a known file inside the expansion file ZIPs
expansionFile.getInputStream(pathToFileInsideZip).use {
    ...
}

Java

// Get a ZipResourceFile representing a merger of both the main and patch files
ZipResourceFile expansionFile =
    APKExpansionSupport.getAPKExpansionZipFile(appContext,
        mainVersion, patchVersion);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

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

หากต้องการอ่านจากไฟล์สำหรับขยายที่เฉพาะเจาะจง ให้ใช้ตัวสร้าง ZipResourceFile พร้อมเส้นทางไปยังไฟล์สำหรับขยายที่ต้องการ

Kotlin

// Get a ZipResourceFile representing a specific expansion file
val expansionFile = ZipResourceFile(filePathToMyZip)

// Get an input stream for a known file inside the expansion file ZIPs
expansionFile.getInputStream(pathToFileInsideZip).use {
    ...
}

Java

// Get a ZipResourceFile representing a specific expansion file
ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

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

การทดสอบไฟล์สำหรับขยาย

ก่อนเผยแพร่แอป คุณควรทดสอบ 2 สิ่ง ได้แก่ การอ่านไฟล์เสริมและการดาวน์โหลดไฟล์

การทดสอบการอ่านไฟล์

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

  1. สร้างไดเรกทอรีที่เหมาะสมในที่เก็บข้อมูลที่ใช้ร่วมกันซึ่ง Google Play จะบันทึกไฟล์ของคุณ

    เช่น หากชื่อแพ็กเกจคือ com.example.android คุณต้องสร้าง ไดเรกทอรี Android/obb/com.example.android/ ในพื้นที่เก็บข้อมูลที่แชร์ (เสียบ อุปกรณ์ทดสอบเข้ากับคอมพิวเตอร์เพื่อเมานต์ที่เก็บข้อมูลที่แชร์และสร้างไดเรกทอรีนี้ด้วยตนเอง )

  2. เพิ่มไฟล์สำหรับขยายลงในไดเรกทอรีนั้นด้วยตนเอง อย่าลืมเปลี่ยนชื่อไฟล์ให้เป็นไปตามรูปแบบชื่อไฟล์ที่ Google Play จะใช้

    เช่น ไม่ว่าจะเป็นไฟล์ประเภทใด ไฟล์เสริมหลักสำหรับแอป com.example.android ควรเป็น main.0300110.com.example.android.obb รหัสเวอร์ชันสามารถเป็นค่าใดก็ได้ตามต้องการ โปรดทราบว่า

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

ข้อควรทราบเกี่ยวกับการจัดการไฟล์ส่วนขยายมีดังนี้

  • อย่านำออกหรือเปลี่ยนชื่อ.obbไฟล์เสริม (แม้ว่าคุณจะแตกไฟล์ ข้อมูลไปยังตำแหน่งอื่นก็ตาม) การทำเช่นนี้จะทำให้ Google Play (หรือแอปของคุณเอง) ดาวน์โหลดไฟล์เสริมซ้ำๆ
  • อย่าบันทึกข้อมูลอื่นๆ ลงในobb/ ไดเรกทอรี หากต้องคลายแพ็กข้อมูลบางอย่าง ให้บันทึกข้อมูลนั้นไปยังตำแหน่งที่ getExternalFilesDir() ระบุ

การทดสอบการดาวน์โหลดไฟล์

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

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

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

การอัปเดตแอป

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

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

หากใช้ไฟล์ ZIP เป็นไฟล์เสริม APK Expansion Zip Library ที่รวมอยู่ในแพ็กเกจการขยาย APK จะมีความสามารถในการผสาน ไฟล์ แพตช์กับไฟล์เสริมหลัก

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

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

ปัญหาบางอย่างที่คุณควรทราบเกี่ยวกับการอัปเดตไฟล์ส่วนขยายมีดังนี้

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