英文:
How to set uncaugthExceptionHandler for all threads in Android app? Crash happens only on some cases
问题
这是一个关于Android问题和Java问题的内容。
我在我的活动中设置了一个自定义的线程异常处理程序,像这样:
public class MainActivity extends FlutterActivity {
public static String TAG = "MainActivity";
Thread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler(this));
thread = new Thread(new Runnable() {
public void run() {
try {
TimeUnit.SECONDS.sleep(25);
crashMe(); //Makes app crash
} catch (Exception exception) {
}
}
});
thread.start();
}
public void crashMe() {
throw new NullPointerException();
}
public void crashMe2() {
runOnUiThread(new Runnable() {
public void run() {
throw new NullPointerException();
}
});
}
}
正如你所看到的,调用 crashMe()
在超时后确实会触发异常处理程序。然而,某些 Flutter 引擎的崩溃并不能被我的处理程序捕获,应用程序会简单关闭。就像这个例子:
2020-03-04 13:36:08.938 3901-3901/com.demo.flutter_app E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.demo.flutter_app, PID: 3901
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.DisplayAdjustments android.view.Display.getDisplayAdjustments()' on a null object reference
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1900)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1636)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7946)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1092)
at android.view.Choreographer.doCallbacks(Choreographer.java:893)
at android.view.Choreographer.doFrame(Choreographer.java:812)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1078)
at android.os.Handler.handleCallback(Handler.java:907)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:216)
at android.app.ActivityThread.main(ActivityThread.java:7625)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)
其中使用了一个实验性的 Flutter WebView。
我如何能够绝对捕获所有崩溃?我想在发生崩溃时能够重新启动应用程序,但有些崩溃未被处理。
更新:
这里是另一个没有处理的崩溃:
[+1520 ms] F/libc (31305): Fatal signal 6 (SIGABRT), code -6 (SI_TKILL) in tid 31351 (RenderThread), pid 31305 (com.example.bug)
[ +184 ms] *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
[ ] Build fingerprint: 'Dragon_Touch/M7/M7:9/PPR2.181005.003/ysq20190426:user/release-keys'
[ ] Revision: '0'
[ ] ABI: 'arm64'
[ ] pid: 31305, tid: 31351, name: RenderThread >>> com.example.bug <<<
[ ] signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
[ ] Abort message: 'Failed to set damage region on surface 0x7a1391d400, error=EGL_BAD_ACCESS'
[ ] ...
如果需要进一步翻译或有其他问题,请随时问我。
英文:
This is an Android question but also a Java question.
I seeted a custom thread exception handler in my activity like this:
public class MainActivity extends FlutterActivity {
public static String TAG = "MainActivity";
Thread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler(this));
thread = new Thread(new Runnable() {
public void run() {
try {
TimeUnit.SECONDS.sleep(25);
crashMe(); //Makes app crash
} catch (Exception exception) {
}
}
});
thread.start();
}
public void crashMe() {
throw new NullPointerException();
}
public void crashMe2() {
runOnUiThread(new Runnable() {
public void run() {
throw new NullPointerException();
}
});
}
}
and as you can see, calling crashMe()
after the timeout indeed makes the exception handler be called. However, some crashes of the Flutter Engine wont be catched by my handler and the app simply closes. Like this one:
2020-03-04 13:36:08.938 3901-3901/com.demo.flutter_app E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.demo.flutter_app, PID: 3901
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.DisplayAdjustments android.view.Display.getDisplayAdjustments()' on a null object reference
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1900)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1636)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7946)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1092)
at android.view.Choreographer.doCallbacks(Choreographer.java:893)
at android.view.Choreographer.doFrame(Choreographer.java:812)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1078)
at android.os.Handler.handleCallback(Handler.java:907)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:216)
at android.app.ActivityThread.main(ActivityThread.java:7625)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)
where an experimental flutter webview is used.
How can I absolutely catch all crashes? I want to be able to restart the application whenever it happens, but some crashes aren't handled.
UPDATE:
Here's another crash that it does not handle:
[+1520 ms] F/libc (31305): Fatal signal 6 (SIGABRT), code -6 (SI_TKILL) in tid 31351 (RenderThread), pid 31305 (com.example.bug)
[ +184 ms] *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
[ ] Build fingerprint: 'Dragon_Touch/M7/M7:9/PPR2.181005.003/ysq20190426:user/release-keys'
[ ] Revision: '0'
[ ] ABI: 'arm64'
[ ] pid: 31305, tid: 31351, name: RenderThread >>> com.example.bug <<<
[ ] signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
[ ] Abort message: 'Failed to set damage region on surface 0x7a1391d400, error=EGL_BAD_ACCESS'
[ ] x0 0000000000000000 x1 0000000000007a77 x2 0000000000000006 x3 0000000000000008
[ ] x4 feff71647164636d x5 feff71647164636d x6 feff71647164636d x7 7f7f7f7f7f7f7f7f
[ ] x8 0000000000000083 x9 0000007abf876718 x10 fffffff87ffffbdf x11 0000000000000001
[ ] x12 0000007a1f57ebd0 x13 ffffffffffffffff x14 ffffffffff000000 x15 ffffffffffffffff
[ ] x16 0000007abf8b22b0 x17 0000007abf7f0298 x18 0000007a1f57e3ba x19 0000000000007a49
[ ] x20 0000000000007a77 x21 0000000000000083 x22 0000007a1a8a1bb0 x23 0000007a1f57f1b0
[ ] x24 0000000000000000 x25 0000007a1a8a1b24 x26 0000007a1f57f198 x27 0000007a1a8a1b22
[ +2 ms] x28 0000000000000000 x29 0000007a1f57eaf0
[ +1 ms] sp 0000007a1f57eab0 lr 0000007abf7e4a50 pc 0000007abf7e4a7c
[ +82 ms] backtrace:
[ ] #00 pc 0000000000021a7c /system/lib64/libc.so (abort+124)
[ ] #01 pc 00000000000080f8 /system/lib64/liblog.so (__android_log_assert+296)
[ ] #02 pc 0000000000486e64 /system/lib64/libhwui.so (android::uirenderer::renderthread::EglManager::damageFrame(android::uirenderer::renderthread::Frame const&, SkRect const&)+180)
[ ] #03 pc 0000000000480858 /system/lib64/libhwui.so (android::uirenderer::skiapipeline::SkiaOpenGLPipeline::draw(android::uirenderer::renderthread::Frame const&, SkRect const&, SkRect const&,
android::uirenderer::FrameBuilder::LightGeometry const&, android::uirenderer::LayerUpdateQueue*, android::uirenderer::Rect const&, bool, bool, android::uirenderer::BakedOpRenderer::LightInfo const&,
std::__1::vector<android::sp<android::uirenderer::RenderNode>, std::__1::allocator<android::sp<android::uirenderer::Re
[ +1 ms] #04 pc 0000000000108728 /system/lib64/libhwui.so (android::uirenderer::renderthread::CanvasContext::draw()+192)
[ ] #05 pc 0000000000485854 /system/lib64/libhwui.so
(_ZNSt3__110__function6__funcIZN7android10uirenderer12renderthread13DrawFrameTask11postAndWaitEvE3$_0NS_9allocatorIS6_EEFvvEEclEv$c303f2d2360db58ed70a2d0ac7ed911b+644)
[ ] #06 pc 000000000043c5a8 /system/lib64/libhwui.so (android::uirenderer::WorkQueue::process()+168)
[ ] #07 pc 0000000000115e5c /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+244)
[ ] #08 pc 000000000000fb80 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+280)
[ ] #09 pc 00000000000830d4 /system/lib64/libc.so (__pthread_start(void*)+36)
[ ] #10 pc 000000000002337c /system/lib64/libc.so (__start_thread+68)
答案1
得分: 1
Edit - 回复评论的答案
即使有try - catch
子句,异常仍可能以任何方式抛出 - 当您的代码隐式地或您在catch
块内部显式抛出异常时,它不会被自定义的UncaughtExceptionHandler
处理。
所有未经处理(没有try - catch
封装)且没有自定义UncaughtExceptionHandler
的异常将由默认处理程序处理。
在Android方面,处理C代码中的异常非常困难,几乎无法捕获 - 最好的方法是将它们传播到您的Android代码并在那里处理它们 - 但这仅适用于您的C代码,而不适用于某些第三方库。如果没有这种传播并且异常是终端的 - 应用程序将会快速且意外地停止,即使使用默认的UncaughtExceptionHandler
也没有适当的错误管理。
您刚刚更新的错误与Java和C的错误有些不同。正如您所看到的,它发生在RenderThread上,因此与代码的CPU和GPU执行直接相关。在Android中,有几种在这种条件下工作的方式:OpenGl
,Vulkan
,RenderScript
,OpenCl
等。
Flutter框架使用GPU渲染范 paradigm,因此它基本上像智能手机上的游戏一样工作。我可以假设它使用了一些描述的技术来渲染UI(我认为是OpenGL
,因为它在大多数设备上都得到了支持,尽管可能是一些组合)。
您发布的错误基本上是一个C错误,但它是在RenderThread中的错误,因此是由GPU或CPU的不正确执行命令引起的。
Flutter可以正确处理其中一些错误并为您提供人类可读的解决方案。但在这种情况下不行。
对不起,伙计,没有办法捕获此错误。但它也不打算被捕获,就像NullPoitnerException
一样 - 这些异常指示开发人员应该解决的一些问题,而不是通过简单重新启动应用程序来隐藏问题。
Edit 结束
问题在于,Java中并没有所谓的“绝对捕获所有崩溃”。这是JVM的预期行为。
虽然setDefaultUncaughtExceptionHandler
在大多数情况下应该能够工作,但在以下情况下将无法工作:
-
在您的代码之外的某个地方使用了另一个
setDefaultUncaughtExceptionHandler
(或发生了未捕获异常的线程具有其自己的处理程序) -
要通过其捕获的异常已经被处理 - 被
try - catch
子句包装 -
您想在自定义的
UncaughtExceptionHandler
内部显示一些内容(对话框或Toast) - 这是极不推荐的,因为如果异常发生在UI线程上,应用可能会冻结;如果异常发生在非UI线程上 - 尝试显示对话框时,您将收到一个未捕获的异常,因为只能在UI线程上显示内容。
还有更多情况,但这些不能应用于Android,因为这些是特定于Web的情况。
此外,Flutter具有自己的异常处理机制 - 因此,可能无法通过自定义的UncaughtExceptionHandler
以您期望的方式处理Flutter框架的异常。
因此,基本上没有办法在Android应用程序中清楚地捕获所有异常。即使Crashlitycs也无法捕获所有异常,它使用的是本地(基于C的)方法。
希望对您有所帮助。
英文:
Edit - answer to comments
Even if there is a try - catch
clause the exception may be thrown either way - when your code implicitly or you explicitly throw an exception inside of a catch
block it won't be handled by custom UncaughtExceptionHandler
All the exception that are not handled(have no try - catch
wrapper) and have no custom UncaughtExceptionHandler
will be handled by the default one.
In terms of Android, exceptions in C code is quite hard to handle and catch whatsoever - the best way for that is to propagate them to your android code and handle them there - but it only applies to your C code but not some third party libraries. If there is no such propagation and exception is terminal - the app will stop rapidly and unexpectedly without any proper error management even with default UncaughtExceptionHandler
The error you've just updated you question with is a bit different from both Java and C ones. As you can see it has occurred on RenderThread thus it is directly connected with the CPU and GPU execution of the code. In android there are several ways to work in such conditions: OpenGl
, Vulkan
, RenderScript
, OpenCl
etc.
Flutter framework use GPU rendering paradigm so it basically works as games work on smartphones. I can assume that it uses some of described technologies to render UI(I think it is OpenGL
since it is supported by most devices out of the box, though it may be some combination).
The error you posted is basically a C error but it is an error in RenderThread thus it is caused by incorrect execution of command either by GPU or CPU.
Flutter can handle some of such errors in correct way and give you a human readable resolution. Not in this case though.
I am sorry mate, there is no way to catch this error. But it is not intended to be caught as well as NullPoitnerException
- those exceptions indicate some problems that should be fixed by the developer but not hidden with simple relaunch of the app.
Edit end
The thing is - there is no such thing as "absolutely catch all crashes" in Java. And this is the intended behaviour of JVM.
While setDefaultUncaughtExceptionHandler
should work in most cases, it will not work when:
-
Another
setDefaultUncaughtExceptionHandler
is used somewhere outside of your code(or thread an uncaught exception has occurred on has its own handler) -
The exception you want to catch by it is handled - wrapped by
try - catch
clause -
You want to show something(dialog or toast) inside the custom
UncaughtExceptionHandler
- that is highly not recommended since if the exception occurred on UI thread the app might freeze and if the exception occurred on non UI thread - when trying to show dialog you will receive an uncaught exception instead, since you can show something only on UI thread.
There are more cases but those cannot be applied to Android since those are web specific cases.
Also flutter has it own exception handling mechanism - so the flutter framework exceptions may not be handled the way you expect with custom UncaughtExceptionHandler
.
So basically there is no way to clearly catch all the exceptions in Android app. Even Crashlitycs fails to catch them all, and it uses native(C based) approach.
Hope it helps.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论