各主要类作用:
Handler:负责发送消息及处理消息
Looper:复制不断的从消息队列中取出消息,并且给发送本条消息的Handler
MessageQueue:负责存储消息
Message:消息本身,负责携带数据
分发流程:
初始化消息队列,创建messageQueue并绑定到Looper上。
Handler的sendMessage发起消息处理流程开端,创建Message并放入到MessageQueue中,由Looper的无限循环任务唤醒阻塞,开始分发Message,执行Message的Runable之后,调用保存的handler的handleMessage方法,回到handler实现结束消息流程。
主要类的关联关系:
ThreadLocal中获取Looper对象,说明Looper是线程独立的,即主线程(MainThread)持有。
MessageQueue是在Looper的构造方法创建,说明Looper是关联的MessageQueue的。采用next持有message对象引用,形成链表实现。
Looper的loop方法,发起for(;;)无限循环,在message.next()中或者没有对象阻塞,或者有对象进行message执行。
Message中callback保存runable对象用来执行,next保存下一个message用来实现链表,target用来执行runable的run方法。
延申问题:
那么,loop的for(;;)会阻塞线程,那么主线程是如何在阻塞时接触阻塞的呢?按上面的说法是没有其他线程进行唤醒操作的
如果你了解下linux的epoll你就知道为什么不会被卡住了,先说结论:阻塞是有的,但是不会卡住
主要原因有2个
-
epoll模型
当没有消息的时候会epoll.wait,等待句柄写的时候再唤醒,这个时候其实是阻塞的。 -
所有的ui操作都通过handler来发消息操作。
比如屏幕刷新16ms一个消息,你的各种点击事件,所以就会有句柄写操作,唤醒上文的wait操作,所以不会被卡死了。
在代码ActivityThread.main()中:
public static void main(String[] args) {
....
//创建Looper和MessageQueue对象,用于处理主线程的消息
Looper.prepareMainLooper();
//创建ActivityThread对象
ActivityThread thread = new ActivityThread();
//建立Binder通道 (创建新线程)
thread.attach(false);
Looper.loop(); //消息循环运行
throw new RuntimeException("Main thread loop unexpectedly exited");
}
thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程,具体过程可查看 startService流程分析,这里不展开说,简单说Binder用于进程间通信,采用C/S架构。