Android - ฉันจะตรวจสอบ ANR ได้อย่างไร


153

มีวิธีหาที่แอปของฉันโยน ANR (แอปพลิเคชันไม่ตอบกลับ) หรือไม่ ฉันดูที่ไฟล์ traces.txt ใน / data และฉันเห็นร่องรอยสำหรับแอปพลิเคชันของฉัน นี่คือสิ่งที่ฉันเห็นในร่องรอย

DALVIK THREADS:
"main" prio=5 tid=3 TIMED_WAIT
  | group="main" sCount=1 dsCount=0 s=0 obj=0x400143a8
  | sysTid=691 nice=0 sched=0/0 handle=-1091117924
  at java.lang.Object.wait(Native Method)
  - waiting on <0x1cd570> (a android.os.MessageQueue)
  at java.lang.Object.wait(Object.java:195)
  at android.os.MessageQueue.next(MessageQueue.java:144)
  at android.os.Looper.loop(Looper.java:110)
  at android.app.ActivityThread.main(ActivityThread.java:3742)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:515)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
  at dalvik.system.NativeStart.main(Native Method)

"Binder Thread #3" prio=5 tid=15 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x434e7758
  | sysTid=734 nice=0 sched=0/0 handle=1733632
  at dalvik.system.NativeStart.run(Native Method)

"Binder Thread #2" prio=5 tid=13 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x433af808
  | sysTid=696 nice=0 sched=0/0 handle=1369840
  at dalvik.system.NativeStart.run(Native Method)

"Binder Thread #1" prio=5 tid=11 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x433aca10
  | sysTid=695 nice=0 sched=0/0 handle=1367448
  at dalvik.system.NativeStart.run(Native Method)

"JDWP" daemon prio=5 tid=9 VMWAIT
  | group="system" sCount=1 dsCount=0 s=0 obj=0x433ac2a0
  | sysTid=694 nice=0 sched=0/0 handle=1367136
  at dalvik.system.NativeStart.run(Native Method)

"Signal Catcher" daemon prio=5 tid=7 RUNNABLE
  | group="system" sCount=0 dsCount=0 s=0 obj=0x433ac1e8
  | sysTid=693 nice=0 sched=0/0 handle=1366712
  at dalvik.system.NativeStart.run(Native Method)

"HeapWorker" daemon prio=5 tid=5 VMWAIT
  | group="system" sCount=1 dsCount=0 s=0 obj=0x4253ef88
  | sysTid=692 nice=0 sched=0/0 handle=1366472
  at dalvik.system.NativeStart.run(Native Method)

----- end 691 -----

ฉันจะทราบได้อย่างไรว่าปัญหาอยู่ที่ใด เมธอดในการติดตามคือเมธอด SDK ทั้งหมด

ขอบคุณ


2
android.os.MessageQueue.nativePollOnce(Native Method)ฉันมีหนึ่งรายงานของชนิดนี้ยังเกิดขึ้นที่ ฉันสามารถเพิกเฉยได้อย่างปลอดภัยหรือไม่?
rds

คำตอบ:


124

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

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

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


6
ปัญหาเดียวคือ "ทำซ้ำ ANR" :-) คุณช่วยอธิบายได้ไหมว่าสแต็กการติดตามสแต็กของเธรดหลักคือ 'idling' ซึ่งจะดีมาก
Blundell

20
การติดตามสแต็กแสดงให้เห็นว่าเธรดหลักอยู่ใน Looper (การใช้งานลูปข้อความ) และทำการรอเวลาผ่าน Object.wait ซึ่งหมายความว่าลูปข้อความไม่ได้มีข้อความใด ๆ ที่จะจัดส่งและกำลังรอข้อความใหม่ที่จะเข้ามา ANR เกิดขึ้นเมื่อระบบตระหนักถึงลูปข้อความจะใช้เวลามากในการประมวลผลข้อความและไม่ประมวลผลข้อความอื่น ๆ ใน คิว. หากลูปกำลังรอข้อความแสดงว่าสิ่งนี้ไม่ได้เกิดขึ้น
sooniln

