你真的了解ANR吗?

对于ANR,只要是一个在逐渐成为流弊的工程师的成长路上,或多或少都应该碰到过,什么?你没碰到过?对不起,你真流弊,惹不起惹不起。
惹不起.png

ANR定义

程序触发ANR:

当您的活动位于前台时,您的应用在5秒内无法响应输入事件或(例如按键或屏幕触摸事件),会弹出ANR弹窗。

位于后台时候:任务在在主线程中相当长的时间内未完成执行,也会触发ANR,但是无弹窗提示,显示卡死状态(后台ANR对话框并不总是显示给用户,可以在开发者选项里打开)

总结:Android应用程序的UI线程被阻止太长时,会触发“应用程序无响应”(ANR)错误。

出现ANR的条件
1 该应用程序在主线程上执行耗时操作(大量的数据处理,复杂的计算等之类的)。
2 主线程正在对另一个进程(workThread)执行同步绑定器调用,而workThread进程需要执行很长时间才能返回。
3 主线程被阻塞,等待在另一个线程上发生的长操作的结果才能继续进行。
4 主线程与另一个线程处于死锁状态,无论是在您的进程中还是通过绑定器调用。

解决问题

普通情况
对于耗时操作:统一丢到子线程去处理

其他情况

锁争用
在某些情况下,导致ANR的工作不会直接在应用程序的主线程上执行。如果工作线程持有主线程完成其工作所需的资源的锁定,则可能发生ANR。


例子.png
@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 {
   @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() {
   @Override
   protected Long doInBackground(Integer[]... params) {
       // This is a long-running operation
       BubbleSort.sort(params[0]);
       pendingResult.finish();
   }
 }.execute(data);
}

注意:广播接收器可用于goAsync()向系统发出信号,表明它需要更多时间来处理消息,需要在子线程去处理,完事后记得pendingResult.finish()。

关于广播接收器goAsync()的使用,感兴趣的可以自行了解。

你可能感兴趣的:(你真的了解ANR吗?)