有时我们的项目会遇见如下所示的崩溃堆栈:
android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2101)
at android.os.Handler.dispatchMessage(Handler.java:108)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7425)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
这种堆栈没有我们业务层代码的信息,所以我们一时间会不知所措,想try-catch捕获都捕获不了,因为都是系统层的崩溃。
那么这就引出一个问题,我们能不能捕获系统层的代码异常或普通逻辑呢?答案是可以的,我们可以用代理的方法来实现。
一.手动代理
1.溯源
这里我们以上述代码为例,尝试捕获系统层的主线程Handler抛出的异常。
首先我们根据崩溃堆栈来看系统层的实现。
public final class ActivityThread extends ClientTransactionHandler {
final H mH = new H();
class H extends Handler {
//...
}
}
一个app进程的入口就在ActivityThread中,这个类中有一个叫做H,继承自Handler的类,且在ActivityThread中,创建了一个该对象叫做mH,这个Handler对象,就是上面堆栈中的那个handleMessage()方法的调用者。
再来看Handler里是如何调用handleMessage()方法的。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//Runnable
handleCallback(msg);
} else {
if (mCallback != null) {
//Handler的callback对象
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//重写该方法
}
}
当Looper将Message对象,传递到Handler的dispatchMessage()方法中时,首先会看Message的callback对象是否存在,存在的话直接回调,这个callback其实是Runnable对象,是我们经常使用的handler.post({})等方法传入的回调参数,在这里由于堆栈显示,走的是Handler的handleMessage()方法,所以显然这里的callback为null,走else分支。
在else分支里,还有一个处理优先级较高的mCallback,如果该mCallback处理结果为true则直接结束处理,否则交由Handler自身的dispatchMessage()方法处理。
public class Handler {
final Callback mCallback;
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
//...
mCallback = callback;
}
}
可见,Handler的callback,是构造函数构造时传入的,而ActivityThread中创建mH时,是直接走的默认构造函数,所以这个mCallback为null,所以上面说的else分支里,会直接调用Handler的dispatchMessage()方法,我们再来看dispatchMessage()方法。
class H extends Handler {
public void