3
@Silil สวัสดีคุณรู้ไหมว่าส่วนที่เหลือหมายถึงอะไรเช่น Binder thread 3, Binder thread 2 JDWP demon prio 5. sCount, dsCount, obj, sysTid, กำหนดการดีหมายถึงอะไร นอกจากนี้ยังมีข้อมูลเช่น VMWAIT, RUNNABLE, NATIVE
minhaz

1
แอพของฉันเป็นแบบ NDK ฉันเห็น ANR เดียวกัน นอกจากนี้เธรดหลักก็ใช้ได้ ฉันลอง DDMS และรีเฟรชเธรดคนงานของฉันเมื่อมันค้าง น่าเสียดายที่ฉันได้รับเพียงบรรทัดเดียว NativeStart :: run มุมมองเธรด DDMS เป็นความสามารถในการตรวจสอบเธรด NDK ดั้งเดิมหรือไม่ นอกจากนี้: StrictMode ไม่พบอะไรเลย
Bram

6
ดูelliotth.blogspot.com/2012/08/…สำหรับคำอธิบายที่ดีเกี่ยวกับผลลัพธ์
เร็ว ๆ นี้

96

คุณสามารถเปิดใช้งานStrictModeในระดับ API 9 ขึ้นไป

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

public void onCreate() {
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                           .detectAll()
                           .penaltyLog()
                           .penaltyDeath()
                           .build());
    super.onCreate();
}

การใช้งานpenaltyLog()คุณสามารถดูผลลัพธ์ของ adb logcat ในขณะที่คุณใช้แอปพลิเคชันของคุณเพื่อดูการละเมิดที่เกิดขึ้น


StrictMode ไม่สามารถแก้ไขเป็นประเภท มีอะไรที่ฉันต้องนำเข้าก่อน การกด CTRL + SHIFT + O ช่วยไม่ได้
kuchi

23
เคล็ดลับเล็ก ๆ - ใช้ถ้า (BuildConfig.DEBUG) ... เพื่อป้องกันการรวมไว้ในการผลิต
Amir Uval

@uval คุณหมายถึงอะไร "เพื่อป้องกันการรวมอยู่ในการผลิต"? !!
Muhammed Refaat

2
@MuhammedRefaat มันไม่ได้ป้องกัน ANR ใด ๆ แอปจะพังทันทีแทนที่จะเป็นหลังจาก 5 วินาที ตัวอย่างเช่นหากคุณเข้าถึงฐานข้อมูลบนเธรดหลักและใช้เวลา 2 วินาทีคุณจะไม่ได้รับ ANR แต่ StrictMode จะทำให้แอปเสียหาย StrictMode เป็นขั้นตอนการดีบักของคุณอย่างเคร่งครัดไม่ใช่การผลิต
Amir Uval

1
@MuhammedRefaat เพิ่มคำตอบของฉันในคำถามของคุณ
Amir Uval

80

คุณกำลังสงสัยว่างานใดที่ถือเธรด UI ไฟล์การติดตามจะให้คำแนะนำแก่คุณในการค้นหางาน คุณต้องตรวจสอบสถานะของแต่ละเธรด

สถานะของเธรด

  • วิ่ง - รันโค้ดแอปพลิเคชัน
  • sleep - เรียกว่า Thread.sleep ()
  • จอภาพ - รอรับการล็อคจอภาพ
  • รอ - ใน Object.wait ()
  • native - รันโค้ดเนทีฟ
  • vmwait - กำลังรอทรัพยากร VM
  • zombie - เธรดกำลังอยู่ระหว่างการตาย
  • init - เธรดกำลังเริ่มต้น (คุณไม่ควรเห็นสิ่งนี้)
  • เริ่มต้น - เธรดกำลังจะเริ่มต้น (คุณไม่ควรเห็นสิ่งนี้)

มุ่งเน้นไปที่สถานะ MONITOR ที่ถูกระงับ สถานะมอนิเตอร์บ่งชี้ว่าเธรดใดถูกตรวจสอบและสถานะ SUSPENDED ของเธรดอาจเป็นสาเหตุหลักของการหยุดชะงัก

