ANR

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

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

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

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

ANR จะทริกเกอร์แอปของคุณเมื่อเงื่อนไขข้อใดข้อหนึ่งต่อไปนี้เกิดขึ้น

  • หมดเวลาการส่งอินพุต: หากแอปไม่ตอบสนองต่ออินพุต เหตุการณ์ (เช่น การกดแป้นหรือหน้าจอสัมผัส) ภายใน 5 วินาที
  • การเรียกใช้บริการ: หากบริการที่แอปของคุณประกาศไว้ดำเนินการไม่เสร็จสิ้น กำลังเรียกใช้ Service.onCreate() และ Service.onStartCommand()/Service.onBind() ภายในไม่กี่วินาที
  • ไม่ได้เรียก Service.startForeground(): หากแอปใช้ Context.startForegroundService() เพื่อเริ่มบริการใหม่ในเบื้องหน้า แต่บริการจะไม่เรียกใช้ startForeground() ภายใน 5 วินาที
  • ประกาศความตั้งใจ: หาก 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 เวอร์ชันเก่า (Android 10 และต่ำกว่า) ซึ่งต่างจาก Android Vitals

Android Vitals

Android Vitals ช่วยคุณตรวจสอบและปรับปรุงอัตรา ANR ของแอปได้ Android Vitals วัดอัตรา ANR หลายอัตราดังนี้

  • อัตรา ANR: เปอร์เซ็นต์ของผู้ใช้ที่ใช้งานอยู่รายวันที่ พบ ANR ทุกประเภท
  • อัตรา ANR ที่ผู้ใช้รับรู้: เปอร์เซ็นต์ของผู้ใช้ที่ใช้งานอยู่รายวัน ที่พบ ANR ที่ผู้ใช้รับรู้อย่างน้อย 1 ครั้ง ขณะนี้เฉพาะ ANR ของ ประเภท Input dispatching timed out จะถือว่าผู้ใช้รับรู้
  • อัตรา ANR หลายรายการ: เปอร์เซ็นต์ของผู้ใช้ที่ใช้งานอยู่รายวันซึ่ง พบ ANR อย่างน้อย 2 ครั้ง

ผู้ใช้ที่ใช้งานอยู่รายวันคือผู้ใช้ที่ไม่ซ้ำที่ใช้แอปของคุณ ในวันเดียวบนอุปกรณ์เดียว ซึ่งอาจมีมากกว่าหลายเซสชัน หากผู้ใช้ใช้แอปของคุณบนอุปกรณ์มากกว่า 1 เครื่องใน 1 วัน อุปกรณ์แต่ละเครื่องจะรวม กับจำนวนผู้ใช้ที่ใช้งานอยู่ในวันนั้น หากมีผู้ใช้หลายคนใช้อุปกรณ์เดียวกันในวันเดียวกัน ก็จะนับเป็นผู้ใช้ที่ใช้งานอยู่ 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 เทรดหลักไม่ต้องรอนาน ดำเนินการให้เสร็จสิ้น แต่อยู่ในสถานการณ์ติดตาย สำหรับข้อมูลเพิ่มเติม ดู Deadlock บน Wikipedia

เทคนิคต่อไปนี้จะช่วยคุณระบุสาเหตุของ ANR ได้

สถิติสุขภาพ

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

แก้ไขข้อบกพร่อง

Debug ช่วยตรวจสอบแอปพลิเคชัน Android ระหว่างการพัฒนา รวมทั้งการติดตามและการจัดสรรที่จะนับเพื่อระบุการกระตุก และความล่าช้าในการใช้แอป คุณยังใช้ Debug เพื่อรับรันไทม์และหน่วยความจำในเครื่องได้ด้วย ตัวนับและเมตริกหน่วยความจำที่ช่วยระบุร่องรอยหน่วยความจำได้ ของกระบวนการหนึ่งๆ โดยเฉพาะ

ข้อมูลการออกของแอปพลิเคชัน

