您的位置:首页 > 其它

深入了解Handler消息机制(一)

2016-04-13 21:50 239 查看
在Android开发了一段时间之后,很多读者应该知道了一个知识点:UI不能在子线程中更新。这本来是一个伪命题,因为并不是UI不可以在子线程中更新,而是UI不可以在不是它创建的线程中更新。只是绝大多数情况下UI都是从UI线程中创建的,因此在其他线程更新时会抛出异常。在这种情况下,当我们在子线程中完成了耗时操作之后,通常会通过一个Handler将结果传递给UI线程,然后在UI线程中更新相关的视图。

那么Handler,Looper的工作原理又是什么呢?它们之间是如何协作的?

在此之前我们需要了解两个概念,即MessageQueue和Message。其实Android应用是事件驱动的,每个事件都会转化为一个系统消息,及Message。消息中包含了事件相关的信息以及这个事件的处理人–Handler。每个线程中都会有一个默认的MessageQueue。这个消息队列维护了一个待处理的消息列表,有一个消息循环不断地从这个队列中取出消息,处理消息,这样就使得应用动态的运作起来。他们的运作原理就像工厂的生产线一样,待加工的产品就是Message,“传送带”就是MessageQueue,电动机就是Looper,工人们就对应于处理事件的Handler。这么一来Message就必然会产生很多的对象,因为整个应用都是由事件驱动的,即Message来驱动的,系统需要不断的产生Message,处理Message,销毁Message,难道Android没有IOS流畅就是这个原因吗?答案显然不是的。

在Message文档中有:

/*
* <p class="note">While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.</p>
*/


意思很清楚,是建议我们在使用Message的obtain方法获取Message对象,而不是通过Message的构造函数,因为obtain方法会从被回收的对象池中获取Message对象。

下面看看obtain方法:

// sometimes we store linked lists of these things
/*package*/ Message next;
private static Message sPool;
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();
}


看到上面的代码,是不是有些熟悉。对,就是链表。这个next指向的下一个Message的。每个Message对象都有一个同类型的next字段。这样一来所有的可用的Message对象就通过next串联成一个可用的Message池。在obtain方法中,首先会声明一个Message对象m,并且让m指向sPool。sPool实际上指向了m1,因此,m实际上也指向了m1,这里相当于保存了m1这个元素。下一步就是sPool指向m1的下一个元素,也就是m2。sPool完成后移就会把m.next赋值为null, 此时再把链表中的元素的个数减1,这样链表中的第一个元素就成功的脱离了原来的消息池队伍了。

但是那些Message对象什么时候被放到链表中的呢?我们在obtain方法中只看到了从链表中获取,没有看到将Message对象放进对象池的代码,那么到底线索在哪呢?

不知道注意到上面的注释的最后一句话:

which will pull them from a pool of recycled objects


相信大家也都明白了,原来在创建的时候是不会把Message对象放在池中的,在回收该对象时才会将该对象添加到链表中。其实Message类中有类似于Bitmap那样的recycle函数,具体代码如下:

void recycleUnchecked() {
// 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;

synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
public void recycle() {
//判断该消息是否还在被使用
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
//清空状态并将添加到消息池中
recycleUnchecked();
}


recycle函数会将一个Message对象回收到一个全局的池中,这个池也就是上文说到的链表。recycle函数首先判断该消息是否还在使用,如果还在使用就会抛出异常,否则调用recycleUnchecked函数处理该消息。recycleUnchecked函数中先清空该消息的各个字段,并且将flags设置为FLAG_IN_USE,表明该消息已经被使用,这个flag在obtain函数中会被设置为0,这样就能够追踪该消息的状态。然后判断是否要将该消息回收到消息池中,如果池的大小小于最大SIZE,则将自身添加到表头。当我们调用obtain方法的时候,如果池中有元素的话就会从池中获取表中的第一个元素。

现在已经很明朗了,Message通过在内部构建一个链表来维护一个被回收的Message对象的对象池,当用户调用obtain函数时会优先从池中获取,如果池中没有可以复用的对象则可以创建这个新的Message对象。这个新创建的Message对象在被使用完之后会被回收到这个对象池中,当下次再调用obtain函数时它们就会被复用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: