การแสดงผลช้า

การแสดงผล UI คือการสร้างเฟรมจากแอปและแสดงเฟรมนั้นๆ หน้าจอ เพื่อให้มั่นใจว่าการโต้ตอบของผู้ใช้กับแอปจะเป็นไปอย่างราบรื่น แอปของคุณต้องแสดงผลเฟรมที่น้อยกว่า 16 มิลลิวินาทีเพื่อให้ได้ 60 เฟรมต่อวินาที (FPS) หากต้องการทำความเข้าใจถึงเหตุผลที่ควรใช้ 60 FPS โปรดดูรูปแบบประสิทธิภาพของ Android ทำไมต้องใช้ 60 FPS หากคุณกำลังพยายามที่จะ 90 fps หน้าต่างนี้จะลดลงเหลือ 11 มิลลิวินาที และสําหรับ 120 เฟรมต่อวินาที 8 มิลลิวินาที

หากคุณใช้หน้าต่างนี้เกิน 1 มิลลิวินาที ไม่ได้หมายความว่าเฟรมนั้นจะแสดง ล่าช้า 1 มิลลิวินาที แต่เหลือเวลา Choreographer วางเฟรมทั้งหมด หากแอปได้รับผลกระทบจากการแสดงผล UI ที่ช้า ระบบถูกบังคับให้ข้ามเฟรม และผู้ใช้รับรู้ถึงการกระตุกในแอปของคุณ วิธีนี้เรียกว่า jank หน้านี้แสดงวิธีวินิจฉัยและแก้ไขการกระตุก

หากคุณกำลังพัฒนาเกมที่ไม่ได้ใช้ View จากนั้นให้คุณข้าม Choreographer ในกรณีนี้คือ Frame Pacing ความช่วยเหลือเกี่ยวกับคลัง OpenGL และ เกม Vulkan สามารถแสดงผลได้อย่างราบรื่นและ ความเร็วของเฟรมใน Android ถูกต้อง

Android จะตรวจสอบแอปเพื่อหาการกระตุกโดยอัตโนมัติเพื่อช่วยปรับปรุงคุณภาพแอป และแสดงข้อมูลในหน้าแดชบอร์ดของ Android Vitals สำหรับข้อมูล เกี่ยวกับวิธีการรวบรวมข้อมูล โปรดดูตรวจสอบคุณภาพทางเทคนิคของแอปด้วย แอนดรอยด์ Vitals

ระบุการกระตุก

การค้นหาโค้ดในแอปที่ทำให้เกิดการกระตุกอาจเป็นเรื่องยาก ส่วนนี้ อธิบายวิธีระบุการกระตุก 3 วิธี ดังนี้

การตรวจสอบด้วยภาพช่วยให้คุณตรวจสอบกรณีการใช้งานทั้งหมดในแอปได้ใน 2-3 ที่ นาที แต่ไม่ได้ให้รายละเอียดมากเท่ากับ Systrace Systrace จะระบุ รายละเอียดเพิ่มเติม แต่ถ้าคุณเรียกใช้ Systrace สำหรับ Use Case ทั้งหมดในแอป คุณสามารถ ด้วยข้อมูลมากมายที่วิเคราะห์ได้ยาก ทั้งภาพ check และ Systrace ตรวจพบการกระตุกในอุปกรณ์ หากทำซ้ำไม่ได้ ความยุ่งยากในอุปกรณ์ภายใน คุณสามารถสร้างการตรวจสอบประสิทธิภาพที่กำหนดเองเพื่อวัดผล ส่วนใดส่วนหนึ่งของแอปในอุปกรณ์ที่ทำงานภาคสนามได้

การตรวจสอบการมองเห็น

การตรวจสอบด้วยภาพจะช่วยคุณระบุกรณีการใช้งานที่ก่อให้เกิดการกระตุก ถึง ดำเนินการตรวจสอบด้วยภาพ เปิดแอปของคุณ และดำเนินการตามขั้นตอน และมองหาการกระตุกใน UI ของคุณ

เคล็ดลับบางส่วนสำหรับการตรวจสอบด้วยภาพมีดังนี้

  • เรียกใช้แอปรุ่น (หรือเวอร์ชันที่แก้ไขข้อบกพร่องไม่ได้) เป็นอย่างน้อย ศิลปะ รันไทม์จะปิดใช้การเพิ่มประสิทธิภาพที่สำคัญหลายอย่างเพื่อรองรับการแก้ไขข้อบกพร่อง เพื่อให้แน่ใจว่าคุณกำลังดูสิ่งที่คล้ายกับสิ่งที่ผู้ใช้ เห็น
  • เปิดใช้ GPU โปรไฟล์ การแสดงภาพ การแสดงผล GPU ของโปรไฟล์จะแสดงแถบบนหน้าจอที่ช่วยให้คุณเห็นภาพ ระยะเวลาที่ใช้ในการแสดงผลเฟรมของหน้าต่าง UI ซึ่งสัมพันธ์กับการเปรียบเทียบ 16 มิลลิวินาทีต่อเฟรม แต่ละแท่งมีส่วนประกอบที่เป็นสี นั้นแมปกับขั้นตอนในไปป์ไลน์การแสดงผล เพื่อให้คุณดูได้ว่าส่วนใด ใช้เวลานานที่สุด เช่น หากเฟรมใช้เวลานาน วิธีจัดการอินพุต ดูโค้ดของแอปที่จัดการข้อมูลจากผู้ใช้
  • เรียกใช้ผ่านคอมโพเนนต์ที่เป็นแหล่งที่มาทั่วไปของความไม่สม่ำเสมอ เช่น ด้วย RecyclerView
  • เริ่มต้นแอปพลิเคชันจาก Cold เริ่มต้น
  • เรียกใช้แอปในอุปกรณ์ที่ช้ากว่าเพื่อทำให้ปัญหาร้ายแรงยิ่งขึ้น

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

