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

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

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

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

ภาพรวม

ทุกครั้งที่คุณอัปโหลด APK โดยใช้ Google Play Console คุณจะมีตัวเลือก เพิ่มไฟล์สำหรับขยายไปยัง APK 1 หรือ 2 ไฟล์ แต่ละไฟล์ต้องมีขนาดไม่เกิน 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
ระบุว่าไฟล์เป็นไฟล์หลักหรือไฟล์สำหรับขยายแพตช์ อาจมี มีเพียงไฟล์หลัก 1 ไฟล์และไฟล์แพตช์ 1 ไฟล์สำหรับแต่ละ APK
<expansion-version>
นี่คือจำนวนเต็มที่ตรงกับรหัสเวอร์ชันของ APK ที่มีการขยาย first ที่เชื่อมโยง (ตรงกับ 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 ไม่สามารถดาวน์โหลดไฟล์สำหรับขยายได้ 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 ล่าช้าออกไป เล่น
  2. กำหนดว่าจะแยกทรัพยากรของแอปใดจาก APK แล้วรวมทรัพยากรเหล่านั้นไว้ใน ที่จะใช้เป็นไฟล์สำหรับขยายหลัก

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

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

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

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

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

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

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

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

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

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

  1. ไฟล์สำหรับขยายแต่ละไฟล์ต้องมีขนาดไม่เกิน 2 GB
  2. ในการดาวน์โหลดไฟล์สำหรับขยายจาก Google Play ผู้ใช้ต้องมี ได้รับแอปของคุณมาจาก Google Play Google Play จะไม่ ระบุ URL สำหรับไฟล์สำหรับขยายของคุณหากมีการติดตั้งแอปด้วยวิธีอื่น
  3. เมื่อดำเนินการดาวน์โหลดจากภายในแอป URL ที่ Google Play แต่ละไฟล์จะไม่ซ้ำกันสำหรับการดาวน์โหลดแต่ละครั้ง และแต่ละไฟล์จะหมดอายุในไม่ช้าหลังจากให้สิทธิ์ กับแอปของคุณ
  4. หากคุณอัปเดตแอปด้วย APK ใหม่หรืออัปโหลด 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 ระบุให้กับ ไฟล์สำหรับขยายจะไม่ซ้ำกันสำหรับการดาวน์โหลดแต่ละครั้ง และแต่ละไฟล์จะหมดอายุในไม่ช้าหลังจากมอบให้กับ แอปของคุณ

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

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

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

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

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

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

เกี่ยวกับไลบรารีโปรแกรมดาวน์โหลด

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

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

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

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

การเตรียมใช้ไลบรารีโปรแกรมดาวน์โหลด

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

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

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

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

  1. เลือก ไฟล์ > ใหม่ > โมดูลใหม่
  2. ในหน้าต่างสร้างโมดูลใหม่ ให้เลือกไลบรารี Android แล้วเลือกถัดไป
  3. ระบุชื่อแอป/ไลบรารี เช่น "คลังใบอนุญาต Google Play" และ "ไลบรารีการดาวน์โหลด Google Play" ให้เลือกระดับ SDK ขั้นต่ำ แล้วเลือก Finish
  4. เลือก ไฟล์ > โครงสร้างของโครงการ
  5. เลือกแท็บพร็อพเพอร์ตี้และในไลบรารี Repository ให้ป้อนไลบรารีจากไดเรกทอรี <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 มีตัวอย่าง แอป ที่แสดงวิธีใช้ไลบรารีโปรแกรมดาวน์โหลดในแอป ตัวอย่างใช้ไลบรารีที่ 3 มีอยู่ในแพ็กเกจการขยาย Apk ที่เรียกว่า APK Expansion Zip Library ถ้า ที่คุณวางแผน โดยใช้ไฟล์ ZIP สำหรับไฟล์สำหรับขยายของคุณ เราขอแนะนำให้คุณเพิ่มไลบรารี Zip สำหรับขยายของ APK ลงใน แอปของคุณ โปรดดูข้อมูลเพิ่มเติมในส่วนด้านล่าง เกี่ยวกับการใช้ไลบรารี ZIP สำหรับขยาย APK

การประกาศการให้สิทธิ์จากผู้ใช้

ในการดาวน์โหลดไฟล์สำหรับขยาย ให้ไลบรารีการดาวน์โหลด ต้องใช้สิทธิ์หลายรายการที่คุณต้องประกาศในไฟล์ 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

การใช้บริการโปรแกรมดาวน์โหลด

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

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

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

getPublicKey()
การดำเนินการนี้ต้องแสดงผลสตริงที่เป็นคีย์สาธารณะ RSA ที่เข้ารหัส Base64 สำหรับผู้เผยแพร่โฆษณา ที่พร้อมใช้งานจากหน้าโปรไฟล์ใน Play Console (ดูการตั้งค่าการอนุญาตให้ใช้สิทธิ)
getSALT()
การดำเนินการนี้ต้องแสดงผลอาร์เรย์ของไบต์แบบสุ่มที่การอนุญาตให้ใช้สิทธิ Policy ใช้เพื่อ สร้าง Obfuscator Salt จะช่วยให้แน่ใจว่า SharedPreferences ที่ปรับให้ยากต่อการอ่าน (Obfuscate) ของคุณ ไฟล์ที่มีการบันทึกข้อมูลการอนุญาตให้ใช้สิทธิของคุณจะเป็นไม่ซ้ำกันและไม่สามารถค้นพบได้
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 เป็นคีย์สาธารณะของบัญชีผู้เผยแพร่เนื้อหา คุณสามารถค้นหาคีย์ได้ใน "นักพัฒนาซอฟต์แวร์" คอนโซลด้านล่างข้อมูลโปรไฟล์ของคุณ ขั้นตอนนี้จำเป็น แม้ในขณะที่ทำการทดสอบ ที่คุณดาวน์โหลด

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

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

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

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

วิธีอินเทอร์เฟซที่จำเป็นสำหรับ 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() ช่วงเวลานี้ แสดงรหัสทรัพยากรสำหรับสตริงใดสตริงหนึ่งที่มาพร้อมกับเครื่องมือดาวน์โหลด คลัง เช่น สตริง "การดาวน์โหลดหยุดชั่วคราวเนื่องจากคุณโรมมิ่ง" สอดคล้องกับSTATE_PAUSED_ROAMING

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

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

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

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

Kotlin

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

Java

remoteService
    .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);

