ANR (Application Not Responding) คือการแจ้งเตือนระบบใน Android ที่ปรากฎตัวเมื่อแอพไม่ตอบสนองต่ออินพุตภายใน 5 วินาที แตกต่างจากข้อผิดพลาดเล็กน้อย (ข้อผิดพลาดเชิงตรรกะที่ไม่มีการบล็อก UI) และลาก (การช้าลงโดยไม่หยุดโดยสิ้นเชิง), ANR เป็นความล้มเหลวรุแบบร้ายแรงที่ถูกบันทึกโดยระบบปฏิบัติการ: Android แสดงกล่องโต้ตอบสนองระบบ “แอปไม่ตอบสนอง” พร้อมตัวเลือกในการปิดหรือรอเผ้าชมากละ. ตาม Android Vitals Documentation แอพที่มีอัตรา ANR สูงกว่า 0.5% จะได้รับการจัดอันดับที่ต่ำลงใน Google Play และอาจถูกซ่อนจากคำแนะนำ การวิจัยรวมถึงการวิเคราะฮ์ /data/anr/traces.txt, การใช้ StrictMode และการทำโปรไฟล์เธรดหลัก
หัวข้อสำคัญ
ANR (Application Not Responding) เป็นกลไกการป้องกันผู้ใช้ใน Android ที่ทำงานเมื่อแอพหยุดตอบสนองต่ออินพุต ระบบจะติดตามเวลาการประมวลผลเหตุการณ์: หาก BroadcastReceiver ไม่เสร็จ onReceive ภายใน 10 วินาที, Service ไม่กลับจาก onCreate ภายใน 20 วินาที หรือ ContentProvider ไม่ตอบสนองภายใน 15 วินาที — Android จะสร้าง ANR
เมื่อเกิด ANR, Android จะแสดงกล่องโต้ตอบสนองระบบเทอบหน้าต่างทั้งหมด: “แอปไม่ตอบสนอง คุณต้องการปิดหรือรอเผ้าชมากละ?” ผู้ใช้สามารถปิดแอพหรือรอให้มันกลับมาใช้งานได้ หากเกิด ANR บ่อยครั้ง ผู้ใช้จะถอนแอพออก Google Play พิจารณา อัตรา ANR — เปอร์เซนต์ของเซสชั่นที่มี ANR — ในอัลกอริทึมการจัดอันดับ
iOS ไม่มีสิ่งที่เทียบเท่ากับ ANR ที่มีกล่องโต้ตอบสนองระบบ แทนนั้น Apple ใช้ Watchdog ซึ่งจะสิ้นสุดกระบวนการด้วยรหัสออก 0x8badf00d ผู้ใช้จะไม่เห็นกล่องโต้ตอบสนอง — แอพจะปิดกลับไปยังหน้าจอหลัก ทำให้ ANR บน Android สังเกตแก่ผู้ใช้มากกว่า แต่ให้ข้อมูลการวิจัยแก่ระบบมากขึ้น
ANR เกิดขึ้นเมื่อระบบติดตามการหมดเวลาสำหรับหนึ่งในสี่ประเภทของคอมโพนันต์ แต่ละคอมโพนันต์มีขอบเขตเวลาของตนเอง
BroadcastReceiver ทำงานในเธรดหลัก หาก onReceive เริ่มคำขอเครือข่ายแบบซิงโครนัส, การเขียนฐานข้อมูลที่ยาว หรือรอการล็อก — ANR จะเกิดขึ้นภายใน 10 วินาที วิธีแก้: ใช้ goAsync() และ WorkManager สำหรับการประมวลผลเบื้องหลัง สถานการณ์ทั่วไปคือการรับแจ้ง Push จาก FCM และบันทึกแบบซิงโครนัสใน Room
Service.onCreate และ Service.onStartCommand มีขอบเขต 20 วินาที หากเซิร์วิสเริ่มการเริ่มต้นที่หนัก (การโหลดไลบรารี, การอ่านค่าการตั้งค่าจากเครือข่าย) ในเธรดหลัก — ANR เป็นสิ่งที่หลีกเลี่ยงไม่ได้ ใช้ IntentService (เก่า) หรือ WorkManager สำหรับการทำงานเบื้องหลังที่มั่นใจ
ContentProvider.onCreate ทำงานก่อน Application.onCreate และมีขอบเขต 15 วินาที หากโปรไวเดอร์ทำการย้ายข้อมูลฐานข้อมูล, โหลดพจนานุกรม หรือเริ่มต้น SDK จากเครือข่าย — สิ่งนี้ทำให้เกิด ANR เมื่อเริ่มต้นแอพ วิธีแก้: การเริ่มต้นแบบขีเฉือย, การโออฟโหลดงานหนักไปยัง WorkManager
Android มีเครื่องมือหลายอย่างสำหรับการวิเคราะฮ์ ANR: ตั้งแต่บันทึกระบบไปจนถึงไลบรารีเฉพาะทาง
ในแต่ละ ANR, Android จะบันทึกไฟล์ /data/anr/traces.txt พร้อมการดัมพ์สแต็กของเธรดทั้งหมดของแอพ ค้นหาเธรด “main” — เมธอดสุดท้ายในสแต็กบ่งบอกสาเหตุ รูปแบบทั่วไป: Thread.sleep(), InputStream.read(), BinderProxy.transact() สำหรับการแยกไฟล์จากอุปกรณ์, ใช้ adb โดยมีสิทธิผู้ใช้ระดับสูง
Firebase Crashlytics จะรวบรวม ANR โดยอัตโนมัติและแสดงในแดสบอร์ดพร้อมการติดตาม สำหรับ Android 11+ รายงาน ANR จะมาพร้อมกับสแต็กเต็มของเธรดหลัก การผนวการต้องเพิ่มการพึ่งพาและเริ่มต้น FirebaseApp ใน Application.onCreate
CPU Profiler ใน Android Studio ช่วยให้คุณบันทึกการติดตามแอพและดูว่าเมธอดใดใช้เวลา CPU เปิดใช้งาน “Record with method traces” และจำลองสถานการณ์ที่ทำให้เกิด ANR เส้นเวลาจะแสดงว่าเมธอดใดที่กำลังทำงานในเธรดหลักขณะที่เกิดการค้าง
ตัวอย่างการผนวก Firebase Crashlytics สำหรับการรวบรวม ANR บน Android:
class App : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
FirebaseCrashlytics.getInstance()
.setCrashlyticsCollectionEnabled(true)
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
}
การแก้ไข ANR หมายความว่าการย้ายการดำเนินการที่ยาวทั้งหมดจากเธรดหลักไปยังเธรดเบื้องหลัง มาดูเทคนิคเฉพาะเจาะจงสำหรับแต่ละประเภทของคอมโพนันต์
WorkManager เป็นโซลูชั่นที่ Google แนะนำสำหรับงานเบื้องหลัง มันรับประกันการทำงานของงานในเธรดเบื้องหลังโดยคำนึงถึงสถานะของอุปกรณ์ แตกต่างจาก Service, WorkManager ไม่บล็อกเธรดหลักและทนต่อการเริ่มต้นแอพใหม่ สำหรับ BroadcastReceiver, ใช้ goAsync() และส่ง PendingResult ไปยัง WorkManager
เริ่มคำขอเครือข่ายทั้งหมด, การดำเนินการฐานข้อมูล และ I/O ไฟล์ด้วย Dispatchers.IO เธรดหลักควรอัพเดต UI เท่านั้น ใช้ viewModelScope สำหรับการยกเลิก coroutine โดยอัตโนมัติเมื่อ Activity ถูกทำลาย หลีกเลี่ยง runBlocking() ในทุกบริบท — มันบล็อกเธรดปัจจุบันแบบซิงโครนัส
หาก ContentProvider ทำการเริ่มต้นที่ช้า, ใช้กลไกการโหลดแบบขีเฉือย: สร้างโปรไวเดอร์ที่ส่งคืนข้อมูลทันทีและเริ่มการเริ่มต้นที่หนักผ่าน WorkManager โดยการหน่วงเวลา ซึ่งป้องกัน ANR ในการเริ่มต้นแอพ เมื่อระบบอ่อนไหวต่อความล่าช้ามากที่สุด
ตัวอย่างการใช้งาน BroadcastReceiver กับ goAsync ใน Android อย่างถูกต้อง:
class FcmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val pendingResult = goAsync()
WorkManager.getInstance(context!!)
.enqueue(OneTimeWorkRequest.from(NotificationWorker::class.java))
pendingResult.finish()
}
}
วิธีที่ดีที่สุดในการต่อสู้ ANR คือการป้องกันในระฮะการพัฒนาโดยใช้เครื่องมือและการตัดสินใจด้านสถาปัตยกรรม
StrictMode โดยมีนโยบาย detectNetwork() และ detectDiskReads()/detectDiskWrites() ที่เปิดใช้งานจะระบุ ANR ที่อาจเกิดขึ้นระหว่างการพัฒนา ในบิลด์ Debug, ตั้ง penaltyDeath — การละเมิดใดๆ จะทำให้เกิดการขัดข้องทันที และนักพัฒนาจะเห็นปัญหาก่อนคอมมิต
Firebase Performance ติดตามเวลาดำเนินการของการดำเนินการหลัก และแสดงว่าสถานการณ์ใดเกินเกณฑ์ ANR ตั้งค่าการติดตามแบบกำหนดเองสำหรับแต่ละหน้าจอ และคำขอเครือข่าย หากเวลาดำเนินการเกิน 3 วินาที — แสดงว่าเป็น ANR ที่อาจเกิดขึ้นและต้องการการเพิ่มเปิ้าหมาย
จำลองสภาพแวดล้อมที่ช้า: จำกัดความเร็วเครือข่ายโดยใช้ Network Link Conditioner บน iOS หรือ Android Emulator ทำให้การอ่านดิสก์ช้าลงโดยการจำลองหน่วยความจำที่ช้า ANR มักเกิดขึ้นในสภาพแวดล้อมเหล่านี้ บนอุปกรณ์นักพัฒนาที่เร็ว จะไม่เห็นปัญหาเหล่านี้
คำถามที่พบบ่อย
Android ติดตามเวลาการประมวลผลเหตุการณ์บนเธรดหลักอย่างชัดเจนและแสดงกล่องโต้ตอบสนอง ANR iOS ใช้ Watchdog ซึ่งปิดแอพอย่างบังคับเมื่อมันค้างนานกว่า 10–20 วินาที ANR เป็นลักษณะของสถาปัตยกรรม Android ที่ส่วนประกอบหลายอย่าง (BroadcastReceiver, Service) มีการหมดเวลาที่เข้มงวด
บน Android 11+ คุณสามารถรับ ANR dump ผ่าน adb shell dumpsys dropbox --print data_app_anr บน Android 10 และต่ำกว่า, ไม่สามารถเข้าถึง /data/anr/traces.txt ได้โดยไม่มีสิทธิผู้ใช้ระดับสูง ใช้ Firebase Crashlytics — มันจะรวบรวมรายงาน ANR โดยอัตโนมัติสำหรับ Android 11+
Google Play แนะนำอัตรา ANR ต่ำกว่า 0.5% — ไม่เกิน 5 ANR ต่อ 1000 เซสชั่น แอพที่มีอัตราเกิน 1% จะได้รับคำเตือนใน Google Play Console และอาจถูกซ่อนจากคำแนะนำ อย่างในอุดมคติ อัตรา ANR ควรต่ำกว่า 0.1%
coroutine เองไม่ได้บล็อกเธรด แต่หากภายใน coroutine คุณเริ่มทำงาน runBlocking บนเธรดหลักหรือ coroutine ถูกเริ่มด้วย Dispatchers.Main และดำเนินการ CPU ที่ยาว — สิ่งนี้จะทำให้เกิด ANR ใช้ Dispatchers.IO สำหรับ I/O และ Dispatchers.Default สำหรับการคำนวณ
ใช้ Android Emulator ที่มีโปรไฟล์ “Slow Network” หรือเขียนการทดสอบที่เรียก Thread.sleep(6000) บนเธรดหลัก เริ่มแอผ่าน Debug และหลังจาก 5 วินาที คุณจะเห็นกล่องโต้ตอบสนอง ANR ตรวจสอบว่า logcat แสดงรายการ ANR พร้อมการติดตาม
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม