在android的消息机制中,Message其充当着信息载体的一个角色,通俗的来说,我们看作消息机制就是个工厂的流水线,message就是流水线上的产品,messageQueue就是流水线的传送带。之前做面试官的时候,经常会问面试者关于message的问题,如:
1.聊一下你对Message的了解。
2.如何获取message对象
3.message的复用(如果以上问题能答对,加分)
在下面我带着这三个问题,从这段代码开始逐一解析。
/**
* 创建一个handler
*/
Handler handler = new Handler();
/**
* 模拟开始
*/
private void doSth() {
//开启个线程,处理复杂的业务业务
new Thread(new Runnable() {
@Override
public void run() {
//模拟很复杂的业务,需要1000ms进行操作的业务
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
@Override
public void run() {
//在这里可以更新ui
mTv.setText("在这个点我更新了:" + System.currentTimeMillis());
}
});
}
}).start();
}
我们创建了一个handler,在doSth()中开启线程模拟处理复杂业务,最后通过handler的post返回结果进行UI操作(子线程不能进行操作UI,后话),我们先从handler的post开始看起,
Handler.java:
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r) {
//通过getPostMessage获取了message,再往下看
return sendMessageDelayed(getPostMessage(r), 0);
}
在post中,我们传进一个Runnable参数,我们发现有一个getPostMessage(r)函数,我们先从getPostMessage()下手。
Handler.java:
private static Message getPostMessage(Runnable r) {
//在这里,获取一个message,把我们的任务封装进message
Message m = Message.obtain();
m.callback = r;
return m;
}
从getPostMessage函数可得,我们把参数Runnable封装进去message的callback变量中,在这里埋伏一个很重要的概念,在Handler的源码中,是如何获取message对象的。顾名思义,在getPostMessage中,我们就是为了获取把runnable封装好的message。这样,我们可以返回上一层,继续看函数sendMessageDelayed(Message,long)。
Handler.java:
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
//顾名思义的delay,也就是延迟,在上一层我们看到了post里传参是0,继续往下看
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//在这里判断handler里的队列是否为空,如果为空,handler则不能进行消息传递,因为生产线的传送带都没有的话,还怎么进行传送
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);
}
在handler中,存在着sendMessageDelayed最终会用sendMessageAtTime,只是sendMessageDelayed中传参为0,使得sendMessageAtTime这函数最大程度能复用,我们继续往enqueueMessage函数看去。
Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//在message中放一个标记
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//在这里把消息放到队列里面去
return queue.enqueueMessage(msg, uptimeMillis);
}
在enqueueMessage函数中,我们发现有个入参queue,这个入参就是消息队列,也就是之前我所说的流水线的传送带,message需要通过传messagequeue进行传递,我们继续往下探索。
MessageQueue.java:
boolean enqueueMessage(Message msg, long when) {
//这里通过之前的判断,之前放的目标,还有这个消息是否已经在使用了,都需要判断
//还记得之前我们看到的Message是怎么获取的吗?Message.obtain(),这里需要判断msg.isInUse,是否已经在使用这个消息
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//这里就是真正把message放到队列里面去,并且循环复用。
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
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 {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
enqueueMessage先判断之前的target是否为空,以及这个message是否已使用,后面的代码则是把message放进队列中,往下我们就不探究了,我们看到最终返回的结果return true.
我们看回来此段代码:
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r) {
//通过getPostMessage获取了message,再往下看
return sendMessageDelayed(getPostMessage(r), 0);
}
@return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting.
我们一层一层往下探索,无非就是把这个这个执行UI操作的Runnable封装成message,再将这个message放进我们的消息队列messagequeue中。在post如果返回true则成功添加进去消息队列,如果返回false则代表失败。
这个流程相信大家也清晰了吧,现在我之前所说的问题,handler中如何获取message对象的。
Handler.java:
private static Message getPostMessage(Runnable r) {
//在这里,获取一个message,把我们的任务封装进message
Message m = Message.obtain();
m.callback = r;
return m;
}
在这里,为什么Message不是通过new一个对象,而是通过其静态函数obtain进行获取?
我们通过其源码继续探索:
Message.java:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases.
我们从注释中看到pool这个词,这个就是池,大家应该也听过过线程池,对象池,没错,我们获取的message对象优先在这个message池里获取,如果池里没有再new一个新的Message.
我们先了解一下,这里面的sPool、next、sPoolSize到底是什么东西。
Message.java:
//池里的第一个对象
private static Message sPool;
//对象池的长度
private static int sPoolSize = 0;
//连接下一个message的成员变量
// sometimes we store linked lists of these things
/*package*/ Message next;
在Message这个类中,存在着一个全局变量sPool,sPoolSize则是对象池中的数量,还有一个成员变量next.我们得理清一下sPool跟next到底存在着什么关系。在这先提出一个问题,我们看了那么久的池,怎么没看到类似Map这样的容器呢?Message对象池其实是通过链表的结构组合起来的池。
上面有三个message,分别为message1、message2、message3
他们的连接关系分别通过其成员变量next进行衔接,举个例子:
message1.next=message2
message2.next=message3
......
以此类推,那么我们了解了message的next有什么作用,那么sPool呢?
我们注意到sPool是全局变量,我们又看回obtain函数中,是怎么样获取的。
Message.java:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
//在池中获取message也是从表头获取,sPool赋值给message,
//同时把其连接的next赋值给sPool(这样,连接起来的message从第二个位置放到表头上了),赋值后设置next为空
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
我们看到源码中,先判断sPool为不为空,为空代码这个池的数量为0,不能从池里获取到message.那如果不空,先将sPool赋值给message,再将这个message的下一个next赋值给sPool,赋值完后将message的next设为空,这不就是从表头里获取数据,sPool就是表头的第一个message。如:
message1是表头第一个元素,sPool也是表头,指向message1。当message1从池中取出来时候,message1连接的message2(通过next),成为了表头,同时sPool也指向新的表头,sPoolSize的数量也相应的需要减少。
通过以上例子,我们了解message的结构,也明白了message如何获取,别忘了我们的message除了在池里获取,还能通过创建一个新的实例,那么,新的实例是怎么放进池的,下面开始看看message的回收。
Message.java:
/**
* Return a Message instance to the global pool.
* <p>
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
* </p>
*/
public void recycle() {
//如果还在使用这个消息,不能进行回收--通过flag进行标示
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
Recycle()函数是怎样调用的,暂且先不讨论,我们先看看其回收的机制,先判断这个message是否使用状态,再调用recycleUnchecked(),我们重点看看这个函数。
Message.java
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
//这里才是真正回收message的代码,把message中的状态还原
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
//如果message池的数量未超过最大容纳量(默认最大50个message容量),将此message回收,以便后期复用(通过obtain)
//在代码中可知,message在recycle()中进行回收的
//假设池中的message数量为0时,sPool全局变量为null
//当我们把第一个message放进去池的时候,sPool(这个时候还是null)赋值给next,而message本身赋值给全局变量sPool,也就是每次回收的message都会插入表头
//这样一来就形成了链表的结构,也就是我们所说的对象池
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
我们看到源码中的最后几行,如果池中现有的数量少于最大容纳量,则可将message放进池中,我们又看到了头疼的next跟sPool,我先举个例子,脑补一下:
1.我有一个message1,我用完了,系统回收这个message1
2.现有的池,表头是message2。
结合以上两个条件再根据源码能得出:
sPool跟message2都指向同一个地址,因为message2是表头,那么message1回收的时候,sPool赋值给了message1的next. 也就是说,message1成了新的表头,同时池的数量sPoolSize相应的增加。
message的回收就是将其放到池的表头,包括获取message也是从表头上获取。
总结:
Android的消息机制都通过message这个载体进行传递消息,如果每次我们都通过new这样的方式获取对象,那么必然会造成内存占用率高,降低性能。而通过对其源码的学习,了解message的缓存回收机制,同时也学习其设计模式,这就是我们所说的享元模式,避免创建过多的对象,提高性能。