การพัฒนาซ้ำอย่างรวดเร็วสำหรับ Android
หน้านี้อธิบายวิธีที่ bazel mobile-install
ทำให้การพัฒนาแบบวนซ้ำ
สำหรับ Android ได้รวดเร็วยิ่งขึ้น โดยอธิบายถึงประโยชน์ของแนวทางนี้เทียบกับ
กับความท้าทายของวิธีการติดตั้งแอปแบบดั้งเดิม
สรุป
หากต้องการติดตั้งการเปลี่ยนแปลงเล็กๆ น้อยๆ ในแอป Android อย่างรวดเร็ว ให้ทำดังนี้
- ค้นหากฎ
android_binary
ของแอปที่ต้องการติดตั้ง - ปิดใช้ Proguard โดยนำแอตทริบิวต์
proguard_specs
ออก - ตั้งค่าแอตทริบิวต์
multidex
เป็นnative
- ตั้งค่าแอตทริบิวต์
dex_shards
เป็น10
- เชื่อมต่ออุปกรณ์ที่ใช้ ART (ไม่ใช่ Dalvik) ผ่าน USB และเปิดใช้ USB การแก้ไขข้อบกพร่อง
- เรียกใช้
bazel mobile-install :your_target
การเริ่มต้นแอปจะเป็นไปเล็กน้อย ช้ากว่าปกติ - แก้ไขโค้ดหรือทรัพยากร Android
- เรียกใช้
bazel mobile-install --incremental :your_target
- คุณไม่ต้องรอนาน
ตัวเลือกบรรทัดคำสั่งบางอย่างสำหรับ 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 ชาร์ด
การโอนไฟล์ส่วนเพิ่ม
หลังจากสร้างแอปแล้ว ขั้นตอนต่อไปคือการติดตั้ง ความพยายามน้อยที่สุดเท่าที่จะเป็นไปได้ การติดตั้งมีขั้นตอนดังนี้
- การติดตั้ง .apk (โดยปกติจะใช้
adb install
) - การอัปโหลดไฟล์ .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 เวอร์ชันเก่านั้น โฟกัส)