ทำให้แอปปรับเปลี่ยนตามอุปกรณ์อยู่เสมอ

รูปที่ 1 กล่องโต้ตอบ ANR ที่แสดงต่อผู้ใช้

เอกสารนี้อธิบายวิธีที่ระบบ Android กำหนดว่าแอปไม่ตอบสนองหรือไม่ และแสดงวิธีทำให้แอปตอบสนองอยู่เสมอ

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

ทริกเกอร์ ANR

โดยทั่วไปแล้ว ระบบจะแสดง ANR หากแอปตอบสนองต่อข้อมูลจากผู้ใช้ที่ป้อนในเธรดหลักไม่ได้ ซึ่งเรียกอีกอย่างว่าเธรด UI ทำให้ระบบประมวลผลเหตุการณ์ข้อมูลจากผู้ใช้ที่ป้อนเข้ามาไม่ได้

ตัวอย่างเช่น ANR อาจเกิดขึ้นหากแอปดำเนินการ I/O ที่บล็อก เช่น การเข้าถึงเครือข่ายในเธรด UI อีกตัวอย่างหนึ่งคือเมื่อแอปใช้เวลามากเกินไปในการสร้างโครงสร้างที่ซับซ้อนในหน่วยความจำหรือคำนวณการเคลื่อนไหวครั้งต่อไปในเกมในเธรด UI

ใน Android ระบบบริการ ActivityManager และ WindowManager จะตรวจสอบการตอบสนองของแอป Android จะแสดงกล่องโต้ตอบ ANR สำหรับแอปเมื่อตรวจพบเงื่อนไขอย่างใดอย่างหนึ่งต่อไปนี้

  • ไม่มีการตอบสนองต่อเหตุการณ์ข้อมูลที่ป้อน เช่น เหตุการณ์การกดปุ่มหรือการแตะหน้าจอ ภายใน 5 วินาที
  • A BroadcastReceiver ไม่ได้ ดำเนินการเสร็จภายใน 10-20 วินาทีสำหรับ Intent เบื้องหน้า ดูข้อมูลเพิ่มเติมได้ที่ การหมดเวลาของ Broadcast Receiver

หลีกเลี่ยง ANR

