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