วิธีที่ดีที่สุดในการตั้งค่าตัวจัดการข้อยกเว้นที่ไม่ถูกจับทั่วโลกใน Android


88

ฉันต้องการตั้งค่าตัวจัดการข้อยกเว้นที่ไม่ถูกจับทั่วโลกสำหรับเธรดทั้งหมดในแอปพลิเคชัน Android ของฉัน ดังนั้นในApplicationคลาสย่อยของฉันฉันจึงตั้งค่าการนำไปใช้Thread.UncaughtExceptionHandlerเป็นตัวจัดการเริ่มต้นสำหรับข้อยกเว้นที่ไม่ถูกจับ

Thread.setDefaultUncaughtExceptionHandler(
                new DefaultExceptionHandler(this));

ในการนำไปใช้งานของฉันฉันกำลังพยายามแสดงข้อความAlertDialogแสดงข้อยกเว้นที่เหมาะสม

อย่างไรก็ตามดูเหมือนจะไม่ได้ผล เมื่อใดก็ตามที่เกิดข้อยกเว้นสำหรับเธรดใด ๆ ที่ไม่ได้รับการจัดการฉันจะได้รับสต็อกกล่องโต้ตอบเริ่มต้นระบบปฏิบัติการ ("ขออภัย! -Application-has-stop-expectedly

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


1
คุณช่วยแชร์รหัสให้เหมือนกันได้
ไหม

2
หากคุณต้องการเข้าสู่ระบบข้อยกเว้นของคุณมีลักษณะที่acra.ch ACRA ช่วยให้คุณสามารถส่งรายงานข้อผิดพลาดไปยัง Google-Doc หรือให้คุณทางอีเมล
Alexander Pacha

1
@Alexander หรือคุณสามารถใช้ Google Analytics สำหรับ Android และบันทึกข้อยกเว้นทั้งหมดที่คุณต้องการ ...
IgorGanapolsky

นี่อาจเป็นความช่วยเหลือstackoverflow.com/questions/19897628/…
Aristo Michael

คำตอบ:


24

นั่นควรเป็นสิ่งที่คุณต้องทำ (ตรวจสอบให้แน่ใจว่าคุณทำให้กระบวนการหยุดชะงักในภายหลัง - สิ่งต่างๆอาจอยู่ในสถานะที่ไม่แน่นอน)

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

เพิ่มข้อความบันทึกที่ด้านบนของเครื่องจัดการเพื่อดูว่าไปถึงที่นั่นหรือไม่ พิมพ์ผลลัพธ์จาก getDefaultUncaughtExceptionHandler จากนั้นโยนข้อยกเว้นที่ไม่ถูกจับเพื่อทำให้เกิดความผิดพลาด จับตาดูเอาต์พุต logcat เพื่อดูว่าเกิดอะไรขึ้น


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

@OneWorld Jepp เหมือนกันที่นี่ - อย่างน้อยก็สำหรับส่วนการล็อกดูเหมือนว่าจะไม่มีวิธี "บันทึก" แอปไม่ให้หยุดทำงาน
AgentKnopf

@ Zainodis ฉันโพสต์คำตอบที่ไม่ตรงประเด็นสำหรับคำถามนี้โดยให้ลิงก์ไปยัง Crittercism - ฉันคิดว่าพวกเขามีคุณสมบัติที่ช่วยให้คุณ "บันทึก" แอปไม่ให้หยุดทำงานได้ ไม่แน่ใจ - ฉันใช้เฉพาะ ATM เวอร์ชันฟรี
Richard Le Mesurier

@RichardLeMesurier ขอบคุณสำหรับคำใบ้ - ฉันจะลองดู :)!
AgentKnopf

แปลก!!! เรียกว่า uncaughtexception แม้ว่าฉันจะจัดการข้อยกเว้นในความไวของฉัน ความคิดใด ๆ
Hiren Dabhi

12

ฉันโพสต์วิธีง่ายๆสำหรับการจัดการข้อขัดข้องของ Android แบบกำหนดเองเมื่อนานมาแล้ว มันแฮ็คเล็กน้อย แต่ใช้ได้กับ Android ทุกรุ่น (รวมถึง Lollipop)

ก่อนอื่นทฤษฎีเล็กน้อย ปัญหาหลักเมื่อคุณใช้ตัวจัดการข้อยกเว้นที่ไม่ถูกจับใน Android มาพร้อมกับข้อยกเว้นในเธรดหลัก (aka UI) และนี่คือเหตุผล เมื่อแอปเริ่มระบบเรียกใช้เมธอดActivityThread.mainซึ่งเตรียมและเริ่มตัววนหลักของแอปของคุณ:

