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

รายงานปัญหา ดูแหล่งที่มา

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

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

สรุป

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

  1. ค้นหากฎ android_binary ของแอปที่ต้องการติดตั้ง
  2. ปิดใช้ Proguard โดยนำแอตทริบิวต์ proguard_specs ออก
  3. ตั้งค่าแอตทริบิวต์ multidex เป็น native
  4. ตั้งค่าแอตทริบิวต์ dex_shards เป็น 10
  5. เชื่อมต่ออุปกรณ์ที่ใช้ ART (ไม่ใช่ Dalvik) ผ่าน USB และเปิดใช้การแก้ไขข้อบกพร่อง USB บนอุปกรณ์นั้น
  6. เรียกใช้ bazel mobile-install :your_target การเริ่มต้นแอปจะช้ากว่าปกติเล็กน้อย
  7. แก้ไขโค้ดหรือทรัพยากรของ Android
  8. เรียกใช้ bazel mobile-install --incremental :your_target
  9. ไม่ต้องรอนานอีกต่อไป

ตัวเลือกบรรทัดคำสั่งไปยัง Bazel ที่อาจเป็นประโยชน์มีดังนี้

  • --adb บอก Bazel ว่าจะใช้ไบนารี adb ใด
  • คุณสามารถใช้ --adb_arg เพื่อเพิ่มอาร์กิวเมนต์เพิ่มเติมในบรรทัดคำสั่ง adb ได้ แอปพลิเคชันที่มีประโยชน์อย่างหนึ่งคือการเลือกอุปกรณ์ที่ต้องการติดตั้งในกรณีที่มีอุปกรณ์หลายเครื่องเชื่อมต่อกับเวิร์กสเตชัน bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
  • --start_app จะเริ่มแอปโดยอัตโนมัติ

หากไม่แน่ใจ โปรดดูตัวอย่างหรือติดต่อเรา

เกริ่นนำ

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

ขออภัย เชนเครื่องมือ Android แบบดั้งเดิมสำหรับการสร้าง .apk นั้นประกอบไปด้วยขั้นตอนแบบโมโนลิธตามลำดับจำนวนมากและต้องทำทั้งหมดนี้เพื่อสร้างแอป Android ที่ Google การรอ 5 นาทีเพื่อสร้างการเปลี่ยนแปลงบรรทัดเดียวนั้นไม่ใช่เรื่องแปลกเลยในโครงการขนาดใหญ่อย่าง Google Maps

bazel mobile-install ช่วยให้การพัฒนาแบบซ้ำๆ สำหรับ Android รวดเร็วขึ้นมากโดยใช้ทั้งการตัดส่วนแก้ไข ชาร์ดดิ้งงาน และการปรับแต่งภายในของ Android อย่างชาญฉลาดโดยไม่ต้องเปลี่ยนแปลงโค้ดของแอปเลย

ปัญหาเกี่ยวกับการติดตั้งแอปแบบเดิม

การสร้างแอป Android มีปัญหาบางอย่าง เช่น

  • Dexing โดยค่าเริ่มต้น จะมีการเรียกใช้ "dx" เพียงครั้งเดียวในบิลด์ และไม่ทราบถึงวิธีนำงานจากบิลด์ก่อนหน้ากลับมาใช้ใหม่ โดยจะเรียกใช้ทุกเมธอดอีกครั้ง แม้ว่าจะมีการเปลี่ยนแปลงเพียงเมธอดเดียว

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

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

การดำเนินการของ bazel mobile-install

bazel mobile-installทำการปรับปรุงต่อไปนี้

  • การชาร์ด Dexing หลังจากที่สร้างโค้ด Java ของแอปแล้ว Bazel จะชาร์ดไฟล์คลาสเป็นส่วนที่มีขนาดเท่ากันโดยประมาณและเรียกใช้ dx แยกกัน ไม่มีการเรียกใช้ dx ในชาร์ดที่ไม่มีการเปลี่ยนแปลงตั้งแต่บิลด์ล่าสุด

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

  • กำลังโหลดส่วนต่างๆ ของแอปจากภายนอก .apk ระบบจะใส่แอปพลิเคชันสตับขนาดเล็กลงใน .apk ซึ่งโหลดทรัพยากรของ Android, โค้ด Java และโค้ดแบบเนทีฟจากไดเรกทอรีการติดตั้งบนอุปกรณ์เคลื่อนที่ในอุปกรณ์ จากนั้นโอนการควบคุมไปยังแอปจริง การดำเนินการนี้จะโปร่งใสสำหรับแอป ยกเว้นบางกรณีที่อธิบายไว้ด้านล่าง

