ติดตั้ง bazel ผ่านมือถือ

การพัฒนาแบบวนซ้ำอย่างรวดเร็วสำหรับ Android

หน้านี้จะอธิบายวิธีที่ bazel mobile-install ช่วยให้การพัฒนาแบบวนซ้ำสำหรับ Android เร็วขึ้นมาก โดยจะอธิบายถึงข้อดีของแนวทางนี้เมื่อเทียบกับข้อเสียของการสร้างและการติดตั้งแยกกัน

สรุป

หากต้องการติดตั้งการเปลี่ยนแปลงเล็กๆ น้อยๆ ในแอป Android อย่างรวดเร็ว ให้ทำดังนี้

  1. ค้นหากฎ android_binary ของแอปที่ต้องการติดตั้ง
  2. เชื่อมต่ออุปกรณ์กับ adb
  3. วิ่ง bazel mobile-install :your_target การเริ่มต้นแอปจะช้ากว่าปกติเล็กน้อย
  4. แก้ไขโค้ดหรือทรัพยากร Android
  5. วิ่ง bazel mobile-install :your_target
  6. เพลิดเพลินกับการติดตั้งเพิ่มเติมที่รวดเร็วและน้อยที่สุด

ตัวเลือกบรรทัดคำสั่งบางอย่างสำหรับ 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