ขั้นตอนการตรวจสอบขั้นพื้นฐาน

  1. ค้นหา "กำลังรอล็อค"
    • คุณสามารถค้นหาสถานะของจอภาพ"Binder Thread # 15" prio = 5 tid = 75 MONITOR
    • คุณโชคดีถ้าพบว่า "กำลังรอล็อค"
    • ตัวอย่าง: รอเพื่อล็อค <0xblahblah> (com.foo.A) ที่จัดขึ้นโดย threadid = 74
  2. คุณสามารถสังเกตเห็นว่า "tid = 74" ถืองานตอนนี้ ดังนั้นไป tid = 74
  3. tid = 74 อาจจะถูกระงับสถานะ! หาเหตุผลหลัก!

การสืบค้นกลับไม่ประกอบด้วย "กำลังรอการล็อค" ในกรณีนี้มันยากที่จะหาเหตุผลหลัก


1
คำอธิบายที่ดี ตอนนี้ฉันเข้าใจการบันทึก ANR ได้ง่ายขึ้น แต่ฉันยังคงมีปัญหาที่จะเข้าใจสาเหตุเนื่องจากในขั้นตอนที่ 1 ฉันสามารถค้นหาเธรด id ได้อย่างง่ายดาย แต่เมื่อในขั้นตอนที่ 2 ฉันพยายามไปที่ที่เป็นอยู่เพื่อตรวจสอบสถานะฉันไม่พบ . มีความคิดว่าจะดำเนินการอย่างไร?
THZ

1
ฉันมีอยู่ภายใน- waiting to lock an unknown object "HeapTaskDaemon" daemon prio=5 tid=8 Blocked บางคนสามารถช่วยอะไรได้
Hilal

13

ฉันได้เรียนรู้เกี่ยวกับหุ่นยนต์ในช่วงสองสามเดือนที่ผ่านมาดังนั้นฉันจึงยังห่างไกลจากผู้เชี่ยวชาญ แต่ฉันผิดหวังกับเอกสารของ ANRs จริงๆ

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

มีสามสิ่งที่คุณต้องการค้นหาด้วยบันทึก ANR

1) Deadlocks: เมื่อเธรดอยู่ในสถานะ WAIT คุณสามารถดูรายละเอียดเพื่อค้นหาว่าใครคือ "holdby =" ส่วนใหญ่มันจะถูกจัดขึ้นโดยตัวของมันเอง แต่ถ้ามันถูกยึดโดยเธรดอื่นนั่นน่าจะเป็นสัญญาณอันตราย ไปดูที่หัวข้อนั้นและดูสิ่งที่มันจัดขึ้น คุณอาจพบลูปซึ่งเป็นสัญญาณที่ชัดเจนว่ามีบางอย่างผิดปกติ มันค่อนข้างหายาก แต่มันเป็นจุดแรกเพราะเมื่อมันเกิดขึ้นมันเป็นฝันร้าย

2) เธรดหลักกำลังรอ: หากเธรดหลักของคุณอยู่ในสถานะ WAIT ให้ตรวจสอบว่าเธรดนั้นอยู่ในเธรดอื่นหรือไม่ สิ่งนี้ไม่ควรเกิดขึ้นเนื่องจากเธรด UI ของคุณไม่ควรถูกจัดไว้โดยเธรดพื้นหลัง

ทั้งสองสถานการณ์หมายความว่าคุณต้องทำงานซ้ำรหัสของคุณอย่างมีนัยสำคัญ

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

สุดท้ายและฉันขอโทษสำหรับลงคอเสียบรหัสของตัวเองคุณสามารถใช้วิเคราะห์เข้าสู่ระบบหลามที่ผมเขียนที่https://github.com/HarshEvilGeek/Android-Log-Analyzerนี้จะผ่านไปล็อกไฟล์ของคุณไฟล์ ANR เปิดหา การหยุดชะงักค้นหาการรอเธรดหลักค้นหาข้อยกเว้นที่ไม่ได้ตรวจสอบในบันทึกตัวแทนของคุณและพิมพ์ออกมาทั้งหมดบนหน้าจอในลักษณะที่อ่านง่าย อ่านไฟล์ ReadMe (ซึ่งฉันกำลังจะเพิ่ม) เพื่อเรียนรู้วิธีใช้งาน มันช่วยฉันได้มากในสัปดาห์ที่ผ่านมา!


