title | date | categories | tags | description | ||||
---|---|---|---|---|---|---|---|---|
把Handler原理说明白 |
2018-08-10 |
|
|
|
在安卓当中提供了两种方式来解决线程之间的通信,一种是AsynchTask,另外一种就是现在我们主要分析的Handler。
Handler是Android类库提供的用于接收、传递和处理消息或Runnable对象的处理类,它结合Message、MessageQueue和Looper类以及当前线程实现了一个消息循环机制,用于实现任务的异步加载和处理。
简单说来,就是Handler将Message丢到MessageQueue中,Looper不断从MessageQueue中取出Message,取出之后,由Handler进行处理。这是最主要的流程,我们要带着这个思路,进行接下来的分析。
-
在 主线程中使用(请忽略内存泄漏的问题, 后续再说)
public class MainActivity extends AppCompatActivity { Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); handler.sendEmptyMessage(1); handler.postDelayed(task1, 10000); new Thread(new Runnable() { @Override public void run() { handler.sendEmptyMessage(2); handler.postDelayed(task2, 20000); } }).start(); } }
-
在子线程中使用。 从
Looper.java
中copy过来的。class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
下面我们提出几个问题:
Q1:为什么在主线程使用的时候不需要Looper.prepare();
和 Looper.loop();
?
Q2:为什么在子线程中这样用?
Q3:为什么在主线程 调用handler.sendEmptyMessage(1);
handler.postDelayed(task1, 10000);
之后, 最后的处理在主线程中进行的?
Q4:为什么在子线程调用handler.sendEmptyMessage(2);
handler.postDelayed(task2, 20000);
之后,最后的处理也是在主线程中进行的?
why??
慢慢分析
以下分析基于 android 2.3.5 源码。
Handler主要完成Message的入队(MessageQueue)和处理,下面将通过Handler的源码分析其消息分发、处理流程。来看下Handler类的方法列表:
首先来看Handler的几个构造方法:
/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
// callback默认为null
mCallback = null;
}
无参构造, 主要是给mLooper、mQueue、mCallback几个成员变量赋值。
/**
* Constructor associates this handler with the queue for the
* current thread and takes a callback interface in which you can handle
* messages.
*/
public Handler(Callback callback) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
}
带一个参数Callback的构造, 一样是给mLooper、mQueue、mCallback几个成员变量赋值。
/**
* Use the provided queue instead of the default one.
*/
// 提供looper的构造
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
带一个参数Looper的构造。
可以看到, 几个构造都是在给mLooper、mQueue、mCallback几个成员变量赋值。这几个变量定义在 Handler.java 文件的最后。稍后, 我们将看到它们是如何被使用的。
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
显然,Handler 就是把消息发送到这个 MessageQueue 中了, 然后再由这个 Looper 将消息从这个 MessageQueue 中取出来,他们之间具体是如何协作的,并且如何实现线程切换的,我们在讲 Looper 的时候再说。
下来我们看一下Handler 是如何创建Message的。
在使用过程中, 我们可能会直接调用Message的构造方法来创建一个新的Message。但是, 这并不是一个好的习惯。在Handler 和 Message中都给我们提供了一系列obtain方法, 我们应该使用obtain方法从消息池中获取对象来使用。因为通过obtain方法获取到的消息对象, 在使用完之后, 消息池会自动帮我们回收(是在Looper.loop()的最后调用了msg.recycle();
回收资源)。
Handler的obtainMessage系列方法
public final Message obtainMessage()
{
return Message.obtain(this);
}
public final Message obtainMessage(int what)
{
return Message.obtain(this, what);
}
public final Message obtainMessage(int what, Object obj)
{
return Message.obtain(this, what, obj);
}
public final Message obtainMessage(int what, int arg1, int arg2)
{
return Message.obtain(this, what, arg1, arg2);
}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
可以看到全部都是调用了Message.obtain()
方法构造一个Message对象出来。第一个参数传入this, 是指定 Message的 target 为当前的Handler。
拿到了Message之后, 就可以通过sendMessage系列方法, 将Message发送到消息队列(MessageQueue)了。
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
// 所有的post方法, send方法, 都间接调用这里
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;// 返回值, 是否enqueue成功
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;// 将msg的target属性设置成当前handler
// 在 Message.obtain()中已经指定 target 为 this 了,这里为啥还指定?
// 因为有些人创建Message的时候不用 Message.obtain(),自己 new Message()就没指定 target
sent = queue.enqueueMessage(msg, uptimeMillis);// 入队
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
透过源码, 我们可以发现, 在sendMessage方法内部, 创建Message对象的时候也是使用Message.obtain()
方法来创建消息的。而且, 所有的sendMessage方法内部最后都是调用到了sendMessageAtTime(Message msg, long uptimeMillis)
在sendMessageAtTime(Message msg, long uptimeMillis)
中, 首先拿到当前关联的MessageQueue, 然后给Message的target指定为当前Handler(后续将Message从queue中取出后, 就是用这个target来处理的), 然后调用enqueueMessage方法, 将Message加入到queue中, 并且返回是否加入成功。
下面我们再看一下post系列方法
/**
* 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.
*
* 将runnable 加入message queue, 这个runnable将在与handler关联的线程中被执行
* @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.
* 如果这个runnable被成功放在messagequeue中, 则返回true
* 出错的话就返回false(那在什么情况下会出错呢,一般是正在退出的情况, 也就是在messagequeue中加
了一个target=null的message, looper死循环要停止了, 就返回 false)
*/
public final boolean post(Runnable r)
{
// 将runnable封装成message对象发出
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
透过源码, 我们可以看到, 所有传入的Runnable对象, 都用getPostMessage()
包装成一个 Message 对象, 然后调用了sendMessage相关的方法, 通过上面的分析我们知道, sendMessage相关方法, 最终都间接调用了sendMessageAtTime(Message msg, long uptimeMillis)
。
我们看一下 getPostMessage()
是如何将Runnable包装成Message的。
/**
* 将runnable对象封装成message对象
*/
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;// 对message中的callback赋值为runnable
return m;
}
很简单, 就是先调用Message.obtain()
拿到空消息对象, 然后给消息的callback属性赋值为Runnable。
到这里, 我们的Message已经被创建好啦。我们下面看一下这个 Message 是如何被丢在MessageQueue中的。
MessageQueue 的底层实现全部放在的native层。 我们主要分析 boolean enqueueMessage(Message msg, long when)
和 Message next()
这两个方法, 便可大概了解MessageQueue 内部的实现逻辑。
// 将 msg 加入到 MessageQueue 中
final boolean enqueueMessage(Message msg, long when) {
// 在调用enqueueMessage()之前, Message对象的when属性是不支持设置的.
// Message的when属性要在下面设置.
// 如果when != 0, 则认为这个Message当前正在被使用.
if (msg.when != 0) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
// msg.target == null 是指定当前的循环器 Looper 退出循环的标志。在 Looper 的源码中可以看到。
// 只有main线程 mQuitAllowed 是 false, 即 main 线程的 Looper 循环不可以退出;
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
final boolean needWake; // 是否需要唤醒
synchronized (this) {
if (mQuiting) { // 正在退出
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) { // target == null, 表示将要退出Loop的循环了.
mQuiting = true;
}
// 如果 mQuiting == false && msg.target != null 才会走到这里
// 这里给msg.when赋值, 也就是何时处理这个Message的时间
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
// 这个mMessages是队列的头
Message p = mMessages;
// 如果队列是空的, 或者指定 的 当前正在入队的这个Message的执行时间是比队头的Message执行时间早, 那么当前的这个Message应该放在队头, 第一个被处理.
if (p == null || when == 0 || when < p.when) {
msg.next = p; // 把之前的队列接在当前的这个msg的屁股后面
mMessages = msg; // 更新头指针, 头指针指向当前的这个Message
needWake = mBlocked; // new head, might need to wake up
// 队列里有消息了, 或许需要唤醒一下.
// 他这里 用 是否阻塞的值 赋给了 是否需要唤醒. 合理的, 阻塞了才需要唤醒嘛.
} else {
// 队列不是空, 或者当前入队的这个msg不是放在队头的, 需要另外处理一下.
Message prev = null;
// 遍历链表, 找到当前msg应该插入的合适的位置.也是根据时间when来判断的.
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up 不需要唤醒
}
}
if (needWake) { // 如果需要唤醒, 调用native层实现去唤醒.
nativeWake(mPtr);
}
// 执行到这里, 表示入队成功, 返回true.
return true;
}
注释已经写了,首先进行异常判断,然后给msg.when赋值, 这个when属性是说这个Message 应该在什么时间被处理, 比如我们一般写 sendEmptyMessageDelay()
延时5秒后处理, 其实就是给这个when设置值。
之后分两种情况, 第一种是当前这个msg要放在队头的情况(当队列为空或者当这个msg要最早被执行的情况,msg要放在队头), 第二种情况, 是要把msg插入到队列的合适位置。比如队列中目前有两个Message, 一个是1秒后执行, 一个是5秒后执行,我们enqueue的这个msg要在3秒后执行, 那么就插入到 1秒 和 5秒之前的位置。其实他这个队列是按照执行时间的优先级来排序的, 需要最早处理的放在最前面,对吧。
加入之后, 判断是否需要唤醒, 如需要, 调用底层代码唤醒.
最后返回的boolean 表示enqueue 是否成功了.
下面我们再看一下 是如何从队列中取出的
// 从队列中取出Message对象
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
// 死循环
// 这里没有用while(flag)的形式, 因为 while(flag)的形式,可以通过反射设置 flag 的值,进行控制循环,所以这里用了 for(;;)
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages; // 拿到队头的Message对象, 用msg记录一下.
if (msg != null) { // 判空
final long when = msg.when; // 拿到队头的Message对象的执行时间
if (now >= when) { // 如果当前时间 >= 执行时间 , 也就是说已经到了或者过了执行时间啦.赶紧取出执行吧
mBlocked = false; // 不阻塞
mMessages = msg.next; // 头指针后移一个. 这个msg就是要取出的了.
msg.next = null; //
if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
return msg; // 返回取出的msg
} else { // 还没到执行时间
// nextPollTimeoutMillis指的是还要多久才执行
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
// If first time, then get the number of idlers to run.
if (pendingIdleHandlerCount < 0) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount == 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
最主要的都注释了, 没注释的就是我没看懂的....
其中有个变量 mAllowQuit
默认是true, 表示当前你能否退出。 只有主线程的是不可以退出的,其他子线程都是可以退出的。
何为退出? 就是 往 MessageQueue里面丢一个target == null 的 Message, 就代表说要退出啦,其实就是告诉 Looper不要再从MessageQueue里面取Message处理了。在Loop中我们会看到具体的实现。
boolean mQuitAllowed = true;
终于轮到 Looper 出场了。
先看看类注释哈。
/**
* 该类用于在线程中运行一个消息轮询器.
* 线程在默认情况下是没有消息轮询器和它关联的.
* 可以在线程中调用prepare()方法去创建一个消息轮询器并运行起来, 然后调用loop()方法去处理message, 直到loop停止.
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
大多数情况下, 与消息轮询器相互作用的都是通过handler. 也就是Handler把Message丢在MessageQueue中, 然后消息轮询器去取出消息, 然后再交给Handler处理.
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
下面是个典型列子, 实现looper的thread
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
*/
注释里面说的很清楚,Looper类是用于在线程中运行一个消息轮询器。线程在默认情况下是没有消息轮询器和它关联的,可以在线程中调用prepare()方法去创建一个消息轮询器并运行起来, 然后调用loop()方法去处理message, 直到loop停止。
那我们来看看prepare()方法做了什么。
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
/** fan:
* 将当前线程初始化成为 一个 Looper线程
* 在真正开始轮训之前, 给你提供一个机会, 让你创建handlers并且引用looper
*
*/
public static final void prepare() {
// 如果没有调用 过prepare就返回null
// 每个线程只能有一个looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 调用Looper的私有构造, 创建一个Looper对象, 放到threadlocal中
// 这里之所以要做成ThreadLocal是因为,prepare方法可能在不同的线程中调用,比如main线程,比如需要loop能力的其他子线程, 不管哪个线程使用的时候,比如get set,都只是针对自己线程里面的变量操作,不影响其他线程
sThreadLocal.set(new Looper());
}
Looper中有个变量 sThreadLocal, 我们看看他是干嘛.
private static final ThreadLocal sThreadLocal = new ThreadLocal();
参考这篇文章,一定要弄明白 ThreadLocal 是干嘛的, 否则一直云里雾里,弄不清线程是如何切换的。
这个ThreadLocal 就是个在线程作用域内的变量。
每个线程只能关联一个Loop, 如果已经关联过了, 再调用prepare()方法, 就会抛出异常。
后面调用了Looper的私有构造方法, 给其内部的 mQueue mRun mThread
几个变量赋值。
构造好了Looper对象之后, 调用了 threadLocal 的 set()方法, 其内部就是拿到当前线程(currentThread)中的那个map, 然后把 ThreadLocal对象作为key, Looper对象作为value的键值对, 存储在了当前线程中的那个map中。 之后再get(), 就是从当前线程的那个map中, 根据 ThreadLoca对象, 拿到Looper对象。
那为啥每个线程只能关联一个Looper对象? 因为Looper类中的这个ThreadLocal对象是static的。。。而且他是 饿加载, 提前new好了的。
因此当你在不同的线程中调用prepare()的时候, 就是给线程中的那个 ThreadLocalMap 中放了 (sThreadLocal, new Looper()) 这样一个键值对。 然后你在不同的线程中再get(), 拿到就是刚set进去的Looper对象。prepare()之后,sThreadLocal 就有值了。
// 私有的构造方法
// 因为Looper的创建时在prepare中, 所以这里private
// 在 Looper 内部持有对 MessageQueue 的引用
private Looper() {
// 创建MessageQueue
mQueue = new MessageQueue();
mRun = true;
// 赋值当前线程对象
mThread = Thread.currentThread();
}
按照注释中给定的代码示例,prepare()之后, 创建一个 Handler 出来,我们回过头来看看前面 Handler 的构造。
/**
* Constructor associates this handler with the queue for the
* current thread and takes a callback interface in which you can handle
* messages.
*/
public Handler(Callback callback) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
}
构造方法中调用了Looper.myLooper();
,就是拿到在 prepare()中 set 进去的那个 Looper 对象,为 Handler 中的 mLooper 、mQueue 赋值。
比如:我们在子线程 t1 中调用了 prepare()方法, 那么就给 t1 线程中的 sThreadLocal 设置了一个 Looper 对象(其实质是给当前线程 t1 中的那个 map 中存放了一个<sThread, looper>的键值对)。然后在Handler 的构造中拿到这个 Looper 对象,并给 Handler 中的相关属性赋值(主要是 mMessageQueue)。之后,Handler 发送消息,就发在了这个 MessageQueue 中,也就是说,这个 MessageQueue 也是在 t1 线程中的。
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
之后, 我们就要loop()了, 取出消息并处理它。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static final void loop() {
// 拿到当前线程关联的looper, 这个 looper 还是之前 set 的那个Looper对象
Looper me = myLooper();
// 封装在looper中的MessageQueue, 因为looper和当前线程是关联的, 所以messagequeue也是和线程相关联的
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();
while (true) {
Message msg = queue.next(); // might block 调用 queue的 next 方法,从MessageQueue中取出, 可能会阻塞
// 如果当前的 MessageQueue 是空的, 或者 MessageQueue 中的第一个 Message 还没到执行时间,就阻塞了。
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
// message没有target为结束信号,退出循环
return;
}
// 打印日志
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
// 将消息分发给target,target就是handler, 如果走到这里, target肯定不是null
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf("Looper", "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
// 回收message资源
msg.recycle();
}
}
}
我们一点一点分析。
首先调用myLooper()
拿到当前线程关联的looper。 直接调用ThreadLocal的get()方法, 那么就是从当前线程 中的 ThreadLocalMap 中, 以sThreadLocal为key, 获取到对应的value, 其实就是在同一线程中, 之前在 prepare()中set进去的那个Looper对象。注意是同一线程中啊, 不可能是其他线程的, 因为ThreadLocal作用域就是线程内的。
拿到Looper之后, Looper中的MessageQueue也拿到了, 然后就调用next()
开始取消息。next()
之前分析过了, 就是死循环, 一直判断队头的Message对象 不是 null, 并且到了它的执行时间(if (now >= when) { // 如果当前时间 >= 执行时间 , 也就是说已经到了或者过了执行时间啦.赶紧取出执行吧
), 就把队头的Message对象取出来, 也就是这里的msg啦。
如果msg的target属性是null, 就直接return, 是return, 就退出循环了, 那么这个消息轮询器就不再轮询了, 以后就不从MessageQueue里面取消息处理了。 因此, msg.target == null 是 整个消息轮询器结束的标志。
之后打印日志。
之后, 调用msg.target.dispatchMessage(msg)
将消息分发给target对Message进行处理,target就是handler。 如果走到这里, target肯定不是null。
我们看一下dispatch方法
/**
* Handle system messages here.
*/
// 处理消息,该方法由looper调用, 在looper的loop()方法中, msg.target.dispatchMessage(msg)
public void dispatchMessage(Message msg) {
// 先判断msg中的callback, 优先调用
// 如果message设置了callback,即runnable消息,处理callback!
if (msg.callback != null) {
handleCallback(msg);
} else {
// 然后这里判断的是, handler的callback
// 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。见http://alex-yang-xiansoftware-com.iteye.com/blog/850865
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 最后回调hanler的handleMessage方法
// 该方法必须由子类进行重写
handleMessage(msg);
}
}
判断msg.callback 不为空的话, 就调用 handleCallback(msg)
。 这个msg.callback 是啥? 就是调用handle.post(Runnable)
时候传入的那个runnable对象啊。还记得不, 他把传入的Runnable 对象包装了一下, 包装成Message对象之后, 调用了sendMessageAtTIme方法, 而这个Runnable对象是成为了Message对象的一个属性。 handleCallback(msg)
就是直接run了一下, 没啥
// 如果messge指定了callback属性, 就调用这个方法
private final void handleCallback(Message message) {
// message.callback就是个runnable
// 直接调用runnable的run方法
message.callback.run();
}
如果没有设置 msg.callback, 走到else, 判断 mCallback是否null, 不为null,就回调mCallback的handleMessage()方法。这个mCallback是啥?是否还记得在创建Handler时候, 有多个构造方法, 其中就有指定Callback参数的。指定的就是这个mCallback。
如果mCallback 也是null, 那么就回调handleMessage(msg)
。 这个是啥? 这个就是子类必须重写的那个handleMessage啦。
处理完之后, 调用了 msg.recycle();
, 这是把Message资源释放, 把Message对象回收到那个全局的消息池, 方便下次使用啦。
说到这里, 整个Handler的流程就完啦。
最后, 我们再回答一下开篇说的几个问题:
Q1:为什么在主线程使用的时候不需要Looper.prepare();
和 Looper.loop();
?
这里我们要先看ActivityThread类的源码。在该类的最后, 有个main()方法, 这个main()方法, 是整个app程序的入口。
public static final void main(String[] args) {
SamplingProfilerIntegration.start();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
if (Process.supportsProcesses()) {
throw new RuntimeException("Main thread loop unexpectedly exited");
}
thread.detach();
String name = (thread.mInitialApplication != null)
? thread.mInitialApplication.getPackageName()
: "<unknown>";
Slog.i(TAG, "Main thread of " + name + " is now exiting");
}
}
第三行, 他调用了 Looper.prepareMainLooper();
随后, 又调用了Looper.loop();
。 真相大白
Q2:为什么在子线程中这样用?
先prepare是给当前的线程关联一个Looper对象。Looper对象中持有MessageQueue的引用。 然后再Looper.loop() 其实就是在当前线程关联的Looper对象上的MessageQueue上不断轮询, 不断取出Message去处理。
Q3:为什么在主线程 调用handler.sendEmptyMessage(1);
handler.postDelayed(task1, 10000);
之后, 最后的处理在主线程中进行的?
Q4:为什么在子线程调用handler.sendEmptyMessage(2);
handler.postDelayed(task2, 20000);
之后,最后的处理也是在主线程中进行的?
最后的处理是在哪个线程中执行, 就是看Looper.loop()方法中, 从MessageQueue中拿到Message之后, msg.target.dispatchMessage(msg)
这个方法在哪个线程调用, 就是在哪个线程处理的, 对吧? 那么, 这个方法究竟是在哪个线程执行的?msg.target.dispatchMessage(msg)
这个方法是在Looper.loop() 方法中调用的,那就看是在哪个线程上调用 loop()方法的,也就是看Handler 中的Looper对象是和哪个线程关联的。
比如:主线程调用默认的构造方法创建Handler, 那么Handler中 Looper 对象就是主线程上的那个 Looper 对象,MessageQueue 也是主线程上的(MessageQueue 是 Looper 的成员变量)。Handler 每次都是往主线程的 MessageQueue 丢消息。然后主线程的这个 Looper 对象调用 loop()方法是在 ActivityThread 中的 main()方法中调用的, 即主线程调用的,它从 MessageQueue 中取出 Message , 再交给 Handler 处理,处理Message就是在主线程执行的。
比如, 我们在子线程中
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
这样写,显然,这个 Handler 中的 Looper 对象是与当前子线程关联的(在 prepare()中创建好的),然后这个 Looper 对象的 loop()方法跑在子线程, 那么最后的handleMessage肯定是在子线程中执行。
比如我们在子线程中
class LooperThread extends Thread {
public void run() {
Handler mHandler = new Handler(getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Log.i("sss", Thread.currentThread().getName()); // main
}
};
mHandler.sendEmptyMessage();
}
}
最后的处理是在主线程的。因为 MainLooper 的 loop()方法是跑在主线程上滴。
前面为了思路的流畅,Message 类没有详细说,这里说一下。
Message有如下的成员变量
/**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
*/
// 翻译一下:用户定义的Message Code, 用于让接收者可以识别到底是哪个Message.
// 每一个Handler 对于MessageCode 都有他自己的命名空间, 所以你不必担心你的MessageCode 会和其他的Handler的MessageCode 冲突
public int what;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
// 如果你只需要存储少量的int 值, 那么 相当于setData()而言, arg1 和 arg2 是一种低成本的备选方案。因为setData()是用Bundle的。
public int arg1;
public int arg2;
// 这个obj 是发送给接收者的任意对象
public Object obj;
public Messenger replyTo;
// 这个when 是Message加入到MessageQueue的时间, 主要用于MessageQueue对Message进行排序的。比如我们sendMessage的时候, 可以设置delay值, 这些都是依赖这个when来实现的。
/*package*/ long when;
// 消息携带的数据, 可以使用setData()方法来设置。
// 接收者可以使用 getData()来拿到Message 携带的数据。
/*package*/ Bundle data;
// 这个target 其实是指定了接收者, 也就是这个Message交给谁来处理, 即最后会调用target.handleMessage 来处理这个Message
/*package*/ Handler target;
// 这个callback 就是我们在调用 handle.post(Runnable) 的时候, 把runnable对象赋给了这个callback, 接收者拿到这个Message之后会回调这个callback。
/*package*/ Runnable callback;
// ---------------------- 消息池相关 ---------------
// sometimes we store linked lists of these things
/*package*/ Message next;
private static Object mPoolSync = new Object();
private static Message mPool;
private static int mPoolSize = 0;
private static final int MAX_POOL_SIZE = 10;
下面的几个变量, 主要用于实现了一个消息池。他是用链表的形式做了一个消息池, 里面的Message对象可以重复利用。
next是指向下一个对象的指针。
mPool是这个链表的头指针。
mPoolSize是当前消息池的大小, 也就是链表中有几个对象(元素)
MAX_POOL_SIZE常量, 表示消息池最多能有几个对象。
之后每次obtain都是从消息池中获取Message对象, 使用完之后, 回收到消息池中。具体如何实现, 我们来看obtain()方法。
/**
* Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
* @param h Handler to assign to the returned Message object's <em>target</em> member.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
/**
* Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
* the Message that is returned.
* @param h Handler to assign to the returned Message object's <em>target</em> member.
* @param callback Runnable that will execute when the message is handled.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
/**
* Same as {@link #obtain()}, but sets the values for both <em>target</em> and
* <em>what</em> members on the Message.
* @param h Value to assign to the <em>target</em> member.
* @param what Value to assign to the <em>what</em> member.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h;
m.what = what;
return m;
}
/**
* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
* members.
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param obj The <em>object</em> method to set.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.obj = obj;
return m;
}
/**
* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
* <em>arg1</em>, and <em>arg2</em> members.
*
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param arg1 The <em>arg1</em> value to set.
* @param arg2 The <em>arg2</em> value to set.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what, int arg1, int arg2) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
return m;
}
/**
* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
* <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
*
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param arg1 The <em>arg1</em> value to set.
* @param arg2 The <em>arg2</em> value to set.
* @param obj The <em>obj</em> value to set.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
Message为我们提供带一系列obtain()方法, 可以传入各种不同的参数, 不过其内部都是调用了无参数的obtain()
方法。 注释中写的很清楚, 与方法obtain()
一样, 不过是设置了 target, arg1, arg2, obj等等各种参数。 @return
都是说从全局的消息池中返回了一个消息对象。对吧。
我们看看这个无参的obtain()方法, 他到底做了什么。
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
// 从全局的消息池中返回一个Message对象。避免创建太多的新对象。
public static Message obtain() {
synchronized (mPoolSync) {
if (mPool != null) {
Message m = mPool;
mPool = m.next;
m.next = null;
return m;
}
}
return new Message();
}
先上锁, 然后判断 链表的头指针是否是null。 (mPool就是链表的头指针, 这是一个单向的链表)。如果链表头指针 mPool 是null, 代表全局的消息池中没有Message对象, 那么直接new 一个 Message对象返回。 如果头指针(mPool)不是null, 代表全局的消息池中至少有一个Message对象, 那么就先找个临时变量(Message m) 记录一下这个链表的头部的这个对象(因为我们就要用他了。) 然后把链表头指针往后移动一个(也就是 mPool = m.next)。然后把我们要用的这个对象的next置null, 并且返回。这样我们就拿到了链表头部的Message对象。
那么使用完了之后, 他又是如何回收的呢? 我们来看recycle()
方法。
/**
* Return a Message instance to the global pool. You MUST NOT touch
* the Message after calling this function -- it has effectively been
* freed.
*/
public void recycle() {
synchronized (mPoolSync) {
if (mPoolSize < MAX_POOL_SIZE) {
clearForRecycle();
next = mPool;
mPool = this;
}
}
}
/*package*/ void clearForRecycle() {
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
when = 0;
target = null;
callback = null;
data = null;
}
先上锁, 如果当前消息池的数量没有到达最大值, 就把当前Message对象里面的东西清空, 然后把当前对象放在链表的头部。(next = mPool; mPool = this
这两行就是做了这个, 把原来的链表接在当前Message的屁股后面)。
Handler -> Looper -> MessageQueue
首先, 在哪个线程执行 Loop.prepare() 就会在那个线程关联 Looper.
比如 在 子线程 t1 执行 Looper.prepare()
public static final void prepare() {
// 调用Looper的私有构造, 创建一个Looper对象, 放到threadlocal中
// 这里之所以要做成ThreadLocal是因为,prepare方法可能在不同的线程中调用,比如main线程,比如需要loop能力的其他子线程, 不管哪个线程使用的时候,比如get set,都只是针对自己线程里面的变量操作,不影响其他线程
sThreadLocal.set(new Looper());
}
就是 在子线程 t1 对象的 map 中添加一个 <ThreadLocal, Looper> 的键值对.
prepare 之后, 这个 新的 Looper()对象就是子线程 t1 的内部变量了. 同时, 在 Looper 对象中还新创建了一个 MessageQueue.
然后, 再创建 Handler
public Handler() {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
// callback默认为null
mCallback = null;
}
用无参构造创建 Handler, Handler 中的 Looper 对象就是当前线程的那个 Looper.
因此, 我们在 子线程 t1 中 new Handler, 那 Handler 中的 Looper 自然就是刚刚 Looper.prepare() 创建出来并且关联到 t1 上的 Looper 对象了. 然后 t1 的 mMessageQueue 就是 Looper 的 MessageQueue
然后, 我们使用 Handler.sendMessage, 就是把消息发送到 t1 线程的 Looper 的 MessageQueue 里.
之后Looper.loop 会把这个 messageQueue 的消息取出, 并在 t1 上执行.
结论: Handler 把消息发到哪里, 主要是看这个 Handler 关联的 那个 Looper 呢.
在主线程, ActivityThread 中提前做了 Looper.loop(). 因此, 我们在主线程上 new Handler, 消息就是发送到主线程了.
在子线程中, new Handler(getMainLooper()), 这样也行, 直接指定 Handler 关联的 Looper 是主线程的, 消息就发送到主线程的 Looper 的 MessageQueue 里了, 然后主线程处理.
在子线程中, new Handler() 如果不明确指定用哪个 Looper, 就是用当前线程的, 那在此之前一定要先 Looper.prepare, 把 Looper 创建出来并关联上. 否则报错的.
那, 在主线程, 想把消息发到子线程执行呢?
class LooperThread extends Thread {
Looper subLooper;
public void run() {
Looper.prepare();
subLooper = Looper.myLooper();
Looper.loop();
}
}
存储一下子线程创建并关联的 Looper, 然后在主线程 创建 Handler 的时候, 指定这个子线程的 Looper, new Handler(LooperThread.subLooper) 这样就把 Handler 和 子线程的 Looper 关联了. Handler 发消息后, 就是子线程处理了.
结束.