ANR

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

กล่องโต้ตอบ 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