Android中的消息机制主要指 Handler的运行机制 以及 MessageQueue,Looper的工做过程 ,三者相互协做,保证着消息的接收,发送,处理,执行。html
图片来自郭神的《第一行代码》java
先简单的介绍一下 Android 中 消息机制你们庭的主要成员 :android
Handler : 是Android消息机制的上层接口,最为你们经常使用,至关于Android消息机制的入口,咱们经过使用Handler
发送消息来引发消息机制的循环。一般用于:在子线程执行完耗时任务完后,更新UI。程序员
MessageQueue : 存储 消息(Message) 对象的消息队列,实则是单链表结构.安全
Looper : 用于无限的从 MessageQueue
中取出消息,至关于消息的永动机,若是有新的消息,则处理执行,若没有,则就一直等待,堵塞。Looper** 所在的线程是 建立 Handler
时所在的线程。bash
主线程建立Handler时,会自动建立一个Looper,可是子线程并不会自动建立Looper多线程
ThreadLocal : 在每一个线程互不干扰的存储,提供数据,以此来获取当前线程的Looper
ide
ActivityThread : Android 的主线程,也叫UI线程,主线程被建立时 自动初始化主线程的 Looper
对象。oop
看完《Android艺术开发探索》 这本书的第10章以后我也才明白post
这个很简单了,若是为不一样的线程访问同一UI元素加上锁机制,那咱们程序员写相关代码的时候会变得超级麻烦。。。 改个UI还得考虑它是否是已经被别的线程占用了,被占用了,还得让那个线程释放锁。。。线程再多一点的话,大大地加大了程序员地工做量.
并且加上锁机制无疑会因为线程堵塞地缘由下降访问UI的效率,帧率下降,体验也会不友好。
让UI元素只能再主线程访问就会省下不少事,建立一个Handler
就好了。
下面从总体概述一下 消息机制的整个工做过程 :
Handler
建立时会采用当前线程的 Looper
来构建内部的消息循环系统,若是Handler在子线程,则一开始是没有Looper
对象的(解决方法稍后介绍),主线程ActivityThread
默认有一个Looper。
Handler
建立完毕,经过 post
方法传入Runnable
对象,或者经过sendMessage(Message msg)
发送消息。
post()
方法里也是经过调用send()
实现的
send()
方法被调用后,调用 MessageQueue的enqueueMessage()方法将消息发送到消息队列中,等待被处理。
Looper
对象运行在Handler
所在的线程,从MessageQueue消息队列
中不断地取出消息,处理,因此业务逻辑(一般是更新UI)就运行在Looper的线程中。
接下来从局部来分析消息机制的每一个成员。
ThreadLocal是一个线程内部的数据存储类,经过它能够在指定的线程中得到存储数据,得到数据,线程之间的ThreadLocal相互独立,且没法得到另外一个线程的TheadLocal.
相对整个程序来讲,每一个线程的ThreadLocal是局部变量。
相对一个线程来讲,线程内的ThreadLocal是线程的全局变量
ThreadLocal是一个泛型类,能够存储任意类型的对象。
示例:
public class ThreadLocalTest {
public static void main(String[] args) {
ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<Boolean>();
mThreadLocal.set(true);
System.out.println("#Main Thread : ThreadLocal " + mThreadLocal.get());
new Thread( new Runnable() {
@Override
public void run() {
mThreadLocal.set(false);
System.out.println("#1 Thread : ThreadLocal " + mThreadLocal.get());
}
}).start();
new Thread( new Runnable() {
@Override
public void run() {
System.out.println("#2 Thread : ThreadLocal " + mThreadLocal.get());
}
}).start();
}
}
复制代码
咱们在主线程建立一个 泛型为Boolean
的ThreadLocal,并.set(True)
,而后在第一个子线程中.set(False)
,在第二个子线程中不作修改,直接打印。 能够看到,在不一样的线程中得到的值也不一样。
输出 :
#Main Thread : ThreadLocal true
#1 Thread : ThreadLocal false
#2 Thread : ThreadLocal null
复制代码
首先每一个线程内部都维护着一个ThreadLocalMap
对象
Thread.Java
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
复制代码
这个ThraedLocalMap 与Map相似,一个线程内能够有多个ThreadLocal类型变量,因此经过ThreadLocalMap <ThreadLocal<?> key, Object value>
.保存着多个<ThreadLocal , 任意类型对象>键值对。
看一下ThreadLocal的set()
方法实现 :
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
复制代码
先是得到当前线程的ThreadLocalMap
对象,map.set(this,value) 设置了我这个ThreadLocal存储的值.
get()
方法实现 :
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */
public T get() {
Thread t = Thread.currentThread();//得到当前线程
ThreadLocalMap map = getMap(t);//根据根据得到它的ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//得到<k,v>键值对
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;//经过<k,v>得到值
return result;
}
}
return setInitialValue();
}
复制代码
通常,当某些数据是以线程为做用域,而且不一样的线程具备不一样的数据副本时,能够考虑用ThreadLocal
对于Handler
,它想要得到当前线程的Looper
,而且Looper
的做用域就是当前的线程,不一样的线程具备不一样的Looper
对象,这时可使用ThreadLocal。
复杂逻辑下的对象的传递,若是想要一个对象贯穿着整个线程的执行过程,可采用Threadlocal让此对象做为该线程的全局对象。
以单链表的形式,存储着Handler
发送过来的消息,再来一张图加深印象
主要包含两个操做:
enqueueMessage(Message msg,long when)
,像队列插入一个消息,这里为了节省篇幅,就不上源码,贴上源码链接,MessageQueue.enqueueMessage()next()
从无限循环队列中取出消息,并从消息队列中删除。MessageQueue.next()虽然它叫作消息队列
,但内部实际上是以单链表的结构存储,有利于插入,删除的操做。
它的主要做用就是 不停地从消息队列中 查看是否有新的消息,若是有新的消息就会马上处理,没有消息就会堵塞。
持有MessageQueue
的引用,而且会在构造方法中初始化
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
复制代码
前面说到主线程本身会建立一个Looper
对象,因此咱们在主线程使用Handler
的时候直接建立就能够了。
可是在子线程使用Handler的话,就须要咱们手动建立Looper
了,
示例:
new Thread() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();
复制代码
prepare()
源码以下:
/** Initialize the current thread as a looper. 77 * This gives you a chance to create handlers that then reference 78 * this looper, before actually starting the loop. Be sure to call 79 * {@link #loop()} after calling this method, and end it by calling 80 * {@link #quit()}. 81 */
82 public static void prepare() {
83 prepare(true);
84 }
85
86 private static void prepare(boolean quitAllowed) {
87 if (sThreadLocal.get() != null) {
88 throw new RuntimeException("Only one Looper may be created per thread");
89 }
90 sThreadLocal.set(new Looper(quitAllowed));
91 }
复制代码
能够看到最终是调用了 此 Looper 所在线程的 **ThreadLocal.set()**方法,存了一个Looper对象进去。
除了prepare()
,还有一些其余方法,咱们也须要知道
loop() : 启动消息循环,,只有当Looper调用了loop()以后,整个消息循环才活了起来
prepareMainLooper() : 给主线程建立Looper对象
getMainLooper() : 得到主线程的Looper对象
quit() : 通知消息队列,直接退出消息循环,不等待当前正在处理的消息执行完,quit以后,再向消息队列中发送新的消息就会失败( Handler的send()方法就会返回false )
public void quit() {
mQueue.quit(false);
}
复制代码
quitSafety() : 经过消息队列,再也不接收新的消息,等当前的消息队列中的消息处理完就退出。
public void quitSafely() {
mQueue.quit(true);
}
复制代码
下面分析loop()
的实现:
/** 119 * Run the message queue in this thread. Be sure to call 120 * {@link #quit()} to end the loop. 121 */
122 public static void loop() {
123 final Looper me = myLooper();
124 if (me == null) {
125 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
126 }
127 final MessageQueue queue = me.mQueue;
128
129 ...//省略部分代码
133 //从这里开启无限循环,直到 没有消息
134 for (;;) {
135 Message msg = queue.next(); // might block
136 if (msg == null) {
137 // No message indicates that the message queue is quitting.
138 return;
139 }
140
141 // This must be in a local variable, in case a UI event sets the logger
142 Printer logging = me.mLogging;
143 if (logging != null) {
144 logging.println(">>>>> Dispatching to " + msg.target + " " +
145 msg.callback + ": " + msg.what);
146 }
147
148 msg.target.dispatchMessage(msg);
149 ...//省略部分代码
166 }
167 }
复制代码
在 for 循环里 :
msg.target.dispatchMessage(msg);
,target是发送此消息的 Hander对像,通知Handler调用dispatchMessage()
来接收消息。Handler的主要工做就是 发送消息,接收消息。
发送消息的方式有post(),send(),不过post()方法最后仍是调用的send()方法
发送消息的过程:
send类型的发送消息方法有不少,而且是嵌套的
sendMessage()
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
复制代码
sendMessageDelayed()
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
复制代码
sendMessageAtTime()
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);
}
复制代码
三种send的发送消息方式,最后都会经过enqueueMessage()
来通知消息队列 插入这条新的消息。
Handler.enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//调用消息队列的enqueueMessage()
}
复制代码
接收消息的过程
接收消息由dispatchMessage(Message msg)
为入口
dispatchMessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
这里的callback
是咱们调用 post(Runnable runnalbe) 时传入的Runnable
对象,若是咱们传入了Runnable
对象
就会执行Runnable的
run方法:
private static void handleCallback(Message message) {
message.callback.run();
}
复制代码
若是没有经过post
传Runnable
,就会看建立Handler时的构造方法中有没有传Runnable
参数,传了的话由mCallback
存储。
这个mCallback
是Handler内部的一个接口
public interface Callback {
public boolean handleMessage(Message msg);
}
复制代码
若是构造Handler时也没有传Runnable
对象,最终会执行handleMessage(msg)
,这个 方法就是咱们建立handler时重写的handleMessage()方法.
参考资料: Android艺术开发探索
(完~)