โปรแกรมเฝ้าระวังง่ายๆ ที่ตรวจจับ ANR ของ Android (แอปพลิเคชันไม่ตอบสนอง)
ขณะนี้ยังไม่มีวิธีใดที่แอปพลิเคชัน Android จะตรวจจับและรายงานข้อผิดพลาด ANR
หากแอปพลิเคชันของคุณไม่ได้อยู่ใน Play Store (อาจเป็นเพราะคุณยังคงพัฒนาแอปพลิเคชันนั้นอยู่หรือเพราะคุณกำลังเผยแพร่แอปในรูปแบบอื่น) วิธีเดียวที่จะตรวจสอบ ANR ได้คือการดึงไฟล์ /data/anr/traces.txt
นอกจากนี้ เราพบว่าการใช้ Play Store ไม่มีประสิทธิภาพเท่ากับการเลือกบริการติดตามข้อบกพร่องของเราเอง
มีปัญหาในตัวติดตามบั๊กของ Android ที่อธิบายถึงการขาดนี้ ติดดาวไว้ได้เลย ;)
มันตั้งค่าตัวจับเวลา "จ้องจับผิด" ที่จะตรวจจับเมื่อเธรด UI หยุดตอบสนอง เมื่อเป็นเช่นนั้น จะทำให้เกิดข้อผิดพลาดพร้อมกับการติดตามสแต็กเธรดทั้งหมด (หลักอยู่ก่อน)
ใช่! ฉันดีใจที่คุณถาม: นั่นคือเหตุผลว่าทำไมมันถึงได้รับการพัฒนาตั้งแต่แรก!
เนื่องจากสิ่งนี้ทำให้เกิดข้อผิดพลาด ตัวจัดการข้อขัดข้องสามารถสกัดกั้นและจัดการได้ตามความต้องการ
ผู้รายงานข้อขัดข้องในการทำงานที่เป็นที่รู้จักได้แก่:
setReportMainThreadOnly()
)และไม่มีเหตุผลว่าทำไมจึงไม่ควรทำงานร่วมกับ [ใส่ระบบรายงานข้อขัดข้องที่คุณชื่นชอบที่นี่]
สุนัขเฝ้าบ้านเป็นเธรดธรรมดาที่ทำสิ่งต่อไปนี้ในวง:
ในไฟล์ app/build.gradle
ให้เพิ่ม:
implementation 'com.github.anrwatchdog:anrwatchdog:1.4.0'
ในคลาสแอปพลิเคชันของคุณ ใน onCreate
ให้เพิ่ม:
new ANRWatchDog (). start ();
ดาวน์โหลด jar ล่าสุด
วาง jar ไว้ในไดเร็กทอรี libs/
ของโปรเจ็กต์ของคุณ
การติดตามสแต็ก ANRError
มีความเฉพาะเจาะจงเล็กน้อย โดยมีการติดตามสแต็กของเธรดทั้งหมดที่ทำงานในแอปพลิเคชันของคุณ ดังนั้นในรายงาน แต่ละ caused by
ส่วนไม่ใช่สาเหตุของข้อยกเว้นที่เกิดขึ้นก่อนหน้า แต่เป็นการติดตามสแต็กของเธรดอื่น
นี่คือตัวอย่างการล็อคแบบตายตัว:
FATAL EXCEPTION: |ANR-WatchDog|
Process: anrwatchdog.github.com.testapp, PID: 26737
com.github.anrwatchdog.ANRError: Application Not Responding
Caused by: com.github.anrwatchdog.ANRError$_$_Thread: main (state = WAITING)
at testapp.MainActivity$1.run(MainActivity.java:46)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
Caused by: com.github.anrwatchdog.ANRError$_$_Thread: APP: Locker (state = TIMED_WAITING)
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:1031)
at java.lang.Thread.sleep(Thread.java:985)
at testapp.MainActivity.SleepAMinute(MainActivity.java:18)
at testapp.MainActivity.access$100(MainActivity.java:12)
at testapp.MainActivity$LockerThread.run(MainActivity.java:36)
จากรายงานนี้เราจะเห็นว่าการติดตามสแต็กของสองเธรด เธรดแรก (เธรด "หลัก") ติดอยู่ที่ MainActivity.java:46
ในขณะที่เธรดที่สอง (ชื่อ "App: Locker") ถูกล็อคในโหมดสลีปที่ MainActivity.java:18
จากนั้น ถ้าเราดูสองบรรทัดนี้ เราก็จะเข้าใจสาเหตุของการล็อคตายอย่างแน่นอน!
โปรดทราบว่าไลบรารีการรายงานข้อขัดข้องบางแห่ง (เช่น Crashlytics) จะรายงานการติดตามสแต็กของเธรดทั้งหมด ณ เวลาที่เกิดข้อยกเว้นที่ไม่ถูกตรวจจับ ในกรณีดังกล่าว การมีเธรดทั้งหมดอยู่ในข้อยกเว้นเดียวกันอาจเป็นเรื่องยุ่งยาก ในกรณีเช่นนี้ เพียงใช้ setReportMainThreadOnly()
หากต้องการตั้งค่าการหมดเวลาอื่น (5,000 มิลลิวินาทีเป็นค่าเริ่มต้น):
if ( BuildConfig . DEBUG == false ) {
new ANRWatchDog ( 10000 /*timeout*/ ). start ();
}
ตามค่าเริ่มต้น โปรแกรมเฝ้าระวังจะเพิกเฉยต่อ ANR หากมีการแนบดีบักเกอร์หรือหากแอปกำลังรอให้ดีบักเกอร์แนบ เนื่องจากตรวจพบการหยุดการดำเนินการและเบรกพอยท์เป็น ANR หากต้องการปิดใช้งานสิ่งนี้และส่ง ANRError
แม้ว่าตัวดีบักเกอร์จะเชื่อมต่ออยู่ คุณสามารถเพิ่ม setIgnoreDebugger(true)
:
new ANRWatchDog (). setIgnoreDebugger ( true ). start ();
หากคุณไม่ต้องการให้แอปพลิเคชันขัดข้องเมื่อตรวจพบ ANR คุณสามารถเปิดใช้งานการโทรกลับแทนได้:
new ANRWatchDog (). setANRListener ( new ANRWatchDog . ANRListener () {
@ Override
public void onAppNotResponding ( ANRError error ) {
// Handle the error. For example, log it to HockeyApp:
ExceptionHandler . saveException ( error , new CrashManager ());
}
}). start ();
สิ่งนี้สำคัญมากเมื่อนำส่งแอปของคุณเป็นเวอร์ชันที่ใช้งานจริง เมื่ออยู่ในมือของผู้ใช้คนสุดท้าย อาจเป็นการดีกว่า ที่จะไม่เกิดข้อขัดข้องหลังจากผ่านไป 5 วินาที แต่เพียงรายงาน ANR ไปยังระบบการรายงานที่คุณใช้ บางทีหลังจากผ่านไปไม่กี่วินาที แอปจะ "ยกเลิกการหยุดทำงาน"
หากคุณต้องการให้รายงานเฉพาะเธรดของคุณเองใน ANRError และไม่ใช่ทุกเธรด (รวมถึงเธรดของระบบ เช่น เธรด FinalizerDaemon
) คุณสามารถตั้งค่าคำนำหน้าได้: เฉพาะเธรดที่มีชื่อขึ้นต้นด้วยคำนำหน้านี้เท่านั้นที่จะถูกรายงาน .
new ANRWatchDog (). setReportThreadNamePrefix ( "APP:" ). start ();
จากนั้น เมื่อคุณเริ่มกระทู้ อย่าลืมตั้งชื่อเป็นชื่อที่ขึ้นต้นด้วยคำนำหน้านี้ (ถ้าคุณต้องการให้รายงาน):
public class MyAmazingThread extends Thread {
@ Override
public void run () {
setName ( "APP: Amazing!" );
/* ... do amazing things ... */
}
}
หากคุณต้องการให้ติดตามสแต็กเธรดหลักเท่านั้น ไม่ใช่เธรดอื่นๆ ทั้งหมด คุณสามารถ:
new ANRWatchDog (). setReportMainThreadOnly (). start ();
บางครั้ง คุณต้องการทราบว่าแอปพลิเคชันค้างมาระยะหนึ่งแล้ว แต่ยังไม่รายงานข้อผิดพลาด ANR ในตอนนี้ คุณสามารถกำหนด interceptor ที่จะถูกเรียกก่อนที่จะรายงานข้อผิดพลาด บทบาทของตัวสกัดกั้นคือการกำหนดว่าข้อผิดพลาด ANR ควรถูกหยิบยกขึ้นหรือเลื่อนออกไปเมื่อพิจารณาถึงระยะเวลาการแช่แข็งที่กำหนดหรือไม่
new ANRWatchDog ( 2000 ). setANRInterceptor ( new ANRWatchDog . ANRInterceptor () {
@ Override
public long intercept ( long duration ) {
long ret = 5000 - duration ;
if ( ret > 0 ) {
Log . w ( TAG , "Intercepted ANR that is too short (" + duration + " ms), postponing for " + ret + " ms." );
}
return ret ;
}
})
ในตัวอย่างนี้ ANRWatchDog เริ่มต้นด้วยการหมดเวลา 2,000 ms แต่ตัวสกัดกั้นจะเลื่อนข้อผิดพลาดออกไปจนกว่าจะถึงการหยุดทำงานอย่างน้อย 5,000 ms
ANRWatchDog เป็นเธรด ดังนั้นคุณจึงสามารถขัดจังหวะได้ตลอดเวลา
หากคุณกำลังเขียนโปรแกรมด้วยความสามารถหลายกระบวนการของ Android (เช่น การเริ่มกิจกรรมในกระบวนการใหม่) โปรดจำไว้ว่าคุณจะต้องมีเธรด ANRWatchDog ต่อกระบวนการ
ANR-Watchdog ใช้งานได้ฟรีสำหรับทั้งการใช้งานที่ไม่หวังผลกำไรและเชิงพาณิชย์ และจะเป็นเช่นนั้นตลอดไป
หากคุณต้องการแสดงการสนับสนุนหรือชื่นชมผลงานของฉัน คุณสามารถ บริจาค ได้ฟรี !
นี่คงจะได้รับการชื่นชมอย่างมาก (แน่นอน) แต่ก็ไม่จำเป็นที่จะได้รับความช่วยเหลือหรือการสนับสนุน ซึ่งเรายินดีให้บริการฟรี :)