这篇文章将带着大家全方位去理解Handler机制,我们先将涉及几个重要的类列出来:
Handler: 发送消息到MessageQueue;Looper循环处理消息后,交给Handler的handleMessage(..)进行处理。
MessageQueue: 顾名思义,消息队列,用于存储消息数据。以队列先进先出的特点,对外提供消息的插入和删除操作;但是,MessageQueue数据结构实际是单链表。
Looper: 顾名思义,循环消息。当MessageQueue中有消息时,Looper循环不断的处理消息;当MessageQueue中消息为空时,Looper阻塞在原地。
ThreadLocal: 不是线程,是一个可以存储数据的类。ThreadLocal在取值操作的时候会传入一个线程引用,使ThreadLocal在不同线程中取出的变量引用的值不同。
Message: 顾名思义,消息。用于封装消息的数据,字段what表示消息唯一标示,obj,arg1,arg2表示存储在消息中的数据。
ActivityThread: 主线程,又UI线程。其在创建的时候,就会初始化线程中的Looper。
接下里,先说明下Handler机制为什么要存在?它能解决什么问题?
Android做一个耗时操作的时候,需要在子线程中进行处理,否则会出现ANR;在执行完耗时操作后,需要将数据用于UI的刷新,操作UI是要在UI线程中完成,否则系统会报错。
而,Handler机制就可以实现线程间的通信,将数据封装到消息中,并将消息发送给UI线程,取出消息中数据刷新UI。
后面我们要分析的是:消息是如何通过MessageQueue,Looper,最终实现消息从子线程传递到UI线程的?
第一步,将子线程中获取的数据封装在Message中,第一步是要发送Message到MessageQueue,常调用的方法有:handler.sendMessage(..),message.sendToTarget()。
sendMessage的源码如下:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
实际上最终调用的是这样一个方法,如下:
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);
}
进入方法enqueueMessage(..),如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
调用的是MessageQueue的enqueueMessage(..),将消息插入到消息队列中,继续查看MessageQueue源码如下:
boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
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;
}
msg.when = when;
Message p = mMessages;
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;
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
其实在handler.sendMessage(..)方法里,是有如下这样的一段注释的,大致意思是:将消息放到消息队列的末尾,并在handler所在线程被接受,handleMessage进行处理。
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received 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.
*/
message.sendToTarget()里面实际调用就是handler.sendMessage(..),感兴趣的同学可以查阅源码去验证,这里不作阐述。
第二步:通过前面我们知道在ActivityThread创建的时候,就已经初始化Looper了。其实,在创建Handler的实例时,如果handler所在线程没有初始化Looper,那么会报一个异常。这里小王君做过一个测试,在子线程里创建Handler实例,没有初始化Looper,报了一个如下异常:
05-28 15:13:50.300: E/AndroidRuntime(2932): FATAL EXCEPTION: Thread-511
05-28 15:13:50.300: E/AndroidRuntime(2932): Process: com.example.test, PID: 2932
05-28 15:13:50.300: E/AndroidRuntime(2932): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
05-28 15:13:50.300: E/AndroidRuntime(2932): at android.os.Handler.<init>(Handler.java:208)
05-28 15:13:50.300: E/AndroidRuntime(2932): at android.os.Handler.<init>(Handler.java:122)
05-28 15:13:50.300: E/AndroidRuntime(2932): at com.example.test.MainActivity$1$1.<init>(MainActivity.java:25)
05-28 15:13:50.300: E/AndroidRuntime(2932): at com.example.test.MainActivity$1.run(MainActivity.java:25)
05-28 15:13:50.602: E/(2932): appName=com.example.test, acAppName=/system/bin/surfaceflinger
05-28 15:13:50.602: E/(2932): 0
Can't create handler inside thread that has not called Looper.prepare(),意思很明显:无法在一个没有调用Looper.prepare()所在的线程里创建handler。我们可以查阅Handler构造函数验证报错原因,留给感兴趣的同学研究,这里不作阐述。
这就间接验证了:为啥我们在主线程里随意创建handler对象,并没有报错的原因,因为主线程在创建的时候就初始化了Looper。那么在子线程中该如何创建Handler呢?查阅Looper源码有这样一段注释,很好告知处理方式,注释如下:
/**
* 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.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <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的两个很关键的方法:Looper.prepare(),Looper.loop();
Looper.prepare(),用于初始化Looper,源码如下:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
分析:这里初始化Looper不是简单的创建对象,还需要存储Looper对象,操作的类是ThreadLocal。前面有ThreadLocal类的解释,目的就是为了handler可以存入和取出所在线程的Looper。很显然每个线程的Looper是不同的,我们也可以用Hash表结构的类存取Looper,但是系统没有这么做,而是提供一个ThreadLocal解决需求。接下来进入ThreadLocal的源码查看方法,存入set(...),取出get()。ThreadLocal的set(..)源码,如下:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
Values values(Thread current) {
return current.localValues;
}
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
分析:如果values为空,执行代码current.localValues = new Values(),values = current.localValues,current即Thread。put(...)方法在ThreadLocal的静态内部类-Values类里面,核心代码是调用values.put(ThreadLocal,Looper),变量values对象持有当前Thread的引用(values = current.localValues)。
put方法就是将Looper对象放入一个数组里(代码里变量value就是传入的Looper对象),由于每个线程都对应一个新的Values对象,因此存入Looper是在不同的数组中操作的。
于是,ThreadLocal类就可以正确的存入线程所在Looper的对象,而不会乱存其他线程的Looper。
总结:Looper.prepare()里面不仅创建了Looper对象,还使用ThreadLocal类实现了Looper于所在线程中的存入。
继续分析Looper.loop(),大家主要看下在源码中的一些注释,源码如下:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//获取Looper对象
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();
//通过无限循环不断的抽取消息队列里的消息
for (;;) {
//消息队列里取出下一个消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//对消息msg进行转发处理
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.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(TAG, "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);
}
msg.recycle();
}
}
分析:
1,final Looper me = myLooper(),源码如下:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
于是查看
ThreadLocal的get方法源码,如下:
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
该方法取出所在线程的Looper对象。先取出Values对象里的数组table(Object[] table = values.table),执行一个&运算得到角标index,存入的数据在角标index + 1位置,于是looper = table[index + 1]。
2,final MessageQueue queue = me.mQueue;消息队列mQueue的实例,在创建Looper对象时创建,也就是我们调用Looper.prepare()时创建。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
3,
Message msg = queue.next();再次进入到MessageQueue类中查看next方法,源码如下:
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
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();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuiting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
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;
}
}
取出消息队列中消息return,并在队列中移除该消息,核心代码如下:
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
4,取出消息之后,调用方法
msg.target.dispatchMessage(msg)进行转发处理,target就是Handler对象,进入Handler源码查看,如下:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
由代码可知,处理消息的方式有三种:
4.1, msg.callback != null时,调用方法handleCallback(msg),该方法里就一行代码:message.callback.run();callback就是一个Message里的Runnable接口,这里是回调接口的run方法。
在调用handler.post(runnable)方法发送消息时,callback不为空,处理消息的代码放在run方法里。handler.post(runnable)源码,如下:
/**
* 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)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
注意:方法上有这样一条注释,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 的执行是在handler所在线程里,我们这里就是UI线程。
getPostMessage(r)就是创建一个callback变量的值为r的消息,查看其源码,如下:private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
sendMessageDelayed就是前面讲到发送消息到消息队列时调用的方法,这里不作阐述。
4.2,mCallback != null时,mCallback 是接口Handler$Callback的引用,执行mCallback.handleMessage(msg)处理消息。Handler$Callback源码,如下:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
注释大致意思是:当去实例化一个Handler对象时,如果不想派生其子类,可以用Handler h = new Handler(mCallback)创建对象。
4.3:当以上两个接口都没有实例化时,调用handleMessage(msg)处理消息,源码如下:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
我们发现这个方法里面是空的,什么也不做,查看注释发现Android要求在创建Handler子类时,必须重写该方法接受消息并处理。当然,这是开发中最常用的处理消息方式。
总结下handler机制,大体是这样的:
1,首先将子线程中获取数据封装在Message对象中,;
2,通过handler的sendMessage,或post方法将消息发送到消息队列里,最终都走得是sendMessageAtTime这条路,sendMessageAtTime里面调用了MessageQueue的enqueueMessage方法,完成消息插入到消息队列的操作;
3,每当我们创建Handler实例时,就会初始化Looper对象,是调用Looper.prepare()创建Looper对象(同时MessageQueue对象创建在Looper构造函数中完成),并将Looper对象通过ThreadLocal类存入到线程中;
4,Looper从消息队列里不断取出消息,是调用Looper.loop(),里面调用了MessageQueue的next方法,将消息取出来;
5,取出来的消息通过handle的dispatchMessage(..)转发处理,具体处理方式有三种,如上所示。
注:学习到这里,可能都有一个这样的疑问:哇~程序执行是如何从子线程慢慢到主线程的?
对,就是调用Handler所在主线程的Looper.loop()方法,抽取到消息处理的时候,代码就执行到主线程了,是不是很神奇,这也说明Looper对于线程间通信简直太重要了!
下面将粘贴王小君实际开发中,利用handler机制实现线程通信的三个不同example,如下:
4.1:
private Handler h = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
public void run() {
SystemClock.sleep(6000);
h.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "run方法是在主线程里执行的", 0).show();
}
});
};
}.start();
}:
4.2,:
private static final int SHOW_UI = 0;
private Handler h = new Handler(new Handler.Callback(){
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case SHOW_UI:
Toast.makeText(getApplicationContext(), "不想派生handler的子类处理消息", 0).show();
break;
default:
break;
}
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
public void run() {
SystemClock.sleep(6000);
Message msg = Message.obtain();
msg.what = SHOW_UI;
h.sendMessage(msg);
};
}.start();
}
4.3:
private static final int SHOW_UI = 0;
Handler h1 = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_UI:
Toast.makeText(getApplicationContext(), "最常使用处理消息的方式", 0).show();
break;
default:
break;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
public void run() {
SystemClock.sleep(6000);
Message msg = Message.obtain();
msg.what = SHOW_UI;
h.sendMessage(msg);
};
}.start();
}
最后,辛苦大家能看到这里,文章有不够准确的地方,期待大家能在下面评论中指出,一起学习进步!!!
———小王君 (*^__^*)