ซิสเตรซ

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

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

วันที่ ตัวอย่าง Systrace
รูปที่ 1 ตัวอย่าง Systrace

ตัวอย่าง Systrace ในรูปที่ 1 มีข้อมูลต่อไปนี้ การระบุการกระตุก:

  1. Systrace จะแสดงเวลาที่วาดแต่ละเฟรมและแสดงรหัสสีให้แต่ละเฟรม ไฮไลต์เวลาในการแสดงผลช้า ซึ่งจะช่วยให้คุณพบเฟรมที่มีคุณภาพต่ำมากขึ้น ที่แม่นยำกว่าการตรวจสอบด้วยภาพ สำหรับข้อมูลเพิ่มเติม โปรดดู ตรวจสอบ UI เฟรมและการแจ้งเตือน
  2. Systrace จะตรวจหาปัญหาในแอปและแสดงการแจ้งเตือนทั้งใน แต่ละเฟรม การแจ้งเตือน แผง ขอแนะนำให้ทำตามคำแนะนำในการแจ้งเตือน
  3. ส่วนต่างๆ ในเฟรมเวิร์กและไลบรารีของ Android เช่น RecyclerView มีเครื่องหมายการติดตาม ดังนั้น ไทม์ไลน์ systrace จะแสดงเมื่อมีการเรียกใช้เมธอดเหล่านั้นบน UI และระยะเวลาในการดำเนินการ

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

หาก Systrace ไม่แสดงรายละเอียดเกี่ยวกับสาเหตุที่การทำงานของเทรด UI ใช้เวลานาน แล้วใช้ CPU ของ Android เครื่องมือสร้างโปรไฟล์เพื่อบันทึก การติดตามเมธอดแบบสุ่มตัวอย่างหรือมีการวัดคุม โดยทั่วไปแล้ว การติดตามเมธอดไม่เหมาะกับประเภท การระบุความยุ่งยากเพราะสร้างความแตกต่างที่ผิดพลาดเนื่องจากปริมาณที่มากเกินไป จากด้านบน และจะไม่เห็นว่าชุดข้อความกำลังทำงานอยู่หรือถูกบล็อก แต่ การติดตามเมธอดสามารถช่วยคุณระบุเมธอดในแอปของคุณที่ใช้ เป็นส่วนใหญ่ หลังจากระบุวิธีการเหล่านี้แล้ว ให้เพิ่ม Trace เครื่องหมายและเรียกใช้ Systrace อีกครั้งเพื่อดู วิธีการเหล่านี้ทำให้เกิดการกระตุกหรือไม่

สำหรับข้อมูลเพิ่มเติม โปรดดู ทำความเข้าใจ Systrace

การตรวจสอบประสิทธิภาพที่กำหนดเอง

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

วิธีการคือรวบรวมเวลาในการแสดงผลเฟรมจากส่วนใดส่วนหนึ่งของแอปด้วย FrameMetricsAggregator แล้วบันทึกและวิเคราะห์ข้อมูลโดยใช้ประสิทธิภาพ Firebase การตรวจสอบ

โปรดดูข้อมูลเพิ่มเติมที่หัวข้อเริ่มต้นใช้งานการตรวจสอบประสิทธิภาพสำหรับ Android

เฟรมที่หยุดทำงาน

เฟรมที่ค้างคือเฟรม UI ที่ใช้เวลาในการแสดงผลนานกว่า 700 มิลลิวินาที นี่คือ เพราะดูเหมือนว่าแอปของคุณค้างและไม่ตอบสนองข้อมูลจากผู้ใช้ เป็นเวลาเกือบ 1 วินาทีระหว่างที่เฟรมกำลังแสดงผล เราขอแนะนำให้เพิ่มประสิทธิภาพ แอปเพื่อแสดงผลเฟรมภายใน 16 มิลลิวินาทีเพื่อดูแลให้ UI ราบรื่น แต่ระหว่างใช้แอป เริ่มต้นหรือขณะที่เปลี่ยนไปใช้หน้าจออื่น เป็นเรื่องปกติที่ เฟรมเริ่มต้นที่จะใช้เวลาในการวาดนานกว่า 16 มิลลิวินาที เนื่องจากแอปต้องสูงเกินจริง มุมมอง จัดวางหน้าจอ และเริ่มต้นวาดใหม่ทั้งหมด จึงเป็นเหตุผลที่ Android ติดตามเฟรมที่ค้างแยกจากการแสดงผลช้า ยังไม่ได้ติดต่อ เฟรมในแอปควรใช้เวลานานกว่า 700 มิลลิวินาทีในการแสดงผล