ApplicationExitInfo ใช้ได้ ใน Android 11 (API ระดับ 30) ขึ้นไป และให้ข้อมูลเกี่ยวกับ เหตุผลที่ผู้ใช้ออกจากแอปพลิเคชัน ซึ่งได้แก่ ANR, หน่วยความจำต่ำ, ข้อขัดข้องของแอป การใช้งาน CPU มากเกินไป การหยุดชะงักของผู้ใช้ การหยุดชะงักของระบบ หรือรันไทม์ การเปลี่ยนแปลงสิทธิ์

โหมดจำกัด

การใช้ StrictMode จะช่วยให้คุณค้นพบ การดําเนินการ I/O โดยไม่ตั้งใจในเทรดหลักขณะที่คุณพัฒนาแอป คุณสามารถใช้ StrictMode ที่ระดับแอปพลิเคชันหรือกิจกรรม

เปิดใช้กล่องโต้ตอบ ANR เบื้องหลัง

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

Traceview

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

ดึงไฟล์การติดตาม

Android จัดเก็บข้อมูลการติดตามเมื่อพบ ANR ในระบบปฏิบัติการเวอร์ชันเก่า จะมีไฟล์ /data/anr/traces.txt ไฟล์เดียวในอุปกรณ์ ในระบบปฏิบัติการรุ่นใหม่ๆ จะมีไฟล์ /data/anr/anr_* หลายไฟล์ คุณสามารถเข้าถึงการติดตาม ANR จากอุปกรณ์หรือโปรแกรมจำลองได้โดยใช้ Android Debug Bridge (adb) เป็นรูท:

adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>

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

แก้ไขปัญหา

หลังจากที่คุณระบุปัญหาได้แล้ว คุณสามารถใช้เคล็ดลับในส่วนนี้เพื่อ แก้ไขปัญหาที่พบได้ทั่วไป

โค้ดที่ช้าในเทรดหลัก

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

ตัวอย่างเช่น รูปที่ 2 แสดงไทม์ไลน์ Traceview ที่เทรดหลักไม่ว่าง นานกว่า 5 วินาที

รูปที่ 2 ไทม์ไลน์มุมมองการติดตามแสดงเส้นทางหลักที่ไม่ว่าง
ชุดข้อความ

รูปที่ 2 ไทม์ไลน์มุมมองการติดตามที่แสดงชุดข้อความหลักที่ไม่ว่าง

รูปที่ 2 แสดงให้เห็นว่าโค้ดที่ไม่เหมาะสมส่วนใหญ่เกิดขึ้นใน onClick(View) ดังแสดงในตัวอย่างโค้ดต่อไปนี้

Kotlin

override fun onClick(v: View) {
    // This task runs on the main thread.
    BubbleSort.sort(data)
}

Java

@Override
public void onClick(View view) {
    // This task runs on the main thread.
    BubbleSort.sort(data);
}

ในกรณีนี้ คุณควรย้ายงานที่ทำงานในเทรดหลักไปยังผู้ปฏิบัติงาน ชุดข้อความ เฟรมเวิร์ก Android มีคลาสที่ช่วยย้ายงานได้ ไปยังชุดข้อความของผู้ปฏิบัติงาน ดูผู้ปฏิบัติงาน ชุดข้อความเพิ่มเติม

IO ในชุดข้อความหลัก

การดำเนินการ IO ในเทรดหลักเป็นสาเหตุที่พบบ่อยที่ทำให้การดำเนินการล่าช้า ในเทรดหลัก ซึ่งอาจทำให้เกิด ANR เราขอแนะนำให้ย้าย IO ทั้งหมด การดำเนินการกับชุดข้อความของผู้ปฏิบัติงานตามที่แสดงในส่วนก่อนหน้า

ตัวอย่างบางส่วนของการดำเนินการ IO คือการดำเนินการเครือข่ายและพื้นที่เก็บข้อมูล สำหรับข้อมูลเพิ่มเติม โปรดดูที่เครือข่ายที่มีประสิทธิภาพ การดำเนินการ และการบันทึก ข้อมูล

