การพัฒนา Android แบบวนซ้ำอย่างรวดเร็ว
หน้านี้จะอธิบายวิธีที่ bazel mobile-install ช่วยให้การพัฒนาแบบวนซ้ำ
สำหรับ Android เร็วขึ้นมาก โดยจะอธิบายถึงประโยชน์ของแนวทางนี้เมื่อเทียบกับ
ข้อเสียของการสร้างและติดตั้งแยกกัน
สรุป
หากต้องการติดตั้งการเปลี่ยนแปลงเล็กๆ น้อยๆ ในแอป Android อย่างรวดเร็ว ให้ทำดังนี้
- ค้นหา
android_binaryของแอปที่ต้องการติดตั้ง - เชื่อมต่ออุปกรณ์กับ
adb - เรียกใช้
bazel mobile-install :your_targetการเริ่มต้นแอปจะช้ากว่าปกติเล็กน้อย - แก้ไขโค้ดหรือทรัพยากร Android
- เรียกใช้
bazel mobile-install :your_target - เพลิดเพลินกับการติดตั้งที่รวดเร็วและง่ายดาย
ตัวเลือกบรรทัดคำสั่งบางอย่างสำหรับ Bazel ที่อาจเป็นประโยชน์มีดังนี้
--adbบอก Bazel ว่าจะใช้ไบนารี adb ใด--adb_argใช้เพื่อเพิ่มอาร์กิวเมนต์พิเศษลงในบรรทัดคำสั่งของadbได้ การใช้งานที่มีประโยชน์อย่างหนึ่งคือการเลือกอุปกรณ์ที่ต้องการติดตั้ง หากคุณมีอุปกรณ์หลายเครื่องที่เชื่อมต่อกับเวิร์กสเตชันbazel mobile-install :your_target -- --adb_arg=-s --adb_arg=<SERIAL>
หากไม่แน่ใจ ให้ดูตัวอย่าง ติดต่อเราใน Google Groups หรือแจ้งปัญหาใน GitHub
บทนำ
คุณสมบัติที่สำคัญที่สุดอย่างหนึ่งของเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์คือความเร็ว ความแตกต่างระหว่างการเปลี่ยนโค้ดและดูโค้ดทำงานภายใน 1 วินาทีกับการต้องรอหลายนาทีหรือบางครั้งอาจเป็นชั่วโมงก่อนที่จะได้รับความคิดเห็น ว่าการเปลี่ยนแปลงของคุณทำงานได้ตามที่คาดไว้หรือไม่นั้นมีมาก
น่าเสียดายที่ Toolchain แบบเดิมของ Android สำหรับการสร้าง .apk นั้นมีขั้นตอนแบบลำดับที่ซับซ้อนมากมาย และต้องทำตามขั้นตอนทั้งหมดนี้เพื่อสร้างแอป Android ที่ Google การรอ 5 นาทีเพื่อสร้างการเปลี่ยนแปลงบรรทัดเดียวไม่ใช่เรื่องแปลกในโปรเจ็กต์ขนาดใหญ่ เช่น Google Maps
bazel mobile-install ช่วยให้การพัฒนาแบบวนซ้ำสำหรับ Android เร็วขึ้นมากโดย
ใช้การผสมผสานระหว่างการตัดทอนการเปลี่ยนแปลง การแบ่งงาน และการจัดการที่ชาญฉลาดของ
ส่วนประกอบภายในของ Android โดยไม่ต้องเปลี่ยนโค้ดของแอป
ปัญหาเกี่ยวกับการติดตั้งแอปแบบเดิม
การสร้างแอป Android มีปัญหาบางอย่าง เช่น
Dexing โดยค่าเริ่มต้น เครื่องมือ Dexer (เดิมคือ
dxปัจจุบันคือd8หรือr8) จะเรียกใช้เพียงครั้งเดียวในการสร้าง และไม่ทราบวิธีนำงานจากการสร้างก่อนหน้ามาใช้ซ้ำ จึงจะแปลงทุกเมธอดเป็น Dex อีกครั้ง แม้ว่าจะมีการเปลี่ยนแปลงเพียงเมธอดเดียวก็ตามการอัปโหลดข้อมูลไปยังอุปกรณ์ adb ไม่ได้ใช้แบนด์วิดท์ทั้งหมดของการเชื่อมต่อ USB 2.0 และแอปขนาดใหญ่อาจใช้เวลาในการอัปโหลดนานมาก ระบบจะอัปโหลดทั้งแอปแม้ว่าจะมีการเปลี่ยนแปลงเพียงเล็กน้อย เช่น ทรัพยากรหรือเมธอดเดียว ซึ่งอาจเป็นจุดคอขวดที่สำคัญ
การคอมไพล์เป็นโค้ดที่มาพร้อมเครื่อง Android L ได้เปิดตัว ART ซึ่งเป็นรันไทม์ใหม่ของ Android ที่คอมไพล์แอปแบบล่วงหน้าแทนที่จะคอมไพล์แบบทันทีเหมือน Dalvik ซึ่งจะทำให้แอปทำงานได้เร็วขึ้นมาก แต่จะใช้เวลาในการติดตั้งนานขึ้น ซึ่งเป็นข้อแลกเปลี่ยนที่ดีสำหรับผู้ใช้ เนื่องจากโดยปกติแล้วผู้ใช้จะติดตั้งแอปเพียงครั้งเดียวและใช้งานหลายครั้ง แต่จะทำให้การพัฒนาช้าลงในกรณีที่แอปได้รับการติดตั้งหลายครั้งและแต่ละเวอร์ชันจะทำงานไม่กี่ครั้ง
แนวทางของ bazel mobile-install
bazel mobile-install ได้ทำการปรับปรุงดังนี้
การแยกส่วนและการแปลง DEX หลังจากสร้างโค้ด Java ของแอปแล้ว Bazel จะแบ่งไฟล์คลาสออกเป็นส่วนๆ ที่มีขนาดใกล้เคียงกัน และเรียกใช้
d8แยกกันd8จะไม่เรียกใช้ใน Shard ที่ไม่มีการเปลี่ยนแปลงตั้งแต่ การสร้างครั้งล่าสุด จากนั้นจะคอมไพล์ Shard เหล่านี้เป็น APK ที่ Shard แยกกันการโอนไฟล์แบบเพิ่ม ระบบจะนำทรัพยากร Android, ไฟล์ .dex และไลบรารีแบบเนทีฟ ออกจาก .apk หลักและจัดเก็บไว้ในไดเรกทอรีการติดตั้งบนอุปกรณ์เคลื่อนที่แยกต่างหาก ซึ่งช่วยให้คุณอัปเดตโค้ดและทรัพยากร Android ได้อย่างอิสระโดยไม่ต้องติดตั้งแอปทั้งแอปอีกครั้ง ดังนั้น การโอนไฟล์จึงใช้เวลาน้อยลง และระบบจะคอมไพล์ไฟล์ .dex ที่มีการเปลี่ยนแปลงอีกครั้งในอุปกรณ์เท่านั้น
การติดตั้งแบบ Shard การติดตั้งบนอุปกรณ์เคลื่อนที่จะใช้เครื่องมือ
apkdeployerของ Android Studio เพื่อรวม APK ที่แยกส่วนในอุปกรณ์ที่เชื่อมต่อและมอบประสบการณ์การใช้งานที่สอดคล้องกัน
การจัดทำดัชนีแบบชาร์ด
การแบ่ง DEX ออกเป็นส่วนๆ นั้นค่อนข้างตรงไปตรงมา เมื่อสร้างไฟล์ .jar แล้ว เครื่องมือจะ
แบ่งไฟล์เหล่านั้นออกเป็นไฟล์ .jar แยกกันซึ่งมีขนาดใกล้เคียงกัน จากนั้นจะเรียกใช้
d8 ในไฟล์ที่มีการเปลี่ยนแปลงนับตั้งแต่การสร้างครั้งก่อน ตรรกะที่กำหนดว่าควร dex ชาร์ดใดไม่ได้เจาะจงสำหรับ Android แต่ใช้อัลกอริทึมการตัดแต่งการเปลี่ยนแปลงทั่วไปของ Bazel
อัลกอริทึมการแบ่งข้อมูลเวอร์ชันแรกเพียงแค่จัดเรียงไฟล์ .class ตามลำดับตัวอักษร แล้วแบ่งรายการออกเป็นส่วนๆ ที่มีขนาดเท่ากัน แต่ปรากฏว่าวิธีนี้ไม่เหมาะสม หากมีการเพิ่มหรือนำคลาสออก (แม้จะเป็นคลาสที่ซ้อนกันหรือคลาสที่ไม่ระบุชื่อ) ก็จะทำให้คลาสทั้งหมดที่อยู่หลังคลาสนั้นตามลำดับตัวอักษรเลื่อนไป 1 ตำแหน่ง ซึ่งส่งผลให้ต้องทำการ Dexing Shard เหล่านั้นอีกครั้ง จึงตัดสินใจที่จะแบ่งแพ็กเกจ Java แทนที่จะแบ่งคลาสแต่ละรายการ แน่นอนว่าการดำเนินการนี้ยังคงส่งผลให้มีการจัดทำดัชนีหลายๆ ชาร์ดหากมีการเพิ่มหรือนำแพ็กเกจใหม่ออก แต่การดำเนินการนี้จะเกิดขึ้นไม่บ่อยเท่ากับการเพิ่มหรือนำคลาสเดียวออก
การกำหนดจำนวน Shard จะควบคุมโดยการกำหนดค่าบรรทัดคำสั่งโดยใช้ Flag
--define=num_dex_shards=N ในโลกที่สมบูรณ์แบบ Bazel จะ
กำหนดจำนวน Shard ที่ดีที่สุดโดยอัตโนมัติ แต่ปัจจุบัน Bazel ต้องทราบ
ชุดการดำเนินการ (เช่น คำสั่งที่จะดำเนินการระหว่างการสร้าง) ก่อน
ที่จะดำเนินการใดๆ ดังนั้นจึงไม่สามารถกำหนดจำนวน Shard ที่เหมาะสมได้
เนื่องจากไม่ทราบว่าในที่สุดแล้วจะมีคลาส Java กี่คลาสใน
แอป โดยทั่วไปแล้ว ยิ่งมี Shard มากเท่าใด การสร้างและการ
ติดตั้งก็จะยิ่งเร็วขึ้น แต่การเริ่มต้นแอปจะช้าลงเนื่องจากลิงก์เกอร์แบบไดนามิก
ต้องทำงานมากขึ้น โดยปกติแล้ว จำนวนที่เหมาะสมจะอยู่ระหว่าง 10 ถึง 50 ชาร์ด
การติดตั้งใช้งานแบบค่อยเป็นค่อยไป
ตอนนี้ยูทิลิตี apkdeployer ที่อธิบายไว้ใน "แนวทางของ mobile-install" จะจัดการการโอนและการติดตั้ง Shard ของ APK แบบเพิ่มทีละรายการ
ในขณะที่เวอร์ชันก่อนหน้า (เนทีฟ) ของการติดตั้งบนอุปกรณ์เคลื่อนที่ต้องมีการติดตามการติดตั้งครั้งแรกด้วยตนเอง
และใช้แฟล็ก --incremental
ในการติดตั้งครั้งต่อๆ ไปแบบเลือกได้ แต่เวอร์ชันล่าสุดใน rules_android
ได้รับการปรับปรุงให้ง่ายขึ้นมาก คุณสามารถใช้การเรียกใช้การติดตั้งบนอุปกรณ์เคลื่อนที่เดียวกันได้ไม่ว่าแอปจะได้รับการติดตั้ง
หรือติดตั้งซ้ำกี่ครั้งก็ตาม
ในระดับสูง apkdeployer คือ Wrapper สำหรับadb
คำสั่งย่อยต่างๆ ตรรกะของจุดแรกเข้าหลักจะอยู่ในคลาส
com.android.tools.deployer.Deployer
โดยมีคลาสยูทิลิตีอื่นๆ อยู่ในแพ็กเกจเดียวกัน
คลาส Deployer จะรับรายการเส้นทางไปยัง APK ที่แยกและ Protobuf ที่มีข้อมูลเกี่ยวกับการติดตั้ง รวมถึงใช้ประโยชน์จากฟีเจอร์การติดตั้งใช้งานสำหรับ Android App Bundle
เพื่อสร้างเซสชันการติดตั้งและติดตั้งใช้งานการแยกแอปทีละรายการ
ดูรายละเอียดการติดตั้งได้ที่คลาส ApkPreInstaller
และ ApkInstaller
ผลลัพธ์
ประสิทธิภาพ
โดยทั่วไปแล้ว bazel mobile-install จะช่วยให้สร้าง
และติดตั้งแอปขนาดใหญ่ได้เร็วขึ้น 4-10 เท่าหลังจากมีการเปลี่ยนแปลงเล็กน้อย
ตัวเลขต่อไปนี้คำนวณขึ้นสำหรับผลิตภัณฑ์บางอย่างของ Google
ซึ่งแน่นอนว่าขึ้นอยู่กับลักษณะของการเปลี่ยนแปลง การคอมไพล์ใหม่หลังจากเปลี่ยนไลบรารีพื้นฐานจะใช้เวลานานกว่า
ข้อจำกัด
แต่กลเม็ดที่แอปพลิเคชัน Stub ใช้ไม่ได้ผลในทุกกรณี กรณีต่อไปนี้แสดงให้เห็นว่าฟีเจอร์นี้ทำงานไม่เป็นไปตามที่คาดไว้
การติดตั้งแอปบนอุปกรณ์เคลื่อนที่รองรับเฉพาะผ่านกฎ Starlark ของ
rules_androidเท่านั้น ดูรายละเอียดเพิ่มเติมได้ที่"ประวัติโดยย่อของการติดตั้งบนอุปกรณ์เคลื่อนที่"รองรับเฉพาะอุปกรณ์ที่ใช้ ART เท่านั้น การติดตั้งบนอุปกรณ์เคลื่อนที่จะใช้ API และฟีเจอร์รันไทม์ ที่มีอยู่ในอุปกรณ์ที่ใช้ ART เท่านั้น ไม่ใช่ Dalvik รันไทม์ Android ที่ใหม่กว่า Android L (API 21 ขึ้นไป) ควรเข้ากันได้
Bazel เองต้องทำงานด้วยรันไทม์ Java ของเครื่องมือและเวอร์ชันภาษา 17 ขึ้นไป
Bazel เวอร์ชันก่อน 8.4.0 ต้องระบุแฟล็กเพิ่มเติมบางอย่างสำหรับ mobile-install ดูบทแนะนำ Bazel สำหรับ Android แฟล็กเหล่านี้จะแจ้งให้ Bazel ทราบว่า Aspect การติดตั้งบนอุปกรณ์เคลื่อนที่ของ Starlark อยู่ที่ใดและรองรับกฎใดบ้าง
ประวัติโดยย่อของการติดตั้งบนอุปกรณ์เคลื่อนที่
Bazel เวอร์ชันก่อนหน้ามีกฎการบิลด์และการทดสอบในตัวโดยค่าเริ่มต้นสำหรับ ภาษาและระบบนิเวศยอดนิยม เช่น C++, Java และ Android ดังนั้นกฎเหล่านี้จึงเรียกว่ากฎเนทีฟ Bazel 8 (เปิดตัวในปี 2024) ได้นำ การรองรับกฎเหล่านี้ออกเนื่องจากมีการย้ายข้อมูลกฎหลายรายการไปยังภาษา Starlark ดูรายละเอียดเพิ่มเติมได้ที่"บล็อกโพสต์เกี่ยวกับ Bazel 8.0 LTS"
กฎ Android เนทีฟเดิมยังรองรับฟังก์ชันการติดตั้งบนอุปกรณ์เคลื่อนที่เวอร์ชันเนทีฟเดิมด้วย ปัจจุบันเราเรียกแคมเปญประเภทนี้ว่า "การติดตั้งแอปบนอุปกรณ์เคลื่อนที่ v1" หรือ "การติดตั้งแอปบนอุปกรณ์เคลื่อนที่แบบเนทีฟ" ฟังก์ชันการทำงานนี้ถูกลบออกใน Bazel 8 พร้อมกับกฎ Android ในตัว
ตอนนี้ฟังก์ชันการติดตั้งบนอุปกรณ์เคลื่อนที่ทั้งหมด รวมถึงกฎการสร้างและการทดสอบ Android ทั้งหมด
ได้รับการติดตั้งใช้งานใน Starlark และอยู่ในที่เก็บ rules_android GitHub
เวอร์ชันล่าสุดเรียกว่า "การติดตั้งบนอุปกรณ์เคลื่อนที่ v3" หรือ "MIv3"
หมายเหตุเกี่ยวกับการตั้งชื่อ: เราเคยมี "การติดตั้งบนอุปกรณ์เคลื่อนที่ v2" ที่ใช้ได้ภายใน Google เท่านั้น แต่เราไม่เคยเผยแพร่ภายนอก และมีเพียง v3 เท่านั้นที่ยังคงใช้สำหรับการติดตั้งกฎทั้งภายใน Google และ OSS_android