การชาร์ดเด็กซ์

การสร้าง Sharded Dexing นั้นตรงไปตรงมาพอสมควร กล่าวคือ เมื่อสร้างไฟล์ .jar แล้ว เครื่องมือจะแยกไฟล์ออกเป็นไฟล์ .jar แยกต่างหากซึ่งมีขนาดเท่ากันโดยประมาณ จากนั้นจะเรียกใช้ dx กับไฟล์ที่มีการเปลี่ยนแปลงตั้งแต่เวอร์ชันก่อนหน้านี้ ตรรกะที่กำหนดว่าชาร์ดใดไปยัง dex ไม่ได้มีแค่ Android เท่านั้น แต่ใช้เพียงอัลกอริทึมสำหรับการตัดทอนการเปลี่ยนแปลงทั่วไปของ Bazel

อัลกอริทึมชาร์ดดิ้งเวอร์ชันแรกจะเรียงลำดับไฟล์ .class ตามลำดับตัวอักษร จากนั้นตัดรายการออกเป็นส่วนๆ ที่มีขนาดเท่ากัน แต่วิธีนี้มีประสิทธิภาพต่ำกว่าเกณฑ์ คือหากมีการเพิ่มหรือนำคลาสออก (รวมถึงคลาสที่ฝังหรือคลาสที่ไม่ระบุตัวตน) จะทำให้คลาสทั้งหมดตามลำดับตัวอักษรหลังจากเลื่อนไป 1 ชั้น ส่งผลให้มีการออกชาร์ดอีกครั้ง ด้วยเหตุนี้ จึงตัดสินใจที่จะชาร์ดแพ็กเกจ Java แทนที่จะเป็นคลาสเดี่ยว แน่นอนว่าวิธีนี้ยังคงส่งผลให้มีการลดชาร์ดจำนวนมากหากมีการเพิ่มหรือนำแพ็กเกจใหม่ออก แต่ก็เกิดขึ้นได้บ่อยน้อยกว่าการเพิ่มหรือนำคลาสเดี่ยวออกมาก

ไฟล์ BUILD ควบคุมจำนวนชาร์ด (โดยใช้แอตทริบิวต์ android_binary.dex_shards) ในอุดมคติ Bazel จะกำหนดจำนวนชาร์ดที่ดีที่สุดโดยอัตโนมัติ แต่ปัจจุบัน Bazel ต้องทราบชุดการดำเนินการ (เช่น คำสั่งที่จะดำเนินการระหว่างการสร้าง) ก่อนที่จะเรียกใช้ชาร์ด ดังนั้นจะไม่สามารถระบุจำนวนชาร์ดที่เหมาะสมได้ เพราะไม่รู้จำนวนคลาส Java ที่จะมีอยู่ในแอปมากกว่า แต่โดยทั่วไปแล้ว ยิ่งมีชาร์ดมากและยิ่งมีชาร์ดทำงานแล้ว ยิ่งมีชาร์ดทำงานและยิ่งมีชาร์ดมาก ตำแหน่งที่ดีที่สุดมักจะอยู่ระหว่าง 10 ถึง 50 ชาร์ด

การโอนไฟล์ที่เพิ่มขึ้น

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

  1. การติดตั้ง .apk (โดยปกติจะใช้ adb install)
  2. การอัปโหลดไฟล์ .dex, ทรัพยากรของ Android และไลบรารีแบบเนทีฟไปยังไดเรกทอรีการติดตั้งบนอุปกรณ์เคลื่อนที่

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

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

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

