ANR定义
程序触发ANR:
当您的活动位于前台时,您的应用在5秒内无法响应输入事件或(例如按键或屏幕触摸事件),会弹出ANR弹窗。
位于后台时候:任务在在主线程中相当长的时间内未完成执行,也会触发ANR,但是无弹窗提示,显示卡死状态(后台ANR对话框并不总是显示给用户,可以在开发者选项里打开)
总结:Android应用程序的UI线程被阻止太长时,会触发“应用程序无响应”(ANR)错误。
出现ANR的条件
1 该应用程序在主线程上执行耗时操作(大量的数据处理,复杂的计算等之类的)。
2 主线程正在对另一个进程(workThread)执行同步绑定器调用,而workThread进程需要执行很长时间才能返回。
3 主线程被阻塞,等待在另一个线程上发生的长操作的结果才能继续进行。
4 主线程与另一个线程处于死锁状态,无论是在您的进程中还是通过绑定器调用。
解决问题
普通情况
对于耗时操作:统一丢到子线程去处理
其他情况
锁争用
在某些情况下,导致ANR的工作不会直接在应用程序的主线程上执行。如果工作线程持有主线程完成其工作所需的资源的锁定,则可能发生ANR。
@Override
public void onClick(View v) {
// The worker thread holds a lock on lockedResource
new LockTask().execute(data);
synchronized (lockedResource) {
// The main thread requires lockedResource here
// but it has to wait until LockTask finishes using it.
}
}
public class LockTask extends AsyncTask<Integer[], Integer, Long> {
@Override
protected Long doInBackground(Integer[]... params) {
synchronized (lockedResource) {
// This is a long-running operation, which makes
// the lock last for a long time
BubbleSort.sort(params[0]);
}
}
}
上面代码可以看出,lockedResource加锁了,如果在LockTask异步任务未执行完成,锁不会释放,主线程这个时候也需要用到lockedResource,就卡在这里了,要避免这种逻辑的出现。
死锁
当线程进入等待状态时发生死锁,因为另一个线程持有所需的资源,该线程也在等待第一个线程持有的资源。如果应用程序的主线程处于这种情况,则可能会发生ANR。
广播接收器
ANR在以下情况下发生:
1.广播接收器onReceive()方法在相当长的时间内未完成其方法的执行。
2.广播接收器使用goAsync()生成PendingResult对象,让PendingResult继续去执行任务,事后PendingResult未调用finish()方法导致ANR。
public void onReceive(Context context, final Intent intent) {
final PendingResult pendingResult = goAsync();
new AsyncTask<Integer[], Integer, Long>() {
@Override
protected Long doInBackground(Integer[]... params) {
// This is a long-running operation
BubbleSort.sort(params[0]);
pendingResult.finish();
}
}.execute(data);
}
注意:广播接收器可用于goAsync()向系统发出信号,表明它需要更多时间来处理消息,需要在子线程去处理,完事后记得pendingResult.finish()。
关于广播接收器goAsync()的使用,感兴趣的可以自行了解。