4

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

ทางออกที่ดีที่สุดของคุณคือการแทรกการโทรจำนวนมาก (Log.XXX ()) ลงในเธรดและการโทรกลับต่าง ๆ ของแอปและดูว่าการล่าช้านั้นอยู่ที่ไหน หากคุณต้องการ stacktrace ให้สร้างข้อยกเว้นใหม่ (เพียงยกตัวอย่างหนึ่ง) และบันทึก


2
ขอบคุณสำหรับคำแนะนำในการสร้างข้อยกเว้นใหม่หากคุณต้องการ stacktrace ที่เป็นประโยชน์มากเมื่อการแก้จุดบกพร่อง :)
Kuchi

3

ทริกเกอร์ ANR คืออะไร?

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

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

วิธีหลีกเลี่ยง ANR

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

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

รหัส: เธรดผู้ปฏิบัติงานด้วยคลาส AsyncTask

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    // Do the long-running work in here
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

    // This is called each time you call publishProgress()
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    // This is called when doInBackground() is finished
    protected void onPostExecute(Long result) {
        showNotification("Downloaded " + result + " bytes");
    }
}

รหัส: เรียกใช้งานเธรด

ในการรันเธรดผู้ปฏิบัติงานนี้เพียงแค่สร้างอินสแตนซ์และเรียกใช้ execute ():

new DownloadFilesTask().execute(url1, url2, url3);

แหล่ง

http://developer.android.com/training/articles/perf-anr.html


1

ปัญหาของฉันกับ ANR หลังจากทำงานมากฉันพบว่าเธรดกำลังเรียกใช้ทรัพยากรที่ไม่มีอยู่ในเค้าโครงแทนที่จะส่งคืนข้อยกเว้นฉันได้รับ ANR ...


นั่นแปลกมาก
Nilabja


0

พื้นฐานใน @Horyun Lee คำตอบฉันเขียนสคริปต์หลามขนาดเล็กเพื่อช่วยตรวจสอบ ANR จากtraces.txtเพื่อช่วยในการตรวจสอบจาก

ANRs จะแสดงผลเป็นกราฟิกโดยgraphvizหากคุณติดตั้งgrapvhvizในระบบของคุณ

$ ./anr.py --format png ./traces.txt

PNG ออกจะเหมือนด้านล่างหากมี ANRs traces.txtตรวจพบในไฟล์ มันใช้งานง่ายมากขึ้น

ป้อนคำอธิบายรูปภาพที่นี่

ตัวอย่างtraces.txtไฟล์ที่ใช้ดังกล่าวข้างต้นเป็นได้รับจากที่นี่


0

พิจารณาใช้ไลบรารีANR-Watchdogเพื่อติดตามและจับภาพร่องรอยสแต็ค ANR อย่างถูกต้องในรายละเอียดระดับสูง จากนั้นคุณสามารถส่งพวกเขาไปยังห้องสมุดรายงานข้อผิดพลาดของคุณ ฉันแนะนำให้ใช้setReportMainThreadOnly()ในสถานการณ์นี้ คุณสามารถทำให้แอปมีข้อยกเว้นที่ไม่ร้ายแรงถึงจุดเยือกแข็งหรือบังคับให้แอปหยุดทำงานเมื่อ ANR เกิดขึ้น

โปรดทราบว่ารายงาน ANR มาตรฐานที่ส่งไปยังคอนโซลนักพัฒนาซอฟต์แวร์ Google Play ของคุณมักไม่แม่นยำพอที่จะระบุปัญหาที่แน่นอน นั่นเป็นเหตุผลที่จำเป็นต้องใช้ห้องสมุดบุคคลที่สาม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.