เมื่อเธรด UI ของแอป Android ถูกบล็อกนานเกินไป ระบบจะทริกเกอร์ข้อผิดพลาด "แอปพลิเคชันไม่ตอบสนอง" (ANR) หากแอปอยู่ในเบื้องหน้า ระบบจะแสดงกล่องโต้ตอบแก่ผู้ใช้ดังที่แสดงในรูปที่ 1 กล่องโต้ตอบ ANR จะให้โอกาสผู้ใช้ในการบังคับปิดแอป
ANR เป็นปัญหาเนื่องจากเธรดหลักของแอปซึ่งมีหน้าที่รับผิดชอบในการอัปเดต UI ไม่สามารถประมวลผลข้อมูลจากผู้ใช้หรือวาด UI ได้ ซึ่งทำให้ผู้ใช้รู้สึกหงุดหงิด ดูข้อมูลเพิ่มเติมเกี่ยวกับเธรดหลักของแอปได้ที่ ภาพรวมของกระบวนการและ เธรด
ระบบจะทริกเกอร์ ANR สำหรับแอปของคุณเมื่อเกิดเงื่อนไขข้อใดข้อหนึ่งต่อไปนี้
- หมดเวลานำส่งอินพุต: หากแอปไม่ตอบสนองต่อเหตุการณ์อินพุต (เช่น การกดปุ่มหรือการแตะหน้าจอ) ภายใน 5 วินาที
- กำลังเรียกใช้บริการ: หากบริการที่แอปประกาศไว้ไม่สามารถเรียกใช้
Service.onCreateและService.onStartCommand/Service.onBindจนเสร็จภายในไม่กี่วินาที Service.startForegroundไม่ได้เรียกใช้: หากแอปใช้Context.startForegroundServiceเพื่อเริ่มบริการใหม่ในเบื้องหน้า แต่บริการไม่ได้เรียกใช้startForegroundภายใน 5 วินาที- การออกอากาศ Intent: หาก
BroadcastReceiverเรียกใช้ไม่เสร็จภายในระยะเวลาที่กำหนด หากแอปมีกิจกรรมใดๆ ในเบื้องหน้า ระยะหมดเวลานี้คือ 5 วินาที JobSchedulerการโต้ตอบ: หากJobServiceไม่ได้กลับมา จากJobService.onStartJobหรือJobService.onStopJobภายในไม่กี่วินาที หรือหากมีการเริ่ม งานที่ผู้ใช้เป็นผู้เริ่ม และแอปไม่ได้เรียกใช้JobService.setNotificationภายในไม่กี่วินาทีหลังจากมีการเรียกใช้JobService.onStartJobสำหรับแอปที่กำหนดเป้าหมายเป็น Android 13 และต่ำกว่า ANR จะไม่แสดงและไม่รายงานไปยังแอป สำหรับแอปที่กำหนดเป้าหมายเป็น Android 14 ขึ้นไป ANR จะแสดงอย่างชัดเจนและรายงานไปยังแอป
หากแอปพบ ANR คุณสามารถใช้คำแนะนำในเอกสารนี้เพื่อวินิจฉัยและแก้ไขปัญหา
ตรวจหาปัญหา
หากคุณเผยแพร่แอปแล้ว คุณสามารถใช้ Android Vitals เพื่อดูข้อมูลเกี่ยวกับ ANR ของแอปได้ นอกจากนี้ คุณยังใช้เครื่องมืออื่นๆ เพื่อตรวจหา ANR ในภาคสนามได้ด้วย แต่โปรดทราบว่าเครื่องมือของบุคคลที่สามไม่สามารถรายงาน ANR ใน Android 10 และต่ำกว่าได้ ซึ่งแตกต่างจาก Android Vitals
Android Vitals
Android Vitals ช่วยให้คุณตรวจสอบและปรับปรุงอัตรา ANR ของแอปได้ Android Vitals วัดอัตรา ANR หลายรายการ ดังนี้
- อัตรา ANR: เปอร์เซ็นต์ของผู้ใช้ที่ใช้งานอยู่รายวันที่พบ ANR ทุกประเภท
- อัตรา ANR ที่ผู้ใช้รับรู้: เปอร์เซ็นต์ของผู้ใช้ที่ใช้งานอยู่รายวันที่พบ ANR ที่ผู้ใช้รับรู้อย่างน้อย 1 ครั้ง ปัจจุบันระบบจะพิจารณาเฉพาะ ANR ประเภท
Input dispatching timed outว่าเป็น ANR ที่ผู้ใช้รับรู้ - อัตรา ANR หลายครั้ง: เปอร์เซ็นต์ของผู้ใช้ที่ใช้งานอยู่รายวันที่พบ ANR อย่างน้อย 2 ครั้ง
ผู้ใช้ที่ใช้งานอยู่รายวันคือผู้ใช้ที่ไม่ซ้ำกันซึ่งใช้แอปของคุณในอุปกรณ์เครื่องเดียวในวันเดียว โดยอาจใช้หลายเซสชัน หากผู้ใช้ใช้แอปของคุณในอุปกรณ์มากกว่า 1 เครื่องในวันเดียว อุปกรณ์แต่ละเครื่องจะมีส่วนร่วมในการนับจำนวนผู้ใช้ที่ใช้งานอยู่ของวันนั้น
อัตรา ANR ที่ผู้ใช้รับรู้เป็น Vitals หลัก ซึ่งหมายความว่าเมตริกนี้ส่งผลกระทบต่อ การค้นพบได้ของแอปคุณใน Google Play และมีความสำคัญเนื่องจาก ANR ที่เมตริกนี้นับอาจเกิดขึ้นทุกครั้งที่ผู้ใช้มีส่วนร่วมกับแอป ซึ่งทำให้เกิดการหยุดชะงักมากที่สุด
Play กำหนดเกณฑ์ลักษณะการทำงานที่ไม่ถูกต้อง 2 รายการไว้ในเมตริกนี้
- เกณฑ์ลักษณะการทำงานที่ไม่ถูกต้องโดยรวม: ผู้ใช้ที่ใช้งานอยู่รายวันอย่างน้อย 0.47% พบ ANR ที่ผู้ใช้รับรู้ในอุปกรณ์ทุกรุ่น
- เกณฑ์ลักษณะการทำงานที่ไม่ถูกต้องในแต่ละอุปกรณ์: ผู้ใช้รายวันอย่างน้อย 8% พบ ANR ที่ผู้ใช้รับรู้ในอุปกรณ์รุ่นใดรุ่นหนึ่ง
หากแอปมีเมตริกเกินเกณฑ์ลักษณะการทำงานที่ไม่ถูกต้องโดยรวม ก็มีแนวโน้มที่จะค้นพบได้น้อยลงในอุปกรณ์ทุกเครื่อง หากแอปมีเมตริกเกินเกณฑ์ลักษณะการทำงานที่ไม่ถูกต้องในแต่ละอุปกรณ์ในอุปกรณ์บางเครื่อง ก็มีแนวโน้มที่จะค้นพบได้น้อยลงในอุปกรณ์เหล่านั้น และอาจมีคำเตือนแสดงในข้อมูลสินค้าใน Store
Android Vitals สามารถแจ้งเตือนคุณผ่าน Play Console เมื่อแอปแสดง ANR มากเกินไป
ดูข้อมูลเกี่ยวกับวิธีที่ Google Play รวบรวมข้อมูล Android Vitals ได้ในเอกสารประกอบของ Play Console
วินิจฉัย ANR
มีรูปแบบที่พบบ่อยบางอย่างที่ควรดูเมื่อวินิจฉัย ANR ดังนี้
- แอปกำลังดำเนินการที่ช้าซึ่งเกี่ยวข้องกับ I/O ในเธรดหลัก
- แอปกำลังทำการคำนวณที่ใช้เวลานานในเธรดหลัก
- เธรดหลักกำลังเรียกใช้ Binder แบบซิงโครนัสไปยังกระบวนการอื่น และกระบวนการนั้นใช้เวลานานในการกลับมา
- เธรดหลักถูกบล็อกโดยรอให้บล็อกที่ซิงโครไนซ์สำหรับการดำเนินการที่ใช้เวลานานซึ่งเกิดขึ้นในเธรดอื่น
- เธรดหลักอยู่ในภาวะชะงักงันกับเธรดอื่น ไม่ว่าจะอยู่ในกระบวนการของคุณเองหรือผ่านการเรียกใช้ Binder เธรดหลักไม่ได้รอเพียงแค่การดำเนินการที่ใช้เวลานาน ให้เสร็จสมบูรณ์ แต่ยังอยู่ในสถานการณ์ การติดตาย ด้วย
เทคนิคต่อไปนี้สามารถช่วยคุณระบุสาเหตุของ ANR ได้
HealthStats
HealthStats ให้เมตริกเกี่ยวกับสถานะของแอปพลิเคชันโดยการบันทึกเวลาทั้งหมดของผู้ใช้และระบบ เวลา CPU สถิติเครือข่ายและวิทยุ เวลาเปิด/ปิดหน้าจอ และการปลุก ซึ่งจะช่วยให้คุณวัดการใช้งาน CPU โดยรวมและการใช้แบตเตอรี่ได้
Debug
Debug ช่วยให้คุณตรวจสอบแอปพลิเคชัน Android ระหว่างการพัฒนา ซึ่งรวมถึงการติดตามและการนับการจัดสรรเพื่อระบุการกระตุกและความล่าช้าในแอป
นอกจากนี้ คุณยังใช้ Debug เพื่อรับตัวนับหน่วยความจำรันไทม์และหน่วยความจำเนทีฟ รวมถึงเมตริกหน่วยความจำที่ช่วยให้คุณระบุหน่วยความจำที่ใช้ของกระบวนการหนึ่งๆ ได้ด้วย
ApplicationExitInfo
ApplicationExitInfo พร้อมใช้งานใน Android 11 (ระดับ API 30) ขึ้นไป และให้ข้อมูลเกี่ยวกับสาเหตุที่แอปพลิเคชันออก ซึ่งรวมถึง ANR, หน่วยความจำเหลือน้อย, แอปขัดข้อง, การใช้งาน CPU มากเกินไป, การหยุดชะงักของผู้ใช้, การหยุดชะงักของระบบ และการเปลี่ยนแปลงสิทธิ์รันไทม์
โหมดเข้มงวด
การใช้ StrictMode ช่วยให้คุณพบการดำเนินการ I/O ที่ไม่ได้ตั้งใจในเธรดหลัก
ขณะที่คุณกำลังพัฒนาแอป คุณสามารถใช้ StrictMode ในระดับแอปพลิเคชันหรือกิจกรรมก็ได้
เปิดใช้กล่องโต้ตอบ ANR ในเบื้องหลัง
Android จะแสดงกล่องโต้ตอบ ANR สำหรับแอปที่ใช้เวลานานเกินไปในการประมวลผลข้อความออกอากาศก็ต่อเมื่อเปิดใช้แสดง ANR ทั้งหมด ในตัวเลือกสำหรับนักพัฒนาแอป ของอุปกรณ์ ด้วยเหตุนี้ ระบบจึงไม่แสดงกล่องโต้ตอบ ANR ในเบื้องหลังต่อผู้ใช้เสมอไป แม้ว่าแอปจะพบปัญหาด้านประสิทธิภาพก็ตาม
จุดคอขวดของการจัดองค์ประกอบใหม่
ใช้ Android Studio Profiler และ เครื่องมือตรวจสอบเลย์เอาต์ เพื่อติดตามจุดคอขวดของการจัดองค์ประกอบใหม่ ดูข้อมูลเพิ่มเติมได้ที่ ประสิทธิภาพของ Jetpack Compose
ดึงไฟล์การติดตาม
Android จะจัดเก็บข้อมูลการติดตามเมื่อพบ ANR ใน OS เวอร์ชันเก่าจะมีไฟล์ /data/anr/traces.txt ไฟล์เดียวในอุปกรณ์ ใน OS เวอร์ชันใหม่จะมีไฟล์ /data/anr/anr_* หลายไฟล์ คุณสามารถเข้าถึงการติดตาม ANR
จากอุปกรณ์หรือโปรแกรมจำลองได้โดยใช้ Android Debug Bridge (adb) ในฐานะผู้ใช้ระดับราก:
adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>
คุณสามารถบันทึกรายงานข้อบกพร่องจากอุปกรณ์จริงได้โดยใช้ตัวเลือกสำหรับนักพัฒนาแอป "บันทึกรายงานข้อบกพร่อง" ในอุปกรณ์หรือใช้คำสั่ง adb bugreport ในคอมพิวเตอร์สำหรับการพัฒนาซอฟต์แวร์ ดูข้อมูลเพิ่มเติมได้ที่
บันทึกและอ่านรายงานข้อบกพร่อง
แก้ไขปัญหา
หลังจากระบุปัญหาแล้ว คุณสามารถใช้เคล็ดลับในส่วนนี้เพื่อแก้ไขปัญหาที่พบได้ทั่วไป
โค้ดที่ทำงานช้าในเธรดหลัก
ระบุตำแหน่งในโค้ดที่เธรดหลักของแอปไม่ว่างนานกว่า 5 วินาที มองหาเคสการใช้งานที่น่าสงสัยในแอปและลองสร้าง ANR ขึ้นมาใหม่
ปัญหาที่พบบ่อยคือการดำเนินการที่ใช้เวลานานในคอมโพสได้โดยตรง ดังนี้
@Composable
fun BadList(rawStrings: List<String>) {
// Math or sorting inside the composable runs on EVERY recomposition pass!
val heavilyProcessedList = rawStrings
.filter { it.isNotBlank() }
.map { it.uppercase().reversed() }
.map { it.computationallyHeavyFunction() }
.sortedBy { it.length }
LazyColumn { items(sortedList) { Text(it) } }
}
// Modern Compose-first fix
@Composable
fun GoodList(viewModel: MyViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// UI simply renders state; no heavy processing allowed here
LazyColumn { items(uiState.sortedData) { Text(it) } }
}
I/O ในเธรดหลัก
การดำเนินการ I/O ในเธรดหลักเป็นสาเหตุที่พบบ่อยของการดำเนินการที่ช้าในเธรดหลัก ซึ่งอาจทำให้เกิด ANR ใน Compose นักพัฒนาแอปมักจะทริกเกอร์การอ่านดิสก์ (เช่น SharedPreferences หรือการเรียกฐานข้อมูล) โดยไม่ได้ตั้งใจขณะพยายามหาค่าเริ่มต้น
ดำเนินการ I/O ที่ใช้เวลานานนอกเลเยอร์ UI ใช้
withContext(Dispatchers.IO) ใน ViewModel หรือใช้
Repository ในเลเยอร์ข้อมูลจะดีกว่า
ภาวะชะงักงัน
การติดตายเกิดขึ้นเมื่อเทรดเข้าสู่สถานะรอเนื่องจากเทรดอื่นถือทรัพยากรที่จำเป็นอยู่ ซึ่งเทรดนั้นก็รอทรัพยากรที่เทรดแรกถืออยู่ด้วย หากเธรดหลักของแอปอยู่ในสถานการณ์นี้ มีแนวโน้มที่จะเกิด ANR
ภาวะชะงักงันเป็นปรากฏการณ์ที่ได้รับการศึกษาอย่างดีในวิทยาการคอมพิวเตอร์ และมีอัลกอริทึมการป้องกันภาวะชะงักงันที่คุณใช้เพื่อหลีกเลี่ยงภาวะชะงักงันได้
ดูข้อมูลเพิ่มเติมได้ที่ภาวะชะงักงันและ อัลกอริทึมการป้องกันภาวะชะงักงันใน Wikipedia
เมื่อใช้ Kotlin และ Compose คุณสามารถแทนที่การล็อกแบบดั้งเดิมด้วย
Mutexes ของโครูทีนแบบไม่บล็อก (Mutex.withLock) เพื่อป้องกันการบล็อกเธรด
โดย ระงับบริบทการดำเนินการแทนที่จะหยุดเธรด UI
เช่น
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
// Modern non-blocking concurrency state architecture
class SecureDataRepository {
private val mutex = Mutex()
suspend fun safeUIAccess() {
// If locked, the main thread suspends seamlessly, preventing an ANR
mutex.withLock {
performSafeOperation()
}
}
}
Broadcast Receiver ที่ทำงานช้า
แอปสามารถตอบสนองต่อข้อความออกอากาศ เช่น การเปิดหรือปิดโหมดเครื่องบิน หรือการเปลี่ยนแปลงสถานะการเชื่อมต่อ ผ่าน Broadcast Receiver ANR จะเกิดขึ้นเมื่อแอปใช้เวลานานเกินไปในการประมวลผลข้อความประกาศ
ANR จะเกิดขึ้นในกรณีต่อไปนี้
- Broadcast Receiver เรียกใช้เมธอด
onReceiveไม่เสร็จภายในระยะเวลาหนึ่ง - Broadcast Receiver เรียกใช้
goAsyncและเรียกใช้finishในออบเจ็กต์PendingResultไม่สำเร็จ
แอปควรดำเนินการสั้นๆ เท่านั้นในเมธอด onReceive
ของ BroadcastReceiver อย่างไรก็ตาม หากแอปต้องมีการประมวลผลที่ซับซ้อนมากขึ้นอันเป็นผลมาจากข้อความประกาศ คุณควรเลื่อนงานไปยัง
ViewModel (ใช้ประโยชน์จากพลังของโครูทีน ขอบเขต และตัวส่งของ Kotlin)
หากคาดว่างานจะใช้เวลาไม่กี่วินาทีเท่านั้น, ตัวเก็บสถานะทุกประเภท
หรือไปยัง WorkManager สำหรับงานที่คาดว่าจะใช้เวลานานกว่าไม่กี่วินาที
GameActivity
ไลบรารี GameActivity ช่วยลด ANR ใน กรณีศึกษา ของ
เกมและแอปที่เขียนด้วย C หรือ C++ หากคุณแทนที่กิจกรรมเนทีฟที่มีอยู่ด้วย GameActivity คุณจะลดการบล็อกเธรด UI และป้องกันไม่ให้เกิด
ANR บางรายการได้
ดูข้อมูลเพิ่มเติมเกี่ยวกับ ANR ได้ที่ ทำให้แอปตอบสนอง ดูข้อมูลเพิ่มเติมเกี่ยวกับเธรดได้ที่ ประสิทธิภาพที่ดีขึ้นผ่านการใช้เธรด
แหล่งข้อมูลเพิ่มเติม
เนื้อหา Views
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- การปลุกระบบบ่อยเกินไป