การพัฒนา 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 Shard ใดไม่ได้เจาะจงสำหรับ 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