แอปพลิเคชัน Stub

แอปพลิเคชัน Stub คือแพลตฟอร์มที่ให้คุณโหลด dexe, โค้ดแบบเนทีฟ และทรัพยากรของ Android จากไดเรกทอรี mobile-install ในอุปกรณ์ได้

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

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

ซึ่งทำได้โดยแทนที่คลาส Application ที่ระบุใน AndroidManifest.xml ด้วยแอปพลิเคชันสตับ การดำเนินการนี้จะควบคุมเวลาที่แอปเริ่มทำงานและปรับเปลี่ยนตัวโหลดคลาสและเครื่องมือจัดการทรัพยากรอย่างเหมาะสมโดยเร็วที่สุด (ตัวสร้าง) โดยใช้การสะท้อน Java ในระบบภายในของเฟรมเวิร์ก Android

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

เมื่อทุกอย่างเรียบร้อยแล้ว แอปพลิเคชันสตับจะสร้างอินสแตนซ์ของคลาส Application จริงโดยเปลี่ยนการอ้างอิงถึงตัวแอปเองไปยังการใช้งานจริงภายในเฟรมเวิร์กของ Android

ผลลัพธ์

การแสดง

โดยทั่วไปแล้ว bazel mobile-install ช่วยให้การสร้างและติดตั้งแอปขนาดใหญ่เร็วขึ้น 4 ถึง 10 เท่าหลังจากการเปลี่ยนแปลงเพียงเล็กน้อย

จำนวนต่อไปนี้ได้รับการคำนวณสำหรับผลิตภัณฑ์บางอย่างของ Google:

แน่นอนว่าจะขึ้นอยู่กับลักษณะของการเปลี่ยนแปลง การคอมไพล์อีกครั้งหลังจากที่เปลี่ยน Base Library จะใช้เวลานานขึ้น

ข้อจำกัด

กลเม็ดที่แอปพลิเคชันสตับใช้งานไม่สามารถใช้ได้ในทุกกรณี กรณีต่อไปนี้ไฮไลต์ว่าฟีเจอร์ไม่ทำงานตามที่คาดไว้

  • เมื่อแคสต์ Context ไปยังคลาส Application ใน ContentProvider#onCreate() เราจะเรียกเมธอดนี้ระหว่างการเริ่มต้นแอปพลิเคชันก่อนที่เราจะมีโอกาสแทนที่อินสแตนซ์ของคลาส Application ดังนั้น ContentProvider จะยังคงอ้างอิงการใช้สตับแทนที่จะเป็นของจริง แน่นอนว่านี่ไม่ใช่ข้อบกพร่องเนื่องจากคุณไม่อยากให้ดาวน์เกรด Context แบบนี้ แต่ดูเหมือนว่าปัญหานี้จะเกิดขึ้นกับแอปไม่กี่แอปที่ Google

  • ทรัพยากรที่ติดตั้งโดย bazel mobile-install จะพร้อมใช้งานจากภายในแอปเท่านั้น หากแอปอื่นๆ เข้าถึงทรัพยากรผ่าน PackageManager#getApplicationResources() ทรัพยากรเหล่านี้จะมาจากการติดตั้งที่ไม่เพิ่มขึ้นครั้งล่าสุด

  • อุปกรณ์ที่ไม่ได้ใช้ ART แม้ว่าแอปพลิเคชันสตับจะทำงานได้ดีใน Froyo และหลังจากนั้น Dalvik ก็มีข้อบกพร่องที่ทำให้คิดว่าแอปไม่ถูกต้องหากมีการกระจายโค้ดไปยังไฟล์ .dex หลายไฟล์ในบางกรณี เช่น เมื่อมีการใช้คำอธิบายประกอบของ Java ในวิธีที่เฉพาะเจาะจง ตราบใดที่แอปของคุณไม่ทำให้เกิดข้อบกพร่องเหล่านี้ แอปก็ควรทำงานกับ Dalvik ได้เช่นกัน (โปรดทราบว่าการรองรับ Android เวอร์ชันเก่าไม่เป็นสิ่งที่เรามุ่งเน้น)