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

วันที่ รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

จำนวนชาร์ดจะถูกควบคุมโดยไฟล์ BUILD (โดยใช้ android_binary.dex_shards) ในโลกอุดมคติ Bazel กำหนดจำนวนชาร์ดที่ดีที่สุดโดยอัตโนมัติ แต่ปัจจุบัน Bazel ต้องรู้ ชุดการดำเนินการ (เช่น คำสั่งที่จะดำเนินการระหว่างบิลด์) กำลังดำเนินการทั้งหมด จึงไม่สามารถระบุจำนวนชาร์ดที่เหมาะสมได้ เนื่องจากโมเดลจะไม่รู้ว่า ท้ายที่สุดแล้วจะมีคลาส Java จำนวนกี่คลาส แอป โดยทั่วไป ยิ่งชาร์ดมากเท่าไหร่ การสร้างและ แต่การเริ่มต้นแอปที่ช้าลงกว่าเดิมจะกลายเป็นเพราะ Linker ต้องทำงานมากขึ้น จุดที่ดีมากมักอยู่ระหว่าง 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ด้วย การใช้สตับ ช่วงเวลานี้ ควบคุมเวลาที่แอปเริ่มทำงาน และปรับแต่งตัวโหลดคลาสและ ผู้จัดการทรัพยากรอย่างเหมาะสมได้ในทันที (ตัวสร้าง) โดยใช้ การสะท้อนของ 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 เวอร์ชันเก่านั้น โฟกัส)