Android源码分析----------Handler消息机制(2)消息队列的建立过程及Loop循环的工作原理

二.消息队列的建立过程及Loop循环的工作原理

我们都知道,Handler向消息队列Looper发送消息需要用到sendMessage()方法.在了解这个方法之前,我们先看一下Looper是从哪里来的.一个Handler关联着一个消息队列,并且主线程只有一个消息队列.所以所有的Handler共用一个Looper.那么这个共用的Looper是从哪来的呢?


我们平时创建Handler时,使用的是Handler空参构造:


这里有一个静态方法Looper.mLooper(),可以帮它获取一个Looper对象mLooper,这就是Handler依赖的looper.同时,在这个构造方法中,通过mLooper.mQueue,Handler获取到了消息队列对象mQueue.

在Looper类中,mLooper又是如何获取的呢?源代码如下:


我们可以发现, mLooper是从当前线程中get出来的.而在当前线程的get方法中,具体的获取方式如下,这里我们不再具体分析.


我们知道,有get()方法就一定有对应的set()方法,只要找到set()方法,我们就找到了mLooper的来源,源代码如下:


在Looper类的静态方法prepare()中,有当前线程的set()方法,并且在set()方法中new了一个Looper对象.

再来看Looper的构造方法中可以看到,它构造了自己的消息队列,new了一个MessageQueue.并且引用了当前线程

mThread = Thread.currentThread().


所以,可以说有了静态方法prepare(),当前线程就有了looper对象.那么,我们的主线程在哪里调用了prepare()方法呢?

可以看到,Looper类专门准备了一个prepareMainLooper()方法,为主线程准备Looper对象,这个方法上来就调用了prepare方法,之后获取当前线程的looper,并设置为主线程的looper.


接下来,就要引入一个新的类的概念:ActivityThread.可以看到,这个类虽然名字中含有Thread,但它并不是真正的线程类,因为它没有继承Thread:


但是,只要这个类跑起来,就是一个进程,也可以看做是一个线程来使用.既然是个普通的类,就要先看它的mian方法:


于是我们看到,在main方法的第三行,调用了Looper.prepareMainLooper()方法,也就是说,当我们打开一个应用的时候,应用的初始化实际上跑的是ActivityThread类,它一跑起来,就为主线程准备了一个Looper对象.


有了Looper对象,接下来我们看一下消息队列是如何运行的.

还是看ActivityThread类的main方法,继续往下看,会看到Looper.loop()方法,开始了loop()循环.我们再进到loop()方法里看一看:


也是先获取了当前线程的Looper对象me,然后拿着me找到了它的消息队列,有了消息队列后,一个while(true)死循环出现了.在循环里不断的queue.next()获取下一条消息,取出一条消息后,dispatchMessage(msg)分发消息,消息执行完后msg.recycle()方法对消息复用.如此往复.

这里有人或许会有疑问,在主线程while(true)无线循环,主线程不就死了吗?不会ANR吗?

带着疑问我们再来看ActivityThread:


在它的main方法中,始终保有一个Handler,用来发送消息,于是,需要运行onCreate()方法的时候,ActivityThread中的Handler就向主线程的Looper中发送一条消息,告诉他该执行onCreate()方法了,于是onCreate()方法跑起来了;onStart()方法等也是这样解决.生命周期都是消息发送过来执行的.


同时,可以得到以下三个知识点:

1.由于有了while(true)循环,才能让主线程一直运行,主线程活着,应用程序才能一直活着,可以说,主线程Looper里的while(true)循环,是保证程序活着的根本,没有这个循环,程序就会退出.

2.由于有了while(true)循环,这个线程就是我们的主线程.我们所有的主线程操作都是封装成消息送进来处理的,包括所有的生命周期方法onCreate(),onStart()等等.

3.ANR的发生,是由于送进主线程Loop循环中的某条消息执行了耗时操作,就是不结束,消息队列里面堆积了很多消息无法取出,消息无法处理,其他程序无法继续执行,系统无法更新界面,所以应用无响应,于是产生了ANR.要解决ANR,就需要在执行了耗时操作的消息中再开启一条子线程,在子线程中执行耗时操作.消息立即得到执行,下一条消息就可以进来,也就不会造成阻塞了.

另外,还有一种情况,当while(true)循环不断运行的时候,消息队列里的消息跑完了怎么办? while(true)循环就不再跑了,当队列取不到下一个消息时queue.next()会停下来等待,线程就进入休眠状态。但休眠后的线程需要唤醒,这个唤醒机制就不是java的唤醒机制了,因为java的唤醒机制相对比较慢,这时的唤醒机制是基于linux内核的.这里就涉及到系统底层的相关知识,叫做管道流机制:


Pipe管道流机制是linux内核里面的,它的唤醒和休眠由两个流负责,一个写入流和一个读取流.它的关联方式是:只要写入流开始写数据,读取流就立刻被唤醒;只要写入流一停止,读取流立刻进入休眠状态.

这里,我们的主线程拿着读取流,接收消息,子线程拿着写入流,发送消息;拿着写入流的子线程只要写入任何一点点数据,拿着读取流的主线程立马被唤醒.

当需要唤醒读取流时,写入流并不真正发送有真实意义的数据,一般只发送一个字符,就可以唤醒主线程.

 

在linux里面,写入流和读取流都不叫做流,在系统层,写入流叫做写入句柄,读取流叫做读取句柄.有想要具体了解句柄与流的区别的朋友,可以研究一下linux内核方面的知识.

 

以上就是消息队列的建立过程及Loop循环的工作原理.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值