Cách lý tưởng để đặt Trình xử lý ngoại lệ không cần thiết toàn cầu trong Android


88

Tôi muốn thiết lập một trình xử lý ngoại lệ không cần suy nghĩ chung cho tất cả các chuỗi trong ứng dụng Android của mình. Vì vậy, trong Applicationlớp con của tôi, tôi đặt một triển khai Thread.UncaughtExceptionHandlerlàm trình xử lý mặc định cho các trường hợp ngoại lệ chưa được giải quyết.

Thread.setDefaultUncaughtExceptionHandler(
                new DefaultExceptionHandler(this));

Trong quá trình triển khai của mình, tôi đang cố gắng hiển AlertDialogthị thông báo ngoại lệ thích hợp.

Tuy nhiên, điều này dường như không hiệu quả. Bất cứ khi nào, một ngoại lệ được đưa ra cho bất kỳ luồng nào không được xử lý, tôi sẽ nhận được hộp thoại mặc định của hệ điều hành ("Xin lỗi! Hộp thoại ứng dụng-đã-dừng-bất ngờ").

Cách chính xác và lý tưởng để đặt một trình xử lý mặc định cho các trường hợp ngoại lệ chưa được giải quyết là gì?


1
Bạn có thể vui lòng chia sẻ mã cho cùng ...
Code_Life

2
Nếu bạn muốn đăng nhập ngoại lệ của bạn, có một cái nhìn tại acra.ch . ACRA cho phép bạn gửi báo cáo lỗi tới Google-Doc hoặc cho bạn qua E-Mail.
Alexander Pacha

1
@Alexander Hoặc bạn chỉ có thể sử dụng Google Analytics cho Android, và đăng nhập tất cả các trường hợp ngoại lệ bạn muốn ...
IgorGanapolsky

Điều này có thể giúp đỡ stackoverflow.com/questions/19897628/...
Aristo Michael

Câu trả lời:


24

Đó là tất cả những gì bạn cần làm. (Đảm bảo rằng bạn khiến quá trình tạm dừng sau đó - mọi thứ có thể ở trạng thái không chắc chắn.)

Điều đầu tiên cần kiểm tra là liệu trình xử lý Android có được gọi hay không. Có thể phiên bản của bạn đang được gọi nhưng bị lỗi nghiêm trọng và system_server đang hiển thị một hộp thoại chung khi nó gặp sự cố quy trình.

Thêm một số thông báo nhật ký ở đầu trình xử lý của bạn để xem liệu nó có đến đó không. In kết quả từ getDefaultUncaughtExceptionHandler và sau đó ném một ngoại lệ không cần thiết để gây ra lỗi. Theo dõi đầu ra logcat để xem điều gì đang xảy ra.


10
"ném một ngoại lệ không cần thiết để gây ra sự cố sau khi bạn xử lý lỗi" vẫn quan trọng. Tôi vừa trải nghiệm nó. Ứng dụng của tôi đã bị khóa sau khi tôi xử lý ngoại lệ và không ném ra một ngoại lệ không cần thiết.
OneWorld vào

@OneWorld Jepp tương tự ở đây - ít nhất là về phần khóa - dường như không có cách nào để "cứu" ứng dụng khỏi sự cố.
AgentKnopf

@Zainodis Tôi đã đăng một câu trả lời hơi lạc đề cho câu hỏi này đưa ra một liên kết đến Crittercism - tôi nghĩ rằng họ có một tính năng cho phép bạn "cứu" ứng dụng khỏi bị treo. Không chắc chắn - Tôi chỉ đang sử dụng phiên bản miễn phí atm.
Richard Le Mesurier

@RichardLeMesurier Cảm ơn vì gợi ý - Tôi sẽ kiểm tra nó :)!
AgentKnopf

Lạ thật !!! uncaughtexception được gọi ngay cả khi tôi xử lý ngoại lệ trong độ nhạy của mình. Bất kỳ ý tưởng.
Hiren Dabhi,

12

Tôi đã đăng giải pháp đơn giản để xử lý tùy chỉnh các sự cố Android cách đây rất lâu. Nó hơi hacky nhưng nó hoạt động trên tất cả các phiên bản Android (bao gồm cả Lollipop).

Đầu tiên là một chút lý thuyết. Các vấn đề chính khi bạn sử dụng trình xử lý ngoại lệ không cần thiết trong Android là các ngoại lệ được đưa vào chuỗi chính (hay còn gọi là giao diện người dùng). Và đây là lý do tại sao. Khi ứng dụng bắt đầu hệ thống gọi phương thức ActivityThread.main chuẩn bị và khởi động Vòng lặp chính của ứng dụng của bạn:

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