การช่วงชิงล็อก

ในบางกรณี งานที่ทำให้เกิด ANR จะไม่ดำเนินการโดยตรงใน เทรดหลักของแอป หากเทรดผู้ปฏิบัติงานมีการล็อกในทรัพยากรที่หลัก เทรดต้องทำงานให้เสร็จสมบูรณ์ก่อน อาจทำให้เกิด ANR

ตัวอย่างเช่น รูปที่ 3 แสดงไทม์ไลน์ Traceview ซึ่งงานส่วนใหญ่ ดำเนินการกับชุดข้อความของผู้ปฏิบัติงาน

รูปที่ 3 ไทม์ไลน์มุมมองการติดตามที่แสดงงานที่กำลังดำเนินการกับผู้ปฏิบัติงาน
ชุดข้อความ

รูปที่ 3 ไทม์ไลน์มุมมองการติดตามที่แสดงงานที่กำลังดำเนินการกับผู้ปฏิบัติงาน ชุดข้อความ

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

แต่หากเทรดหลักดำเนินการต่อไม่ได้ แสดงว่าเทรดหลักจะอยู่ใน สถานะ BLOCKED และตอบสนองต่อกิจกรรมไม่ได้ สถานะจะแสดงในการตรวจสอบอุปกรณ์ Android เป็น ตรวจสอบหรือรอ ดังที่แสดงในรูปที่ 5

รูปที่ 4 เทรดหลักในการตรวจสอบ
สถานะ

รูปที่ 4 เทรดหลักในสถานะการตรวจสอบ

การติดตามต่อไปนี้แสดงเทรดหลักของแอปที่ถูกบล็อกขณะรอ แหล่งข้อมูล:

...
AsyncTask #2" prio=5 tid=18 Runnable
  | group="main" sCount=0 dsCount=0 obj=0x12c333a0 self=0x94c87100
  | sysTid=25287 nice=10 cgrp=default sched=0/0 handle=0x94b80920
  | state=R schedstat=( 0 0 0 ) utm=757 stm=0 core=3 HZ=100
  | stack=0x94a7e000-0x94a80000 stackSize=1038KB
  | held mutexes= "mutator lock"(shared held)
  at com.android.developer.anrsample.BubbleSort.sort(BubbleSort.java:8)
  at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:147)
  - locked <0x083105ee> (a java.lang.Boolean)
  at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:135)
  at android.os.AsyncTask$2.call(AsyncTask.java:305)
  at java.util.concurrent.FutureTask.run(FutureTask.java:237)
  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
  at java.lang.Thread.run(Thread.java:761)
...

การตรวจสอบการติดตามจะช่วยให้คุณหาโค้ดที่บล็อกเทรดหลักได้ รหัสต่อไปนี้มีหน้าที่ในการระงับล็อกที่บล็อกรหัสหลัก เทรดในการติดตามก่อนหน้า:

Kotlin

override fun onClick(v: View) {
    // The worker thread holds a lock on lockedResource
    LockTask().execute(data)

    synchronized(lockedResource) {
        // The main thread requires lockedResource here
        // but it has to wait until LockTask finishes using it.
    }
}

class LockTask : AsyncTask<Array<Int>, Int, Long>() {
    override fun doInBackground(vararg params: Array<Int>): Long? =
            synchronized(lockedResource) {
                // This is a long-running operation, which makes
                // the lock last for a long time
                BubbleSort.sort(params[0])
            }
}

Java

@Override
public void onClick(View v) {
    // The worker thread holds a lock on lockedResource
   new LockTask().execute(data);

   synchronized (lockedResource) {
       // The main thread requires lockedResource here
       // but it has to wait until LockTask finishes using it.
   }
}

public class LockTask extends AsyncTask<Integer[], Integer, Long> {
   @Override
   protected Long doInBackground(Integer[]... params) {
       synchronized (lockedResource) {
           // This is a long-running operation, which makes
           // the lock last for a long time
           BubbleSort.sort(params[0]);
       }
   }
}

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

