- 什么是ANR
Appliction Not Responding 直译过来的意思就是应用程序没有响应
- ANR的产生原因, 及场景
原因: 在UI线程上执行一个潜在的耗时操作
场景:
1.KeyDipatchTimeout(5 seconds)-->按键或触摸事件在特定时间内没有响应
2.BroadcastReceiver(10 seconds)-->在特定时间内无法处理完成
3.ServiceTimeout(20 seconds)-->service在特定时间内没有完成
- 如何监测ANR
原理:
Android应用程序的所有交互操作和响应,都是通过主线程的消息机制来进行的。
例如当用户点击了某个Button,系统会向主线程发送消息,主线程的Looper从主线程消息队列中取出消息并处理,处理完当前消息,主线程Looper再去取出下一个消息。
当主线程做了耗时的任务,主线程的Looper就无法从消息队列中取出新的消息,所表现出的就是程序卡顿,甚至是ANR。
同理,我们在子线程往主线程发送一个消息,要是消息无法得到及时处理,那说明程序发生ANR了。
思路:
在子线程里向主线程发消息,如果过了固定时间后,消息仍未处理,则说明已发生ANR了
当程序ANR后,我们可以通过主线程Looper拿到主线程Thread,然后通过getStackTrace拿到主线程当前的调用栈,从而定位到发生ANR的地方,定位到耗时操作
定义一个线程, 用来监测主线程
class WatchDogThread : Thread() {
companion object {
val MESSAGE_WATCHDOG_TIME_TICK = 0
/**
* 判定Activity发生了ANR的时间,必须要小于5秒,否则等弹出ANR,可能就被用户立即杀死了。
*/
val ACTIVITY_ANR_TIMEOUT = 2000
private var lastTimeTick = -1
private var timeTick = 0
}
private val watchDogHandler = object : android.os.Handler() {
override fun handleMessage(msg: Message) {
timeTick++
timeTick %= Integer.MAX_VALUE
}
}
override fun run() {
while (true) {
watchDogHandler.sendEmptyMessage(MESSAGE_WATCHDOG_TIME_TICK)
try {
Thread.sleep(ACTIVITY_ANR_TIMEOUT.toLong())
} catch (e: InterruptedException) {
e.printStackTrace()
}
// 如果相等,说明过了ACTIVITY_ANR_TIMEOUT的时间后watchDogHandler仍没有处理消息,已经ANR了
if (timeTick == lastTimeTick) {
throw ANRException()
} else {
lastTimeTick = timeTick
}
}
}
}
在Application启动监测线程
public class MyApplication extends Application {
@Override
public void onCreate() {
new WatchDogThread().start();
super.onCreate();
}
}
当监测到发生ANR时,抛出一个自定义异常
class ANRException : RuntimeException("应用程序无响应,快来改BUG啊!!") {
init {
val mainThread = Looper.getMainLooper().thread
stackTrace = mainThread.stackTrace
}
}