เพื่อช่วยคุณปรับปรุงคุณภาพแอป Android จะตรวจสอบแอปของคุณโดยอัตโนมัติเพื่อหา เฟรมที่ค้างและแสดงข้อมูลในแดชบอร์ด Android Vitals สำหรับข้อมูลเกี่ยวกับการรวบรวมข้อมูล โปรดดูที่ตรวจสอบด้านเทคนิคของแอป คุณภาพด้วย Android Vitals

เฟรมที่ค้างคือรูปแบบของการแสดงผลช้าอย่างมาก ดังนั้น ในการวินิจฉัยและแก้ไขปัญหานั้นเหมือนกัน

การติดตามการกระตุก

ไทม์ไลน์ของเฟรม ใน Perfetto สามารถช่วยในการติดตามได้ช้าหรือ เฟรมที่ค้าง

ความสัมพันธ์ระหว่างเฟรมที่ช้า เฟรมที่ค้าง และ ANR

เฟรมที่ช้า เฟรมที่ค้าง และ ANR ล้วนเป็นข้อขัดข้องที่ อาจพบเจอ โปรดดูตารางด้านล่างเพื่อทําความเข้าใจความแตกต่าง

เฟรมที่ช้า เฟรมที่หยุดทำงาน ANR
เวลาในการแสดงผล ระหว่าง 16 ถึง 700 มิลลิวินาที ระหว่าง 700 ถึง 5 วินาที มากกว่า 5 วินาที
บริเวณที่เกี่ยวข้องกับผู้ใช้ที่มองเห็นได้
  • การเลื่อน RecyclerView ทำงานกะทันหัน
  • บนหน้าจอที่มีภาพเคลื่อนไหวที่ซับซ้อนซึ่งเคลื่อนไหวได้ไม่ถูกต้อง
  • ระหว่างเริ่มต้นแอป
  • การย้ายจากหน้าจอหนึ่งไปยังหน้าจออื่น เช่น หน้าจอ กำลังเปลี่ยน
  • ขณะมีกิจกรรมที่ทำงานอยู่เบื้องหน้า แอปไม่ตอบสนอง ไปยังเหตุการณ์อินพุตหรือ BroadcastReceiver เช่น การกดแป้น หรือ เหตุการณ์การแตะหน้าจอ 5 วินาที
  • ขณะที่คุณไม่มีกิจกรรมอยู่เบื้องหน้า BroadcastReceiver ยังดำเนินการภายใน ค่อนข้างนาน

ติดตามเฟรมที่ช้าและเฟรมที่ค้างแยกกัน

ระหว่างที่แอปเริ่มทำงานหรือขณะเปลี่ยนไปใช้หน้าจออื่น เป็นเรื่องปกติ เฟรมเริ่มต้นที่จะใช้เวลาในการวาดนานกว่า 16 มิลลิวินาที เนื่องจากแอปต้อง ขยายมุมมอง จัดพื้นที่หน้าจอ และเริ่มวาดรูปแรกตั้งแต่ต้น

แนวทางปฏิบัติแนะนำในการจัดลำดับความสำคัญและแก้ไขปัญหาการกระตุก

โปรดคํานึงถึงแนวทางปฏิบัติแนะนําต่อไปนี้เมื่อต้องการแก้ไขการกระตุกใน แอป:

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

กำลังแก้ไขการกระตุก

ถ้าจะแก้ไขการกระตุก ให้ตรวจสอบว่าเฟรมใดทำงานไม่เสร็จใน 16 มิลลิวินาที และดูว่า ผิด ตรวจสอบว่า Record View#draw หรือ Layout ใช้เวลานานผิดปกติหรือไม่ เฟรมบางส่วน โปรดดูแหล่งที่มาทั่วไปของความไม่สม่ำเสมอสำหรับปัญหาเหล่านี้ และ อื่นๆ

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

หากคุณมี UI หลักที่ซับซ้อนและสำคัญ เช่น อินเทอร์เฟซหลัก รายการแบบเลื่อน - ลองใช้การใช้เครื่องมือในการเขียน การทดสอบ ที่สามารถทดสอบ ตรวจจับเวลาในการแสดงผลช้าและทำการทดสอบบ่อยๆ เพื่อป้องกันการเกิดปัญหาซ้ำ

แหล่งที่มาของการกระตุกที่พบบ่อย

ส่วนต่อไปนี้จะอธิบายสาเหตุทั่วไปของการกระตุกในแอปโดยใช้ View ที่เป็นระบบและแนวทางปฏิบัติแนะนำในการจัดการกับปัญหา สำหรับข้อมูลเกี่ยวกับการแก้ไข ปัญหาเกี่ยวกับประสิทธิภาพของ Jetpack Compose โปรดดู Jetpack ประสิทธิภาพของการเขียน

รายการแบบเลื่อนได้