Main looper chịu trách nhiệm xử lý các thông báo được đăng trong chuỗi giao diện người dùng (bao gồm tất cả các thông báo liên quan đến hiển thị và tương tác với giao diện người dùng). Nếu một ngoại lệ được đưa ra trong chuỗi giao diện người dùng, nó sẽ bị trình xử lý ngoại lệ của bạn bắt, nhưng vì bạn không sử dụng loop()phương pháp, bạn sẽ không thể hiển thị bất kỳ hộp thoại hoặc hoạt động nào cho người dùng vì không còn ai để xử lý thông báo giao diện người dùng cho bạn.

Giải pháp được đề xuất khá đơn giản. Chúng tôi chạy Looper.loopphương pháp của riêng mình và bao quanh nó bằng khối try-catch. Khi một ngoại lệ được phát hiện, chúng tôi xử lý nó theo ý muốn (ví dụ: bắt đầu hoạt động báo cáo tùy chỉnh của chúng tôi) và gọi Looper.looplại phương thức.

Phương pháp sau thể hiện kỹ thuật này (nó nên được gọi từ Application.onCreatengười nghe):

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);
        }
    }
}

Như bạn có thể thấy, trình xử lý ngoại lệ chưa được sử dụng chỉ được sử dụng cho các ngoại lệ được đưa vào các luồng nền. Trình xử lý sau nắm bắt những ngoại lệ đó và truyền chúng tới chuỗi giao diện người dùng:

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);
            }
        });
    }
}

Một dự án mẫu sử dụng kỹ thuật này có sẵn trên repo GitHub của tôi: https://github.com/idolon-github/android-crash-catcher


Như người ta có thể đoán, bằng cách này, không chỉ có thể hiển thị hộp thoại lỗi tùy chỉnh mà còn cho phép người dùng bỏ qua ngoại lệ và tiếp tục làm việc với ứng dụng (và mặc dù có vẻ là một ý tưởng tồi đối với các ứng dụng đã xuất bản, nhưng nó có thể là khá thuận tiện trong phiên gỡ lỗi hoặc thử nghiệm).
Idolon

Xin chào, mặc dù điều này hoạt động trong việc bắt các ngoại lệ không cần thiết, nhưng vì một số lý do, mã này luôn ném ra các ngoại lệ. Ban đầu, tôi mặc dù đó là do dòng RuntimeException mà bạn có trong phương thức startCatcher nhưng tôi vẫn nhận được ngoại lệ sau khi xóa nó. Tôi không chắc đó có phải là một điều bắt buộc hay không. Dù sao thì ngoại lệ mà tôi nhận được là java.lang.RuntimeException: Performing pause of activity that is not resumed. Tôi tin rằng khi tôi đang cố gắng khởi động trình lặp của riêng mình, hệ thống sẽ tạm dừng hoạt động sắp bắt đầu? Tôi không chắc chắn nhưng mọi sự giúp đỡ sẽ được đánh giá cao. Cảm ơn
sttaq

ngoài ra nếu tôi xóa startCatcher tức là chạy ứng dụng mà không có mã của bạn thì mọi thứ hoạt động tốt mà không có bất kỳ ngoại lệ nào.
sttaq

@sttaq Bạn sử dụng phiên bản Android nào?
Idolon

Tôi nghĩ rằng tôi đã thử điều này vào ngày 2.3.x
sttaq

3

Tôi nghĩ rằng để vô hiệu hóa điều đó trong phương thức uncaughtException () của bạn, không gọi trướcHandler.uncaughtException () trong đó beforeHandler được đặt bởi

previousHandler = Thread.getDefaultUncaughtExceptionHandler();

2

FWIW Tôi biết điều này hơi lạc đề, nhưng chúng tôi đã sử dụng kế hoạch miễn phí của Crittercism thành công. Họ cũng cung cấp một số tính năng cao cấp, như xử lý ngoại lệ để ứng dụng không bị lỗi.

Trong phiên bản miễn phí, người dùng vẫn thấy sự cố, nhưng ít nhất tôi nhận được email và dấu vết ngăn xếp.

Chúng tôi cũng sử dụng phiên bản iOS (nhưng tôi đã nghe các đồng nghiệp của mình nói rằng nó không tốt bằng).


Đây là những câu hỏi tương tự:


1

Nó không hoạt động cho đến khi bạn gọi

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

vào cuối UncaughtExceptionHandler của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.