ต่อไปนี้เป็นเคล็ดลับทั่วไปในการหลีกเลี่ยง ANR ดูรายละเอียดเพิ่มเติมเกี่ยวกับการวินิจฉัยและแก้ไขข้อบกพร่องของ ANR ประเภทต่างๆ ได้ในหน้าอื่นๆ ในส่วนนี้

  • ทำให้เธรดหลักไม่ถูกบล็อกอยู่เสมอ และใช้เธรดอย่างมีกลยุทธ์

    • อย่าดำเนินการที่บล็อกหรือใช้เวลานานในเธรดหลักของแอป ให้ใช้ Coroutine ของ Kotlin เพื่อโอนงานไปยัง Dispatcher เบื้องหลัง (เช่น Dispatchers.IO หรือ Dispatchers.Default) ใช้กลไกต่างๆ เช่น viewModelScope เพื่อเปิดใช้งานงานเบื้องหลังเหล่านี้อย่างปลอดภัย หรือ LaunchedEffect เพื่อทริกเกอร์งานเหล่านี้เพื่อตอบสนองต่อการเปลี่ยนแปลงสถานะ Compose

    • พยายามลดการแย่งชิงล็อกระหว่างเธรดหลักกับเธรดอื่นๆ

    • ลดงานที่ไม่เกี่ยวข้องกับ UI ในเธรดหลัก เช่น เมื่อจัดการการบรอดแคสต์หรือเรียกใช้บริการ เมธอดหรือฟังก์ชันใดก็ตามที่ทำงานในเธรด UI ต้องทำงานให้น้อยที่สุด โดยเฉพาะอย่างยิ่ง กิจกรรมต่างๆ ต้องทำงานให้น้อยที่สุดเพื่อตั้งค่าในเมธอดวงจรชีวิตที่สำคัญ เช่น onCreate และ onResume อย่าดำเนินการ I/O หรือการคำนวณที่ซับซ้อนและบล็อกโดยตรงภายในฟังก์ชันที่ประกอบกันได้ เนื่องจากจะบล็อกเธรด UI ระหว่างการคอมโพสและการคอมโพสใหม่ ดูข้อมูลเพิ่มเติมเกี่ยวกับโซลูชันที่พร้อมใช้งานสำหรับการกำหนดเวลาทำงานในเธรดเบื้องหลังและการสื่อสารกลับไปยัง UI ได้ที่ ภาพรวมของงานเบื้องหลัง

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

  • ทำให้การเริ่มต้นแอปเป็นไปอย่างรวดเร็ว ลดการดำเนินการที่ช้าหรือบล็อกในโค้ดการเริ่มต้นของแอป เช่น เมธอดที่ทำงานระหว่างการตั้งค่าการแทรกทรัพยากร Dependency (เช่น กับ Hilt) หรือคอมโพเนนต์ที่เริ่มต้นโดยใช้ ไลบรารี Jetpack App Startup คุณสามารถเพิ่มประสิทธิภาพการเริ่มต้นแอปได้โดยใช้ โปรไฟล์พื้นฐาน, โปรไฟล์การเริ่มต้น และ R8

  • หากใช้ BroadcastReceiver ให้ลองเรียกใช้ Broadcast Receiver ในเธรดที่ไม่ใช่เธรดหลักโดยใช้ Context.registerReceiver ดูข้อมูลเพิ่มเติมได้ที่ ANR ใน BroadcastReceiver

    • หากใช้ goAsync ให้ตรวจสอบว่าได้เรียก PendingResult.finish อย่างรวดเร็ว ก่อนที่จะหมดเวลา ANR

ANR ใน BroadcastReceiver

เวลาดำเนินการของ BroadcastReceiver มีข้อจำกัดเนื่องจาก Broadcast Receiver มีไว้เพื่อทำงานขนาดเล็กและแยกกันในเบื้องหลัง เช่น การบันทึกการตั้งค่าหรือการลงทะเบียน Notification ดังนั้น เช่นเดียวกับเมธอดอื่นๆ ที่เรียกในเธรด UI แอปต้องหลีกเลี่ยงการดำเนินการหรือการคำนวณที่อาจใช้เวลานานใน Broadcast Receiver ให้ดำเนินการที่ใช้เวลานานในเบื้องหลังเพื่อดำเนินการในภายหลังแทนที่จะดำเนินการผ่านเธรด UI ดูข้อมูลเพิ่มเติมเกี่ยวกับโซลูชันที่เป็นไปได้ได้ที่ภาพรวมของงานเบื้องหลัง

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

เสริมการตอบสนอง

โดยทั่วไปแล้ว 100-200 มิลลิวินาทีคือเกณฑ์ที่ผู้ใช้จะรู้สึกว่าแอปช้า ต่อไปนี้เป็นเคล็ดลับเพิ่มเติมในการทำให้แอปดูเหมือนตอบสนองต่อผู้ใช้

  • หากแอปทำงานในเบื้องหลังเพื่อตอบสนองต่อข้อมูลจากผู้ใช้ ให้แสดงความคืบหน้า เช่น ด้วย CircularProgressIndicator หรือ LinearProgressIndicator ใน UI

  • สำหรับการคำนวณการเคลื่อนไหวในเกมโดยเฉพาะ ให้คำนวณใน Coroutine เบื้องหลังหรือเทรดผู้ปฏิบัติงาน

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

  • ใช้เครื่องมือประสิทธิภาพ เช่น Perfetto และ เครื่องมือสร้างโปรไฟล์ CPU เพื่อระบุจุดคอขวดในการตอบสนองของแอป