ListView และที่สำคัญ RecyclerView - มักใช้สำหรับรายการแบบเลื่อนที่ซับซ้อนซึ่งส่วนใหญ่ มีแนวโน้มที่จะกระตุกได้ ทั้งคู่มีเครื่องหมาย Systrace คุณจึงสามารถใช้ Systrace ได้ เพื่อดูว่าส่วนขยายส่งผลต่อการกระตุกในแอปของคุณหรือไม่ ส่งบรรทัดคำสั่ง อาร์กิวเมนต์ -a <your-package-name> เพื่อรับส่วนการติดตามใน RecyclerView รวมถึงส่วนการติดตาม เครื่องหมายการติดตามที่คุณเพิ่มไว้จะปรากฏขึ้น ทำตามคำแนะนำของ ที่สร้างขึ้นในเอาต์พุต Systrace ภายใน Systrace คุณสามารถคลิก ส่วนที่ติดตาม RecyclerView เพื่อดูคำอธิบายงาน RecyclerView กำลังทำอะไรอยู่

RecyclerView: AlertDataSetChanged()

หากคุณเห็นรายการทั้งหมดใน RecyclerView ได้รับการย้อนกลับ และมีการวางตำแหน่งอีกครั้ง และวาดใหม่ในเฟรมเดียว ดูให้แน่ใจว่าคุณไม่ได้เรียกใช้ notifyDataSetChanged(), setAdapter(Adapter), หรือ swapAdapter(Adapter, boolean) สำหรับการอัปเดตเล็กๆ น้อยๆ วิธีการเหล่านี้จะส่งสัญญาณว่า มีการเปลี่ยนแปลงกับ แสดงเนื้อหาและแสดงใน Systrace เป็น RV Fullต่อยอด ให้ใช้ SortedList หรือ DiffUtil เพื่อสร้าง มีการอัปเดตเพียงเล็กน้อยเมื่อมีการเปลี่ยนแปลงหรือเพิ่มเนื้อหา

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

Kotlin

fun onNewDataArrived(news: List<News>) {
    myAdapter.news = news
    myAdapter.notifyDataSetChanged()
}

Java

void onNewDataArrived(List<News> news) {
    myAdapter.setNews(news);
    myAdapter.notifyDataSetChanged();
}

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

เราขอแนะนำให้คุณใช้ DiffUtil ซึ่งคำนวณและส่งการอัปเดตเพียงเล็กน้อย สำหรับคุณ:

Kotlin

fun onNewDataArrived(news: List<News>) {
    val oldNews = myAdapter.items
    val result = DiffUtil.calculateDiff(MyCallback(oldNews, news))
    myAdapter.news = news
    result.dispatchUpdatesTo(myAdapter)
}

Java

void onNewDataArrived(List<News> news) {
    List<News> oldNews = myAdapter.getItems();
    DiffResult result = DiffUtil.calculateDiff(new MyCallback(oldNews, news));
    myAdapter.setNews(news);
    result.dispatchUpdatesTo(myAdapter);
}

หากต้องการแจ้ง DiffUtil เกี่ยวกับวิธีตรวจสอบรายการ ให้กำหนด MyCallback เป็น Callback การใช้งานของคุณ

RecyclerView: RecyclerView แบบซ้อน

เป็นเรื่องปกติที่จะฝัง RecyclerView หลายอินสแตนซ์ โดยเฉพาะที่มี รายการแนวตั้งของรายการที่เลื่อนในแนวนอน ตัวอย่างนี้คือตารางกริด ในหน้าหลักของ Play Store วิธีนี้อาจได้ผลดี แต่ก็ต้อง มุมมองที่ย้ายไปมา

หากคุณเห็นเนื้อหาด้านในจำนวนมากพองออกเมื่อคุณเลื่อนหน้าเว็บลงมาเป็นครั้งแรก คุณก็ควรตรวจสอบว่ากำลังแชร์ RecyclerView.RecycledViewPool ระหว่างอินสแตนซ์ด้านใน (แนวนอน) ของ RecyclerView โดยค่าเริ่มต้น แต่ละ RecyclerView สร้างกลุ่มรายการของตัวเอง แต่ในกรณีของสิบครั้ง itemViews บนหน้าจอในเวลาเดียวกัน จะเกิดปัญหาเมื่อ itemViews เข้าถึงไม่ได้ รายการแนวนอนที่ต่างกัน หากทุกแถวแสดงคล้ายกัน ประเภทต่างๆ

Kotlin

class OuterAdapter : RecyclerView.Adapter<OuterAdapter.ViewHolder>() {

    ...

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        // Inflate inner item, find innerRecyclerView by ID.
        val innerLLM = LinearLayoutManager(parent.context, LinearLayoutManager.HORIZONTAL, false)
        innerRv.apply {
            layoutManager = innerLLM
            recycledViewPool = sharedPool
        }
        return OuterAdapter.ViewHolder(innerRv)
    }
    ...

Java

class OuterAdapter extends RecyclerView.Adapter<OuterAdapter.ViewHolder> {
    RecyclerView.RecycledViewPool sharedPool = new RecyclerView.RecycledViewPool();

    ...

