在Android中为啥建议你用Message.obtain()方法获取Message对象,而不是使用new关键字?
大家都知道Android应用程序是通过消息来驱动的。每开启一个应用进程,我们都会在当前应用进程的主线程成创建一个消息队列,和Handler来处理应用程序中的消息。不管是刷新界面,或者是启动组件等等,都和消息相关。所以Message对象在我们的应用程序中使用时很频繁的。
一般情况下,我们会通过Handler去发送一个消息,而这个消息(即Message对象)我们可以通过关键字new进行创建,也可以通过Message.obtain()方法进行创建。在Android源码中,我们在Message的构造方法上方可以看到如下一段注释:
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}). */ public Message() { }
这段注释的意思是:更加建议我们使用Message.obtain()方法来获取一个Message对象。
为什么Android官方更加建议我们使用Message.obtain()方法呢?下面我将从源码角度,带大家了解一下其中的奥妙。
既然它建议我们使用obtain()方法,那我们就来看看它为我们做了些什么事。
以下源码都源码基于Android8.0。
1.解析Message.obtain()方法:
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(); }
可以看到在obtain方法中,如果sPool不为null的时候,还是会调用new Message()来创建一个Message对象。那么这个sPool又是个什么东西呢?其实呢从上面代码我们就可以看出来,它其实是一个链表。每个Message对象都有next字段,它的类型也是Message。上面的代码其实就是把链表中的第一个节点返回,然后把sPool置为第一个Messgae的next对象,即下一个节点。那我们这个sPool对象是怎么生成的呢?我们在来找一找答案。我们在Message类中搜索一下sPool这个对象,看看什么时候会往它里面添加Message对象。我们可以找到recycleUnchecked()方法,recycleUnchecked()是在recycle()方法中被调用的。
2.解析recycle()方法和recycleUnchecked()方法
public void recycle() { //判断当前Message对象是否能被回收 if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } //可以回收调用recycleUnchecked()方法 recycleUnchecked(); } void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. //清除Message对象里面成员变量的信息 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) { //当sPoolSize大小小于MAX_POOL_SIZE即50时,继续往池中添加。 //sPoolSize可以理解为Message池内Messgae对象的数量。 if (sPoolSize < MAX_POOL_SIZE) { //当前Message对象当作第一个链表的第一个节点 next = sPool; sPool = this; sPoolSize++; } } }
好了现在我们知道了当Message对象被回收的时候,会像池中添加一个Messgae对象。那么什么时候会回收一个Messsge对象呢。可以猜想以下,一个对象不用了之后才会被回收,在Android消息机制中,Looper会通过loop()方法想消息队列中去消息,我们去loop()方法中找找,看看是否会调用我们Message的回收方法。
3.解析Looper.loop()方法
源码如下:
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); // Allow overriding a threshold with a system prop. e.g. // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start' final int thresholdOverride = SystemProperties.getInt("log.looper." + Process.myUid() + "." + Thread.currentThread().getName() + ".slow", 0); boolean slowDeliveryDetected = false; //无限循环 for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //省略部分代码 ..... ..... try { //调用Handler的dispatchMessage来处理消息 msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } //此处省略部分代码 ..... ..... //调用回收处理完成的Message的方法 msg.recycleUnchecked(); } }
可以清楚的看到当我们的Looper对象处理完我们的Message时,会去调用回收Message的方法,向我们的Message对象中的复用池中添加一个Message对象。
4.总结
分析到这里,我相信所有朋友都明白了。Message类里维护了一个sPool对象。可以理解成一个Message链表,这个链表默认的最大长度是50。在Android消息机制中,每当一个Message对象被处理完成之后,都会被放入这个池中,为我们提供了复用。当我们调用Message.obtain()方法时,如果复用池中存在Message对象,我们就不会去创建一个新的Message对象。这样就避免了频繁创建和销毁Messgae对象的带来的性能开销。
更多相关文章
- Android(安卓)用户界面---操作栏(Action Bar 三)
- Android(安卓)UI设计小知识——富文本
- Android(安卓)TextView中标点符号或英文导致自动换行问题
- Android序列化详解及最佳实践(Serialize&Parcel)
- Launcher功能的修改及添加,本篇是一些小功能的展示,通知栏显隐,dock
- 《Android开发艺术探索》之学习笔记(三)View的基础知识
- 三种方法,刷新 Android(安卓)的 MediaStore!让你保存的图片立即出
- Android(安卓)launcher动态Icon的实现方法
- 防止Android过快点击造成多次事件的三种方法