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

รายงานปัญหา ดูแหล่งที่มา /3} /4} {3/4} {3/4} {3/4} {3/4} /4.

การพัฒนาซ้ำอย่างรวดเร็วสำหรับ 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 จะเริ่มแอปโดยอัตโนมัติ

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

เกริ่นนำ

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

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

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

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

การสร้างแอป Android มีปัญหาบางอย่าง ได้แก่

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

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

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

แนวทางของ bazel mobile-install

bazel mobile-installได้ปรับปรุงดังต่อไปนี้

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

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

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

Dexing แบบชาร์ด

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

อัลกอริทึมชาร์ดดิ้งเวอร์ชันแรกจะจัดเรียงไฟล์ .class ตามตัวอักษร จากนั้นตัดรายการออกเป็นส่วนๆ ที่มีขนาดเท่าๆ กัน แต่สิ่งนี้พิสูจน์แล้วว่ามีประสิทธิภาพต่ำกว่าที่ควร นั่นคือ หากมีการเพิ่มหรือนำคลาสออก (แม้แต่คลาสที่ซ้อนกันหรือแบบไม่ระบุชื่อ) จะทำให้คลาสทั้งหมดตามตัวอักษรหลังจากนั้นเลื่อนไปตามชาร์ดอีก ดังนั้นจึงมีการตัดสินใจที่จะชาร์ดแพ็กเกจ 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 ของไฟล์ในอุปกรณ์ แต่ก็ถือว่าไม่คุ้มกับการเพิ่มเวลาในการติดตั้ง

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

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

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

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

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

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

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

ผลลัพธ์

การแสดง

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

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

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

ข้อจำกัด

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

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

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

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