    @Override
    public void onCreateViewHolder(ViewGroup parent, int viewType) {
        // Inflate inner item, find innerRecyclerView by ID.
        LinearLayoutManager innerLLM = new LinearLayoutManager(parent.getContext(),
                LinearLayoutManager.HORIZONTAL);
        innerRv.setLayoutManager(innerLLM);
        innerRv.setRecycledViewPool(sharedPool);
        return new OuterAdapter.ViewHolder(innerRv);

    }
    ...

หากต้องการเพิ่มประสิทธิภาพเพิ่มเติม คุณยังเรียกใช้ setInitialPrefetchItemCount(int) ใน LinearLayoutManager ภายใน RecyclerView ตัวอย่างเช่น หากคุณแสดง 3.5 รายการอยู่เสมอ เรียก innerLLM.setInitialItemPrefetchCount(4) ติดต่อกัน ซึ่งจะส่งสัญญาณถึง RecyclerViewว่าเมื่อแถวแนวนอนกำลังจะแสดงให้เห็นหน้าจอ แถวแนวนอน พยายามดึงข้อมูลรายการที่อยู่ข้างในล่วงหน้าหากมีเวลาว่างในเธรด UI

RecyclerView: เงินเฟ้อมากเกินไปหรือการสร้างใช้เวลานานเกินไป

ในกรณีส่วนใหญ่ ฟีเจอร์การดึงข้อมูลล่วงหน้าใน RecyclerView สามารถช่วยแก้ไข จากเงินเฟ้อด้วยการดำเนินการล่วงหน้าขณะที่เทรด UI ไม่มีการใช้งาน หากคุณเห็นเงินเฟ้อขณะอยู่ในเฟรมและไม่ได้อยู่ในส่วนที่มีป้ายกำกับว่า RV ดึงข้อมูลล่วงหน้า อย่าลืมทดสอบในอุปกรณ์ที่รองรับและใช้ เวอร์ชันไลบรารีการสนับสนุน การดึงข้อมูลล่วงหน้าทำได้เฉพาะใน Android 5.0 API ระดับ 21 ขึ้นไปเท่านั้น

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

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

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

RecyclerView: การเชื่อมโยงใช้เวลานานเกินไป

การเชื่อมโยง ซึ่งก็คือ onBindViewHolder(VH, int) จะต้องตรงไปตรงมาและใช้เวลาน้อยกว่า 1 มิลลิวินาทีอย่างมาก ยกเว้นรายการที่ซับซ้อนที่สุด ต้องใช้ออบเจ็กต์ Java (POJO) แบบเก่าธรรมดา รายการต่างๆ จากข้อมูลรายการภายในของอะแดปเตอร์และตัวตั้งค่าการเรียกในมุมมองใน ViewHolder หาก RV OnBindView ใช้เวลานาน ให้ตรวจสอบว่าคุณ ทำงานน้อยที่สุดในโค้ดการเชื่อมโยง

หากคุณใช้ออบเจ็กต์ POJO พื้นฐานเพื่อเก็บข้อมูลในอะแดปเตอร์ คุณจะทําสิ่งต่อไปนี้ได้ หลีกเลี่ยงการเขียนโค้ดการเชื่อมโยงใน onBindViewHolder อย่างสิ้นเชิงด้วยการใช้ ไลบรารีการเชื่อมโยงข้อมูล

RecyclerView หรือ ListView: การออกแบบหรือการวาดใช้เวลานานเกินไป

สำหรับปัญหาเกี่ยวกับการวาดและเลย์เอาต์ โปรดดูประสิทธิภาพของเลย์เอาต์และ ประสิทธิภาพการแสดงผล

มุมมองรายการ: เงินเฟ้อ

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

Kotlin

fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
    return (convertView ?: layoutInflater.inflate(R.layout.my_layout, parent, false)).apply {
        // Bind content from position to convertView.
    }
}

Java

View getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {
        // Only inflate if no convertView passed.
        convertView = layoutInflater.inflate(R.layout.my_layout, parent, false)
    }
    // Bind content from position to convertView.
    return convertView;
}

ประสิทธิภาพของเลย์เอาต์

หาก Systrace แสดงว่าส่วนเลย์เอาต์ของ Choreographer#doFrame ทำงานมากเกินไปหรือบ่อยเกินไป ซึ่งนั่นหมายความว่าคุณกำลังเลือกใช้งาน ปัญหาด้านประสิทธิภาพ ประสิทธิภาพเลย์เอาต์ของแอปขึ้นอยู่กับส่วนใด ของลำดับชั้นการแสดงผลได้เปลี่ยนพารามิเตอร์ของเลย์เอาต์หรืออินพุต

ประสิทธิภาพของเลย์เอาต์: ต้นทุน

หากกลุ่มมีความยาวมากกว่า 2-3 มิลลิวินาที เป็นไปได้ว่า ประสิทธิภาพการซ้อนในกรณีที่แย่ที่สุดสำหรับ RelativeLayouts หรือ weighted-LinearLayouts แต่ละ เลย์เอาต์เหล่านี้จะทริกเกอร์การวัดผลและเลย์เอาต์แบบต่างๆ ของบุตรหลานได้ ดังนั้น การซ้อนกันอาจทำให้เกิดลักษณะการทำงาน O(n^2) ในระดับความลึกของการซ้อน

พยายามหลีกเลี่ยง RelativeLayout หรือคุณลักษณะน้ำหนักของ LinearLayout ในทั้งหมดยกเว้น โหนดใบไม้ที่ต่ำที่สุดในลำดับชั้น ซึ่งทำได้หลายวิธี ดังนี้

ประสิทธิภาพของการออกแบบ: ความถี่

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

โดยทั่วไป ภาพเคลื่อนไหวต้องทำงานโดยใช้คุณสมบัติการวาดของ View เช่น ดังต่อไปนี้:

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

