相信很多人都会有一个疑问,我们为何要去阅读源码,工作上又用不上,这个问题很棒,我们就先从使用出发,然后分析这些用法的实现原理,这样才能体现出阅读源码的意义。
- 基于 Handler 和 Looper 拦截全局崩溃(主线程),避免 APP 退出。
- 基于 Handler 和 Looper 实现 ANR 监控。
- 基于 Handler 实现单线程的线程池。
实现代码
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
var startWorkTimeMillis = 0L
Looper.getMainLooper().setMessageLogging {
if (it.startsWith(">>>>> Dispatching to Handler")) {
startWorkTimeMillis = System.currentTimeMillis()
} else if (it.startsWith("<<<<< Finished to Handler")) {
val duration = System.currentTimeMillis() - startWorkTimeMillis
if (duration > 100) {
Log.e("主线程执行耗时过长","$duration 毫秒,$it")
}
}
}
val handler = Handler(Looper.getMainLooper())
handler.post {
while (true) {
try {
Looper.loop()
} catch (e: Throwable) {
// TODO 主线程崩溃,自行上报崩溃信息
if (e.message != null && e.message!!.startsWith("Unable to start activity")) {
android.os.Process.killProcess(android.os.Process.myPid())
break
}
e.printStackTrace()
}
}
}
Thread.setDefaultUncaughtExceptionHandler { thread, e ->
e.printStackTrace()
// TODO 异步线程崩溃,自行上报崩溃信息
}
}
}
通过上面的代码就可以就可以实现拦截UI线程的崩溃,耗时性能监控。但是也并不能够拦截所有的异常,如果在Activity的onCreate出现崩溃,导致Activity创建失败,那么就会显示黑屏。
ANR获取堆栈信息《Android:基于 Handler、Looper 实现 ANR 监控,获取堆栈》
源码剖析
通过上面简单的代码,我们就实现崩溃和ANR的拦截和监控,但是我们可能并不知道是为何实现的,包括我们知道出现了ANR,但是我们还需要进一步分析为何处出现ANR,如何解决。今天分析的问题有:
- 如何拦截全局崩溃,避免APP退出。
- 如何实现 ANR 监控。
- 利用 Handler 实现单线程池功能。
- Activity 的生命周期为什么用 Handler 发送执行。
- Handler 的延迟操作如何实现。
涉及的源码
/java/android/os/Handler.java
/java/android/os/MessageQueue.java
/java/android/os/Looper.java
/java/android.app/ActivityThread.java
我们先从APP启动开始分析,APP的启动方法是在ActivityThread中,在main方法中创建了主线程的Looper,也就是当前进程创建。并且在main方法的最后调用了 Looper.loop()
,在这个方法中处理主线程的任务调度,一旦执行完这个方法就意味着APP被退出了,如果我们要避免APP被退出,就必须让APP持续执行Looper.loop()。
package android.app;
public final class ActivityThread extends ClientTransactionHandler {
...
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
throw new RuntimeExcep