在Android应用程序中,存在一个主线程我们通常叫做UI线程,可以进行界面的更新等,进行系统的消息发送。接触Android久了,就会知道,Activity的生命周期就是通过系统内部的Handler发送消息来进行回调,其中消息传递过程是Handler的消息机制;
为什么要使用Handler的消息机制?
—它的设计避免了多线程的并发执行操作,我们知道Android规定:UI只是支持单线程模型。
假如多个线程都更新UI的界面,就会发生线程不安全问题,造成数据的丢失等等;
有了Handler之后一些好事的的操作都可以放到其他子线程里面执行,执行完毕之后可通过Handler与UI主 线程进行交互,这个过程很好的避免的多线程的操作,同时增强了用户的体验。
下图是消息机制总体流程图:
它主要涉及下面四个类 Handler、MessageQueue、Message、Looper;
Message:消息
MessageQueue:消息队列,负责存储消息,它内部其实是一个单链表的结构
Handler:负责消息的发送和处理.必须要关联当前线程中唯一的Looper对象,才可以,否则抛异常
Looper:负责消息队列中消息的轮询操作,每一个线程只能有唯一的Looper对象(比如UI线程)
在UI线程被创建的时候,就会初始化一个Looper对象(是唯一的),然后在通过Looper.looper()方法(这个方法是一个无限循环方法),发现MessageQueue中有消息的时候,就会把它取出发送给Handler的handleMessage()方法处理;
对于消息队列,当有消息的时候,它会被唤醒,来处理消息,当没有消息的时候,他就会处于阻塞状态。
消息基本传递过程大体是这样的:
1.Handler发送一个Message(消息)同时这个消息会插入MessageQueue(消息队列)中;
2.然后Looper会轮询这个消息队列,发现有消息,会取出这个消息;
3.取出来之后会把它传递给原来的Handler对象处理。
通过上面我们会有一个整体认识,下面通过源代码来进行剖析:
最开始UI主线程被创建的时候,在ActivityThread内部会创建一个唯一的Looper对象:
public static void main(String[] args) {
//代码省略...
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();//创建消息循环Looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
//代码省略...
Looper.loop();//执行循环消息
throw new RuntimeException("Main thread loop unexpectedly exited");
}
上面有注释的两块地方,开始了对Looper的操作处理—
第一个方法Looper.prepareMainLooper()是初始化一个Looper对象和一个消息队列
第二个方法Looper.loop()是Looper对象对消息队列进行轮询操作,发现里面有消息的时候就会发给Handler处理,没有消息的时候就Looper对象本身就会处于阻塞状态
再接着看一下Looper.prepareMainLooper()方法的源代码:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
从这里我们可以看到,这个方法内部首先调用了prepare(false),
prepare(false)内部它先调用了ThreadLocal的get()方法,来进行判断当前UI线程中是否有Looper对象变量值,如果有的话,get方法返回就不等于null,那么这里就会抛一个异常“only one Looper may be created per thread ”意思就是每个线程只允许创建一个Looper对象,如果Looper对象已经有了那么就不能再调用这个方法了,否则会抛上面异常。这里因为是第一次启动创建,所以一定是null,不会报异常。
再往下面执行的时候调用 sThreadLocal.set(new Looper(quitAllowed));
ThreadLocal会把新new 的Looper对象设置进去,下次调用sThreadLocal.get()的时候可以取出的取出这个变量的值,当然前提是在同一个线程中(这里是UI线程)。熟悉线程的朋友,知道这个ThreadLocal类作用就是存储与当前线程有关的变量,不涉及其他线程!
执行到最后的时候,会调用Looper.myLooper()将looper对象赋值给到Looper类里面的sMainLooper变量。
public static Looper myLooper() {
return sThreadLocal.get();//再当前线程中通过ThreadLocal取出我们设置的Looper对象
}
通过new Looper(quitAllowed)我们看一下Looper的带参构造方法,就会发现原来消息队列MessageQueue被Looper内部创建了.
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//Looper封装了消息队列
mThread = Thread.currentThread();
}
到这里,我们就会发现Android的UI线程中会有一个默认的Looper对象(而且是唯一的),它里面还封装了一个消息队列。这样Looper和MessageQueue就关联上了
还有一个Looper.loop()方法,我们到后面提到再说…
下面我们在看看Handler是如何与Looper和MessageQueue关联上的呢?
当我们在UI主线程中创建一个Handler对象的时候(比如在一个Actvity中创建 Handler mHandler=new Handler();)其实在这个过程中会把主线程里面的Looper和MessageQueue分别赋值给到Handler里面的对应变量,这个看一下 Handler的构造方发就会发现
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
//省略次要代码...
mLooper = Looper.myLooper();//这里赋值Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//这里赋值MessageQueue
mCallback = callback;
mAsynchronous = async;
}
1,mLooper = Looper.myLooper();//这里赋值Looper,当前是在UI主线程里面,会把当前线程中唯一的一个Looper传递过来(上面已经分析了);
同理mQueue = mLooper.mQueue;//这里赋值MessageQueue,也会把与Looper关联的唯一MessageQueue传递进来;
到这里也就是说我们创建的Handler对象里面包含了主线程的Looper和MessageQueue,这样Handler就和它们关联上了;
当使用Handler对象发送消息的时候会调sendMessage();
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
发送消息的方法内部调用了发送延迟消息的方法,从内部知道是从开机到现在时间的上延迟了0秒,接着把这个时间当做是消息发送时间,接着调定时发送消息的方法,这个方法里面会先拿出消息队列,后面又会把这条消息插入消息队列里面。这一点从以上代码看还是比较容易理解的。接着上面代码往下继续看代码enqueueMessage(queue, msg, uptimeMillis)…
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//这个this不就是当前的Handler
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg.target = this,把Handler对象赋给了Message中的target引用,这样这条消息中的target指向Handler.
而queue.enqueueMessage(msg, uptimeMillis)就是消息队列自己插入消息的方法了。
这条条消息Message里面的target(Handler类型)引用指向了我们最开始创建的Hanlder对象
看看消息队列MessageQueue的queue.enqueueMessage(msg, uptimeMillis)是如何插入消息的:
boolean enqueueMessage(Message msg, long when) {
//省略次要代码...
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null &&msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
代码比较长,其实也没有什么,就不全部贴出来了,只看重要的部分,用心看,你会明白,MessageQueue内部存储其实使用的单链表存储结构,它并不是一个什么队列容器,(而是一个消息内部包含另一个消息的应用,通过这个应用可以查找到下一个消息),通过这个方法可以将一个消息插入到消息的队尾里去,这样一条Message消息就成功插入到里面去了。
好了,前面的过程我们都了解的差不多了,我来看看最重要的一个方法 Looper.loop(),
上面已经说了,一旦消息队列俩面有消息,Looper就会轮询出来发送给Handler处理。
public static void loop() {
final Looper me = myLooper();//拿到当前UI线程中唯一的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//拿到当前UI线程中唯一MesssageQueue对象
//省略次要代码...
for (;;) {//看到了吗是一个无限循环
//阻塞的方法,没有消息的时候,MessageQueue会阻塞在这里
Message msg = queue.next(); //might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//省略次要代码...
msg.target.dispatchMessage(msg);//这条msg.target指向我们创建的Handler
//省略次要代码..
msg.recycle();
}
}
在这个方法里面先是取得前线程中的Looper对象,然后拿到消息队列,之后进入到for循环,这个for循环是一个死循环,第一行代码是注释可以知道,会阻塞,其实是一旦没有消息的时候就会休眠阻塞,一旦消息来了就会唤醒处理,这个next()方法里面也是消息队列链表的取出和删除操作,大家可以自己去读原码。这里取出刚才那条Message之后会调用 msg.target.dispatchMessage(msg)方法,msg.target指向的是放送这条消息的 Hanlder对象!呵呵,继续往下看看Handler类里面这个代码吧
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//判断Message自身Callback接口是否为null
handleCallback(msg);
} else {//到了Handler来处理消息了
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
别的不说了,就看看handleMessage(msg),这个是Handler的接口回调方法。我们最熟悉的吧。
最后总结一下:
Handler负责消息的放送和处理,Looper负责轮询消息队列中的消息。消息队列负责消息的插入和取出删除。