การใช้ APKExpansionPolicy

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

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

โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับการอ่านจากพื้นที่เก็บข้อมูลที่ใช้ร่วมกันที่หัวข้อพื้นที่เก็บข้อมูล เอกสารประกอบ

การใช้ไลบรารี 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()
วิธีการเดียวกันกับที่แสดงด้านบนซึ่งจะแสดงเส้นทางไฟล์ที่สมบูรณ์ไปยังการขยายทั้ง 2 ส่วน
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 วิธีนี้มีประโยชน์สำหรับ API ของ Android บางอย่างที่จำเป็นต้องใช้ AssetFileDescriptor เช่น API ของ MediaPlayer บางอย่าง
APEZProvider
แอปส่วนใหญ่ไม่จำเป็นต้องใช้ชั้นเรียนนี้ คลาสนี้กำหนด ContentProvider ที่รวมข้อมูลจากไฟล์ ZIP ผ่านเนื้อหา ผู้ให้บริการ Uri เพื่อให้สิทธิ์การเข้าถึงไฟล์สำหรับ API ของ Android บางอย่างที่ คาดว่าจะมีสิทธิ์เข้าถึงไฟล์สื่อ 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 แต่ละรายการ คุณใช้ไฟล์ที่สองเป็น "แพตช์" ได้ ซึ่งมีทั้งข้อมูลอัปเดตและชิ้นงานใหม่ๆ การทำเช่นนี้จะหลีกเลี่ยง ต้องดาวน์โหลดไฟล์เสริมหลักใหม่อีกครั้ง ซึ่งอาจมีขนาดใหญ่และมีราคาแพงสำหรับผู้ใช้

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

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

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

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

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

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