ประสิทธิภาพการแสดงผล

UI ของ Android แบ่งเป็น 2 ระยะ ดังนี้

  • Record View#draw ในเธรด UI ซึ่งทำงาน draw(Canvas) ในทุก มุมมองที่ไม่ถูกต้อง และสามารถเรียกใช้การเรียกไปยังมุมมองที่กำหนดเองหรือเข้าสู่โค้ดของคุณ
  • DrawFrame ใน RenderThread ซึ่งทำงานบน RenderThread ของระบบ แต่จะทำงานตามงานที่สร้างขึ้นในระยะ Record View#draw

ประสิทธิภาพการแสดงผล: ชุดข้อความ UI

หาก Record View#draw ใช้เวลานาน ถือเป็นเรื่องปกติที่บิตแมป บนชุดข้อความ UI การวาดภาพเป็นบิตแมปจะใช้การแสดงผล CPU ให้หลีกเลี่ยงเหตุการณ์นี้เมื่อทำได้ คุณสามารถใช้การติดตามเมธอดด้วย Android เครื่องมือสร้างโปรไฟล์ CPU เพื่อดูว่าใช่ ปัญหาที่เกิดขึ้น

การลงสีเป็นบิตแมปมักจะทำเมื่อแอปต้องการตกแต่งบิตแมปก่อน สำหรับการแสดงรูปภาพ บางครั้งก็จะเป็นการตกแต่ง เช่น การใส่มุมโค้งมน

Kotlin

val paint = Paint().apply {
    isAntiAlias = true
}
Canvas(roundedOutputBitmap).apply {
    // Draw a round rect to define the shape:
    drawRoundRect(
            0f,
            0f,
            roundedOutputBitmap.width.toFloat(),
            roundedOutputBitmap.height.toFloat(),
            20f,
            20f,
            paint
    )
    paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)
    // Multiply content on top to make it rounded.
    drawBitmap(sourceBitmap, 0f, 0f, paint)
    setBitmap(null)
    // Now roundedOutputBitmap has sourceBitmap inside, but as a circle.
}

Java

Canvas bitmapCanvas = new Canvas(roundedOutputBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
// Draw a round rect to define the shape:
bitmapCanvas.drawRoundRect(0, 0,
        roundedOutputBitmap.getWidth(), roundedOutputBitmap.getHeight(), 20, 20, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
// Multiply content on top to make it rounded.
bitmapCanvas.drawBitmap(sourceBitmap, 0, 0, paint);
bitmapCanvas.setBitmap(null);
// Now roundedOutputBitmap has sourceBitmap inside, but as a circle.

หากนี่เป็นงานประเภทที่คุณกำลังทำบนเธรด UI คุณสามารถทำ ในเธรดการถอดรหัสในเบื้องหลัง ในบางกรณี เช่น ตัวอย่างเช่น คุณสามารถทำงาน ในช่วงเวลาที่ได้ด้วย ดังนั้นถ้า Drawable หรือ โค้ด View มีลักษณะดังนี้

Kotlin

fun setBitmap(bitmap: Bitmap) {
    mBitmap = bitmap
    invalidate()
}

override fun onDraw(canvas: Canvas) {
    canvas.drawBitmap(mBitmap, null, paint)
}

Java

void setBitmap(Bitmap bitmap) {
    mBitmap = bitmap;
    invalidate();
}

void onDraw(Canvas canvas) {
    canvas.drawBitmap(mBitmap, null, paint);
}

โดยใช้คำสั่งต่อไปนี้แทน

Kotlin

fun setBitmap(bitmap: Bitmap) {
    shaderPaint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
    invalidate()
}

override fun onDraw(canvas: Canvas) {
    canvas.drawRoundRect(0f, 0f, width, height, 20f, 20f, shaderPaint)
}

Java

void setBitmap(Bitmap bitmap) {
    shaderPaint.setShader(
            new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP));
    invalidate();
}

void onDraw(Canvas canvas) {
    canvas.drawRoundRect(0, 0, width, height, 20, 20, shaderPaint);
}

คุณยังตั้งค่านี้สำหรับการป้องกันพื้นหลังได้ด้วย เช่น เมื่อวาดการไล่ระดับสี ที่ด้านบนของบิตแมป และการกรองรูปภาพด้วย ColorMatrixColorFilter - 2 การดำเนินการอื่นๆ ทั่วไปที่กระทำ การแก้ไขบิตแมป

หากคุณกำลังวาดไปยังบิตแมปด้วยเหตุผลอื่น อาจเป็นไปได้ว่าการใช้บิตแมปเป็น แคช - พยายามวาดไปยัง Canvas ที่เร่งการแสดงผลด้วยฮาร์ดแวร์ที่ส่งไปยัง View หรือ Drawableโดยตรง หากจำเป็น ขอแนะนำให้โทร setLayerType() กับ LAYER_TYPE_HARDWARE เพื่อแคชเอาต์พุตการแสดงผลที่ซับซ้อนและยังคงใช้ประโยชน์จากการแสดงภาพของ GPU

ประสิทธิภาพการแสดงผล: RenderThread

การดำเนินการ Canvas บางรายการมีราคาถูก แต่ทริกเกอร์การประมวลผลที่มีราคาแพง ในวันที่ RenderThread โดยทั่วไปแล้ว Systrace จะแจ้งเตือนให้ทราบ

การสร้างภาพเคลื่อนไหวเส้นทางขนาดใหญ่

วันและเวลา มีการเรียกใช้ Canvas.drawPath() เมื่อผ่าน Canvas ที่เร่งการแสดงผลด้วยฮาร์ดแวร์ ถึงวันที่ View Android จะวาดเส้นทางเหล่านี้บน CPU ก่อนแล้วอัปโหลดไปยัง GPU หากคุณมีเส้นทางขนาดใหญ่ ให้หลีกเลี่ยงการแก้ไขจากเฟรมหนึ่งไปยังอีกเฟรม เพื่อให้ สามารถแคชและวาดได้อย่างมีประสิทธิภาพ drawPoints() drawLines() และ drawRect/Circle/Oval/RoundRect() มีประสิทธิภาพมากกว่าและ ใช้งานได้ดียิ่งขึ้น แม้ว่าคุณจะใช้การเรียกโฆษณามากกว่าก็ตาม

Canvas.clipPath

clipPath(Path) ทำให้เกิดพฤติกรรมการตัดค่าใช้จ่ายที่มีราคาสูง และโดยทั่วไปแล้วต้องหลีกเลี่ยง วันและเวลา หากเป็นไปได้ ให้เลือกใช้การวาดรูปร่างแทนการตัดให้เป็นรูปสี่เหลี่ยมผืนผ้า ทั้งนี้ ทำงานได้ดีกว่าและรองรับการลบรอยหยัก ตัวอย่างเช่น URL ต่อไปนี้ การเรียก clipPath อาจแสดงต่างออกไป ดังนี้

Kotlin

canvas.apply {
    save()
    clipPath(circlePath)
    drawBitmap(bitmap, 0f, 0f, paint)
    restore()
}

Java

canvas.save();
canvas.clipPath(circlePath);
canvas.drawBitmap(bitmap, 0f, 0f, paint);
canvas.restore();

แต่ให้แสดงตัวอย่างก่อนหน้านี้ดังนี้

Kotlin

paint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
// At draw time:
canvas.drawPath(circlePath, mPaint)

Java

// One time init:
paint.setShader(new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP));
// At draw time:
canvas.drawPath(circlePath, mPaint);
การอัปโหลดบิตแมป