Kotlin

fun onClick(v: View) {
    val lock = java.lang.Object()
    val waitTask = WaitTask(lock)
    synchronized(lock) {
        try {
            waitTask.execute(data)
            // Wait for this worker thread’s notification
            lock.wait()
        } catch (e: InterruptedException) {
        }
    }
}

internal class WaitTask(private val lock: java.lang.Object) : AsyncTask<Array<Int>, Int, Long>() {
    override fun doInBackground(vararg params: Array<Int>): Long? {
        synchronized(lock) {
            BubbleSort.sort(params[0])
            // Finished, notify the main thread
            lock.notify()
        }
    }
}

Java

public void onClick(View v) {
   WaitTask waitTask = new WaitTask();
   synchronized (waitTask) {
       try {
           waitTask.execute(data);
           // Wait for this worker thread’s notification
           waitTask.wait();
       } catch (InterruptedException e) {}
   }
}

class WaitTask extends AsyncTask<Integer[], Integer, Long> {
   @Override
   protected Long doInBackground(Integer[]... params) {
       synchronized (this) {
           BubbleSort.sort(params[0]);
           // Finished, notify the main thread
           notify();
       }
   }
}

มีสถานการณ์อื่นๆ ที่บล็อกเทรดหลักได้ ซึ่งรวมถึง ชุดข้อความที่ใช้ Lock Semaphore, และระดับทรัพยากร (เช่น แหล่งรวมการเชื่อมต่อฐานข้อมูล) หรือกลไกการยกเว้นร่วมกัน (หลายฝ่าย) อื่นๆ

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

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

เดดล็อก

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

การตายเป็นปรากฏการณ์ที่มีการศึกษากันเป็นอย่างดีในแวดวงวิทยาการคอมพิวเตอร์ อัลกอริทึมการป้องกันการติดตายที่คุณสามารถใช้เพื่อหลีกเลี่ยงการติดตาย

ดูข้อมูลเพิ่มเติมได้ที่การหยุดทำงานและ การป้องกันการหยุดทำงาน อัลกอริทึมเปิดอยู่ Wikipedia

Broadcast Receiver ที่ช้า

แอปสามารถตอบสนองต่อข้อความที่เผยแพร่ เช่น เปิดหรือปิดใช้เครื่องบิน หรือการเปลี่ยนแปลงสถานะการเชื่อมต่อ โดยใช้ Broadcast Receiver ANR เกิดขึ้นเมื่อแอปใช้เวลานานเกินไปในการประมวลผลข้อความประกาศ

ANR เกิดขึ้นในกรณีต่อไปนี้

  • Broadcast Receiver ยังดำเนินการไม่เสร็จสิ้น onReceive() ภายในระยะเวลาที่กำหนด
  • สายเรียกเข้าจาก Broadcast Receiver goAsync() เรียกใช้ไม่ได้ finish() ใน PendingResult ออบเจ็กต์

แอปของคุณควรดำเนินการสั้นๆ เฉพาะใน onReceive() วิธีการ BroadcastReceiver แต่หากแอปของคุณต้องใช้สิทธิ์ที่ซับซ้อนมากขึ้น เป็นผลของข้อความประกาศ คุณควรเลื่อนงานออกไปให้ IntentService

คุณสามารถใช้เครื่องมือ เช่น Traceview เพื่อระบุได้ว่า Broadcast Receiver ประมวลผลหรือไม่ การดำเนินการที่ใช้เวลานานในเทรดหลักของแอป เช่น รูปที่ 6 แสดง ไทม์ไลน์ของ Broadcast Receiver ที่ประมวลผลข้อความในชุดข้อความหลัก เป็นเวลาประมาณ 100 วินาที

รูปที่ 5 ไทม์ไลน์มุมมองการติดตามแสดงงาน &quot;BroadcastReceiver&quot; บนอุปกรณ์หลัก
ชุดข้อความ

