【Android面试】主线程中的Looper.loop()一直无限循环为什么不会造成ANR?

Android面试中,你也许会被问到题目中的问题,这里我们基于以下几点来延伸解读其中原因:

1、什么是ANR?ANR发生的原因是什么?

2、Looper为什么要无限循环?

3、线程的几种状态

4、主线程中的Looper.loop()一直无限循环为什么不会造成ANR?

1、什么是ANR?ANR发生的原因是什么?

ANR即Application Not Responding,顾名思义就是应用程序无响应。

在Android中,一般情况下,四大组件均是工作在主线程中的,Android中的Activity Manager和Window Manager会随时监控应用程序的响应情况,如果因为一些耗时操作(网络请求或者IO操作)造成主线程阻塞一定时间(例如造成5s内不能响应用户事件或者BroadcastReceiver的onReceive方法执行时间超过10s),那么系统就会显示ANR对话框提示用户对应的应用处于无响应状态。

简单总结就是以下两点:

1. 不要让主线程干耗时的工作

2. 不要让其他线程阻塞主线程的执行

2、Looper为什么要无限循环?

Looper中重要的两个方法为prepare()、loop()

《【Android面试】主线程中的Looper.loop()一直无限循环为什么不会造成ANR?》 prepare()源码

Looper在prepare中通过ThreadLocal保证了每个线程Looper对象的唯一性,即对于每个线程,有唯一的Looper对象和MessageQueue队列

《【Android面试】主线程中的Looper.loop()一直无限循环为什么不会造成ANR?》 loop()源码

显而易见的,主线程中如果没有looper进行循环,那么主线程一运行完毕就会退出。那么我们还能运行APP吗,显然,这是不可能的,Looper主要就是做消息循环,然后由Handler进行消息分发处理,一旦退出消息循环,那么你的应用也就退出了。

总结:Looper的无限循环必不可少

3、线程的几种状态

很多读者会问,你说的好好地Looper,干嘛突然转到线程状态的知识点了呢?

别急别急,请允许老衲细细道来。

众所周知,线程的五大状态为:New、Runnable、Running、Blocked、Dead,而他们之间有者千丝万缕的关系,为了便于大家理解,看图说话

《【Android面试】主线程中的Looper.loop()一直无限循环为什么不会造成ANR?》 线程状态图

很多人会问,为什么有这么多Blocked?

(1)新建状态(New):新创建了一个线程对象。

(2)就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

(3)运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

(4)阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

不管线程进入哪种阻塞状态,都得等所等待的事件(wait、sleep、join、synchronized、I/O)完成后,才可以进入就绪队列,排队等待CPU资源

(5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

4、主线程中的Looper.loop()一直无限循环为什么不会造成ANR?

也许讲到这里,很多人已经知道原因了吧!不过习惯使然,我还是要总结一下。

主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗

讲到这里,各位看官应该知道为什么上面要引入线程状态的了解了吧。

    原文作者:itbird01
    原文地址: https://www.jianshu.com/p/e149a2e89290
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