Android แสดงบิตแมปเป็นพื้นผิว OpenGL และครั้งแรกที่บิตแมปคือ แสดงในเฟรมแล้วจึงอัปโหลดไปยัง GPU คุณสามารถดูข้อมูลนี้ใน Systrace เป็น การอัปโหลดพื้นผิว(รหัส) กว้าง x สูง ซึ่งอาจใช้เวลาหลายมิลลิวินาที แสดงในรูปที่ 2 แต่ต้องแสดงภาพด้วย GPU

หากใช้เวลานาน ให้ตรวจสอบค่าความกว้างและความสูงใน การติดตาม ตรวจสอบให้แน่ใจว่าบิตแมปที่แสดงไม่ได้มีขนาดใหญ่กว่า บริเวณหน้าจอที่กำลังแสดง เพราะจะทำให้เสียเวลาอัปโหลด ความทรงจำ โดยทั่วไปแล้ว ไลบรารีการโหลดบิตแมปจะให้วิธีการในการส่งคำขอ บิตแมปที่มีขนาดเหมาะสม

ใน Android 7.0 โค้ดที่โหลดบิตแมปซึ่งมักดำเนินการโดยไลบรารีสามารถเรียกใช้ prepareToDraw() ถึง ทริกเกอร์การอัปโหลดล่วงหน้าก่อนที่จะจำเป็นต้องใช้ วิธีนี้จะทำให้การอัปโหลดเกิดขึ้นตั้งแต่เนิ่นๆ ขณะที่ RenderThread ไม่มีการใช้งาน ซึ่งทำได้หลังจากถอดรหัสหรือเมื่อเชื่อมโยง บิตแมปไปยังมุมมอง ตราบใดที่คุณทราบบิตแมป ตามหลักการแล้ว การโหลดบิตแมป ไลบรารีนี้ช่วยคุณได้ แต่ถ้าคุณจัดการ หรือต้องการดูแล อย่ากดการอัปโหลดบนอุปกรณ์ที่ใหม่กว่า คุณสามารถโทรหา prepareToDraw() ด้วยตัวเองได้ โค้ด

วันที่ แอปหนึ่งใช้เวลาจำนวนมากใน
  เฟรมการอัปโหลดบิตแมปขนาดใหญ่
รูปที่ 2 แอปใช้เวลาอยู่ในเฟรมนาน อัปโหลดบิตแมปขนาดใหญ่ ลดขนาดหรือทริกเกอร์ตั้งแต่เนิ่นๆ ถอดรหัสด้วย prepareToDraw()

ความล่าช้าในการกำหนดเวลาของชุดข้อความ

เครื่องจัดตารางเวลาเทรดเป็นส่วนหนึ่งของระบบปฏิบัติการ Android ที่รับผิดชอบ ซึ่งกำหนดว่าเทรดใดในระบบจะต้องทำงาน เมื่อใด และใช้เวลาเท่าไร