รูปที่ 5 ไทม์ไลน์มุมมองการติดตามแสดงผลงาน BroadcastReceiver ใน เทรดหลัก

การทำงานลักษณะนี้อาจเกิดจากการดำเนินการที่ใช้เวลานานใน onReceive() ของวิธีการ BroadcastReceiver, ดังที่ปรากฏในตัวอย่างต่อไปนี้

Kotlin

override fun onReceive(context: Context, intent: Intent) {
    // This is a long-running operation
    BubbleSort.sort(data)
}

Java

@Override
public void onReceive(Context context, Intent intent) {
    // This is a long-running operation
    BubbleSort.sort(data);
}

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

Kotlin

override fun onReceive(context: Context, intent: Intent) {
    Intent(context, MyIntentService::class.java).also { intentService ->
        // The task now runs on a worker thread.
        context.startService(intentService)
    }
}

class MyIntentService : IntentService("MyIntentService") {
    override fun onHandleIntent(intent: Intent?) {
        BubbleSort.sort(data)
    }
}

Java

@Override
public void onReceive(Context context, Intent intent) {
    // The task now runs on a worker thread.
    Intent intentService = new Intent(context, MyIntentService.class);
    context.startService(intentService);
}

public class MyIntentService extends IntentService {
   @Override
   protected void onHandleIntent(@Nullable Intent intent) {
       BubbleSort.sort(data);
   }
}

จากการใช้ IntentService การทดสอบระยะยาว ระบบจะดำเนินการในเทรดของผู้ปฏิบัติงานแทนเทรดหลัก ภาพที่ 7 แสดงงานที่เลื่อนไปยังเทรดผู้ปฏิบัติงานในไทม์ไลน์ Traceview

รูปที่ 6 ไทม์ไลน์มุมมองการติดตามที่แสดงข้อความประกาศที่ประมวลผลใน
เทรดผู้ปฏิบัติงาน

รูปที่ 6 ไทม์ไลน์มุมมองการติดตามที่แสดงข้อความประกาศที่ประมวลผลใน เทรดผู้ปฏิบัติงาน

Broadcast Receiver สามารถใช้ goAsync() เพื่อส่งสัญญาณแจ้งว่าต้องการเวลาเพิ่มเติมในการประมวลผลข้อความ อย่างไรก็ตาม เธอควรโทรหา finish() ใน PendingResult ออบเจ็กต์ ตัวอย่างต่อไปนี้จะแสดงวิธีการเรียกFinish() เพื่อให้ระบบ นำ Broadcast Receiver มารีไซเคิลและหลีกเลี่ยง ANR

Kotlin

val pendingResult = goAsync()

object : AsyncTask<Array<Int>, Int, Long>() {
    override fun doInBackground(vararg params: Array<Int>): Long? {
        // This is a long-running operation
        BubbleSort.sort(params[0])
        pendingResult.finish()
        return 0L
    }
}.execute(data)

Java

final PendingResult pendingResult = goAsync();
new AsyncTask<Integer[], Integer, Long>() {
   @Override
   protected Long doInBackground(Integer[]... params) {
       // This is a long-running operation
       BubbleSort.sort(params[0]);
       pendingResult.finish();
   }
}.execute(data);

อย่างไรก็ตาม การย้ายโค้ดจาก Broadcast Receiver แบบช้าไปยังเทรดอื่นและ กำลังใช้ goAsync() จะไม่แก้ไข ANR หากการออกอากาศอยู่ในเบื้องหลัง ระยะหมดเวลา ANR ยังคงมีผล

กิจกรรมเกม

ไลบรารี GameActivity ได้ลด ANR ใน กรณีศึกษาของเกมและแอปที่เขียน ใน C หรือ C++ หากคุณแทนที่กิจกรรมโฆษณาเนทีฟที่มีอยู่ด้วย GameActivity คุณจะลดการบล็อกชุดข้อความ UI และป้องกันไม่ให้เกิด ANR บางอย่างได้

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