public static void main(String[] args) {
  …
  …
    Looper.prepareMainLooper();
  …
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

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

วิธีแก้ปัญหาที่เสนอค่อนข้างง่าย เราเรียกใช้Looper.loopวิธีการของเราเองและล้อมรอบด้วยบล็อกลองจับ เมื่อพบข้อยกเว้นเราจะดำเนินการตามที่เราต้องการ (เช่นเริ่มกิจกรรมรายงานที่กำหนดเอง) และLooper.loopวิธีการโทรอีกครั้ง

วิธีการต่อไปนี้แสดงให้เห็นถึงเทคนิคนี้ (ควรเรียกจากApplication.onCreateผู้ฟัง):

private void startCatcher() {
    UncaughtExceptionHandler systemUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();

    // the following handler is used to catch exceptions thrown in background threads
    Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler(new Handler()));

    while (true) {
        try {
            Looper.loop();
            Thread.setDefaultUncaughtExceptionHandler(systemUncaughtHandler);
            throw new RuntimeException("Main thread loop unexpectedly exited");
        } catch (Throwable e) {
            showCrashDisplayActivity(e);
        }
    }
}

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

static class UncaughtHandler implements UncaughtExceptionHandler {

    private final Handler mHandler;

    UncaughtHandler(Handler handler) {
        mHandler = handler;
    }

    public void uncaughtException(Thread thread, final Throwable e) {
        mHandler.post(new Runnable() {
            public void run() {
                throw new BackgroundException(e);
            }
        });
    }
}

โครงการตัวอย่างที่ใช้เทคนิคนี้มีอยู่ใน repo GitHub ของฉัน: https://github.com/idolon-github/android-crash-catcher


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

สวัสดีแม้ว่าจะใช้งานได้ในการตรวจจับข้อยกเว้นที่ไม่ถูกจับ แต่ด้วยเหตุผลบางประการรหัสนี้มักจะทิ้งข้อยกเว้น ตอนแรกฉันคิดว่าเป็นเพราะบรรทัด RuntimeException ที่คุณมีในเมธอด startCatcher แต่ฉันยังคงได้รับข้อยกเว้นหลังจากลบออก ฉันไม่แน่ใจว่านั่นเป็นสิ่งที่จำเป็นหรือไม่ java.lang.RuntimeException: Performing pause of activity that is not resumedอย่างไรก็ตามข้อยกเว้นที่ฉันได้รับคือ ฉันเชื่อว่าเมื่อฉันพยายามเริ่มลูปเกอร์ของตัวเองระบบจะหยุดกิจกรรมที่กำลังจะเริ่มชั่วคราว? ฉันไม่แน่ใจ แต่จะได้รับความช่วยเหลือใด ๆ ขอบคุณ
sttaq

นอกจากนี้ถ้าฉันลบ startCatcher เช่นเรียกใช้แอพโดยไม่มีรหัสของคุณทุกอย่างก็ทำงานได้ดีโดยไม่มีข้อยกเว้นใด ๆ
sttaq

@sttaq คุณใช้ Android เวอร์ชันอะไร?
Idolon

ฉันคิดว่าฉันกำลังลองอยู่บน 2.3.x
sttaq

3

ฉันคิดว่าจะปิดใช้งานในเมธอด uncaughtException () ของคุณอย่าเรียก previousHandler.uncaughtException () โดยที่ previousHandler ถูกตั้งค่าโดย

previousHandler = Thread.getDefaultUncaughtExceptionHandler();

2

FWIW ฉันรู้ว่านี่เป็นเรื่องนอกประเด็นเล็กน้อย แต่เราใช้แผนฟรีของ Crittercism จนประสบความสำเร็จ นอกจากนี้ยังมีคุณสมบัติพิเศษบางอย่างเช่นการจัดการข้อยกเว้นเพื่อให้แอปไม่ขัดข้อง

ในเวอร์ชันฟรีผู้ใช้ยังคงเห็นข้อขัดข้อง แต่อย่างน้อยฉันก็ได้รับอีเมลและการติดตามสแต็ก

นอกจากนี้เรายังใช้เวอร์ชัน iOS (แต่ฉันได้ยินมาจากเพื่อนร่วมงานของฉันว่ามันไม่ค่อยดีนัก)


คำถามที่คล้ายกันมีดังนี้


1

มันใช้ไม่ได้จนกว่าคุณจะโทร

android.os.Process.killProcess(android.os.Process.myPid());

ที่ส่วนท้ายสุดของ UncaughtExceptionHandler ของคุณ

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