บางครั้งการกระตุกอาจเกิดขึ้นเนื่องจากเทรด UI ของแอปถูกบล็อกหรือไม่ทำงาน Systrace ใช้สีต่างๆ ดังที่แสดงในรูปที่ 3 เพื่อระบุเมื่อชุดข้อความ นอนหลับ (สีเทา) เรียกใช้ได้ (สีฟ้า: ทำงานได้ แต่ไม่ได้เลือกโดย เครื่องจัดตารางเวลาที่จะเรียกใช้) กำลังทำงานอยู่ (สีเขียว) หรืออยู่ในการนอนหลับอย่างไม่ขาดตอน (สีแดงหรือสีส้ม) วิธีนี้เป็นประโยชน์อย่างยิ่งในการแก้ปัญหาการกระตุกที่ เกิดจากความล่าช้าในการจัดตารางเวลาของชุดข้อความ

ไฮไลต์ช่วงเวลาที่ชุดข้อความ UI
  นอนหลับ
รูปที่ 3 ไฮไลต์ของช่วงเวลาที่ชุดข้อความ UI อยู่ นอนหลับ

บ่อยครั้งที่การเรียกใช้ Binder คือกลไกการสื่อสารระหว่างโปรเซส (IPC) ใน Android - ทำให้การดำเนินการของแอปหยุดชั่วคราวเป็นเวลานาน ใน Android เวอร์ชันหลังๆ เป็นหนึ่งในสาเหตุที่พบบ่อยที่สุดที่ทำให้เธรด UI หยุดทำงาน โดยทั่วไป วิธีแก้ไขคือให้หลีกเลี่ยงการเรียกฟังก์ชันที่เรียกไฟล์ Binder หากเป็น ให้แคชค่าหรือย้ายงานไปยังชุดข้อความเบื้องหลัง เป็นฐานของโค้ด ใหญ่ขึ้น คุณอาจเพิ่มการเรียก Binder โดยไม่ตั้งใจด้วยการเรียกใช้ Binder ระดับต่ำ ถ้าคุณไม่ระวังตัว แต่คุณสามารถค้นหาและแก้ไขได้ด้วยการติดตาม

หากคุณมีธุรกรรม Binder คุณสามารถบันทึกสแต็กการโทรด้วย คำสั่ง adb ต่อไปนี้

$ adb shell am trace-ipc start
… use the app - scroll/animate ...
$ adb shell am trace-ipc stop --dump-file /data/local/tmp/ipc-trace.txt
$ adb pull /data/local/tmp/ipc-trace.txt

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

วันที่ แสดงเธรด UI ที่เข้าสู่โหมดสลีปเนื่องจากแฟ้ม
  การทำธุรกรรมในการขนย้ายรถบ้าน โฟกัสที่ตรรกะการเชื่อมโยงและใช้การติดตาม IP เพื่อ
  ติดตามและลบการเรียก Binder
รูปที่ 4 เธรด UI อยู่ในโหมดสลีปเนื่องจากแฟ้ม การทำธุรกรรมในการขนย้ายรถบ้าน ทำให้ตรรกะการเชื่อมโยงเรียบง่ายและใช้ง่าย trace-ipc เพื่อติดตามและนำการเรียก Binder ออก

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

การจัดสรรออบเจ็กต์และการรวบรวมขยะ

การจัดสรรออบเจ็กต์และการรวบรวมขยะ (GC) เป็นปัญหาน้อยกว่าอย่างมาก ตั้งแต่ ART เปิดตัว ART เป็นรันไทม์เริ่มต้นใน Android 5.0 ในการลดน้ำหนักชุดข้อความด้วยงานเพิ่มเติมนี้ คุณสามารถจัดสรรได้ เพื่อตอบสนองต่อเหตุการณ์ที่เกิดขึ้นไม่บ่อยนัก ซึ่งไม่ได้เกิดขึ้นหลายครั้งต่อวินาที เช่น ผู้ใช้แตะปุ่ม แต่โปรดทราบว่าการจัดสรรแต่ละครั้งจะมีค่าใช้จ่าย ถ้า อยู่ในลูปที่เรียกใช้บ่อยๆ ดังนั้น โปรดหลีกเลี่ยงการจัดสรร เพื่อลดภาระของ GC

Systrace จะแสดงให้คุณทราบว่า GC ทำงานบ่อยหรือไม่ และหน่วยความจำของ Android เครื่องมือสร้างโปรไฟล์จะแสดงให้เห็นว่าการจัดสรร มาจากไหน หากคุณหลีกเลี่ยงการจัดสรรเมื่อเป็นไปได้ โดยเฉพาะในจำนวนที่จำกัด การวนซ้ำ คุณก็มีแนวโน้มที่จะพบปัญหาน้อยลง

วันที่ แสดง GC 94 มิลลิวินาทีบน HeapTaskDaemon
รูปที่ 5 GC 94 มิลลิวินาทีบน HeapTaskDaemon ชุดข้อความ

ใน Android เวอร์ชันล่าสุด โดยทั่วไปแล้ว GC จะทำงานบนเทรดเบื้องหลังที่ชื่อ HeapTaskDaemon การจัดสรรปริมาณมากอาจหมายความว่ามี CPU มากขึ้น ทรัพยากรที่ใช้ใน GC ดังที่แสดงในรูปที่ 5