前言
Handler
是Android的消息机制,他能够很轻松的在线程间传递数据。由于Android开发规范的限制,我们不能在主线程执行耗时操作(如网络,IO操作等),不能在子线程更新UI,所以Handler
大部分用来在耗时操作与更新UI之间切换。这让很多人误以为Handler
就是用来更新UI的,其实这只是它的一小部分应用。
开始
我相信大多数人对Handler
的用法已经烂熟于心了,这篇文章不会去探讨Handler
的使用,而是着重从源码上分析Handler
的运行机制。
想要了解Handler
的运行机制,我们需要了解 MessageQueue
,Message
,Looper
这几个类。
MessageQueue
的意思就是消息队列,它存储了我们需要用来处理的消息Message
。Message
是消息类,内部存在一个Bundle
对象和几个public
字段存储数据,MessageQueue
作为一个消息队列不能自己处理消息,所以需要用到Looper
。Looper
是一个循环装置,他负责从不断从MessageQueue
里取出Message
,然后回调给Handler
的handleMessage
来执行具体操作。Handler
在这里面充当的角色更像是一个辅助类,它让我们不用关系MessageQueue
和Looper
的具体细节,只需要关系如何发送消息和回调的处理就行了。
上面讲了几个关键类在Handler
运行机制中的职责,相对大家对Handler机制有个粗略的了解。
我相信各位看官在阅读这篇文章前都是带着问题的,我们将通过问题来解答大家的疑惑。
分析
Looper
在分析Looper
之前,我们还需要知道ThreadLocal
这个类,如果对ThreadLocal
还不太了解,可以去看我的另一篇文章《ThreadLocal详解》。
Looper是如何创建?
Handler
执行的线程和它持有的Looper
有关。每个Thread
都可以创建唯一的Looper对象。
//为当前线程创建Looper对象的方法。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//使用ThreadLocal来存储当前线程的Looper对象,这保证了每个线程有且仅有一个Looper对象。
//这里做了非空判断,所以在同一个线程prepare方法是不允许被调用两次的
//第一次创建好的Looper对象不会被覆盖,它是唯一的。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
那么主线程的Looper
对象是怎么创建的呢?
public static void prepareMainLooper() {
//其实主线程创建Looper和其他线程没有区别,也是调用prepare()。
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//但是Looper用sMainLooper这个静态变量将主线程的Looper对象存储了起来
//可以通过getMainLooper()获取,存储MainLooper其实非常有作用,下面会讲到。
sMainLooper = myLooper();
}
}
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
Looper是如何从MessageQueue取出消息并分发的?
Looper分发消息的主要逻辑在loop方法里
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
//保证当前线程必须有Looper对象,如果没有则抛出异常,调用Looper.loop()之前应该先调用Looper.prepare().
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//Looper需要不断从MessageQueue中取出消息,所以它持有MessageQueue对象
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 (;;) {
//这里开始执行死循环,queue通过调用next方法来取出下一个消息。
//很多人很疑惑死循环不会相当耗费性能吗,如果没有那么多消息怎么办?
//其实当没有消息的时候,next方法会阻塞在这里,不会往下执行了,性能问题不存在。
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
//这里满足了死循环跳出的条件,即取出的消息为null
//没有消息next不是会阻塞吗,怎么会返回null呢?
//其实只有MessageQueue停止的时候(调用quit方法),才会返回null
//MessageQueue停止后,调用next返回null,且不再接受新消息,下面还有详细介绍。
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.target是Handler对象,分发消息到Handler去执行。
//有人问主线程可以创建这么多Handler,怎么保证这个Handler发送的消息不会跑到其它Handler去执行呢?
//那是因为在发送Message时,他会绑定发送的Handler,在此处分发消息时,也只会回调发送该条消息的Handler。
//那么分发消息具体在哪个线程执行呢?
//我觉得这个不该问,那当然是当前方法在哪个线程调用就在哪个线程执行啦。
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);
}
//这里对Message对象进行回收,会清空所有之前Message设置的数据。
//正是因为Message有回收机制,我们在创建消息的时候应该优先选择Message.obtain().
//如果发送的消息足够多,Message缓存的Message对象不够了,obtain内部会调用new Message()创建一个新的对象。
msg.recycleUnchecked();
}
}
Looper 分发的消息在哪个线程执行?
先给大家展示一段Looper
文档上的示例代码
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); //创建LooperThread的Looper对象
mHandler = new Handler() {
public void handleMessage(Message msg) {
//处理发送过来的消息
}
};
Looper.loop(); //开始循环消息队列
}
}
上面这段代码相信很多人都写过,这是一段在子线程创建Handler的案例,其中handleMessage
所执行的线程为LooperThread
,因为Looper.loop()
执行在LooperThread
的run
方法里。可以在其他线程通过mHandler
发送消息到LooperThread
如果不调用Looper.prepare()
直接new Handler()
会怎么样呢?
我们可以查看Handler
的源码看看无参构造是如何运行的
public Handler() {
//调用两参构造
this(null, false);
}
public Handler(Callback callback, boolean async) {
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());
}
}
//获取当前线程的Looper,如果不创建Looper会抛出异常。
//主线程我也没看到有调用Looper.prepare()啊,怎么在主线程不会抛异常呢?这个看下一个问题。
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;
mAsynchronous = async;
}
主线程的Looper对象在哪里创建的?
从上一个问题可以看出如果不调用Looper.prepare()
直接new Handler()
就会抛出异常`
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");`
那么主线程的Looper
在哪里创建的呢?首先它是创建了的,因为Looper.getMainLooper() != null
,其实MainLooper
创建的时间比我们想象的早,它在ActivityThread
类里面,ActivityThread
是Android
的启动类,main
方法就在里面(如果有人问你Android有没有main方法,你应该知道怎么回答了吧),而MainLooper
就是在main
方法里面创建的。
上代码:
//android.app.ActivityThread
public final class ActivityThread {
...
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
//注意这里,这里创建了主线程的Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
//开启消息循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
MainLooper可以用来做什么
判断当前线程是否为主线程
因为Looper是在某一线程唯一的,那么可以在么做。如果
public static boolean isMainThread() {
//如果当前线程的Looper和MainLooper是同一个对象,那么可以认为当前线程是主线程
return Looper.myLooper() == Looper.getMainLooper() ;
}
但是也有人说下面这样也可以
public static boolean isMainThread() {
//这个方法其实是不准确的,线程的名称是可以随便更改的。
return Thread.currentThread().getName().equals("main");
}
所以用Looper
来判断主线程是很好的做法
创建运行在主线程的Handler
Handler
除了有无参构造,还有一个可以传入Looper
的构造。通过指定Looper
,可以在任意地方创建运行在主线程的Handler
class WorkThread extends Thread{
private Handler mHandler;
@Override
public void run() {
super.run();
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//运行在主线程
}
};
mHandler.sendEmptyMessage(0);
}
}
Looper的quit方法和quitSafely方法有什么区别
下面是Looper
两个方法的源码
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
可以看出实际上是调用的MessageQueue
的quit
方法
下面是MessageQueue
的源码
//android.os.MessageQueue
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
//如果调用的是quitSafely运行removeAllFutureMessagesLocked,否则removeAllMessagesLocked。
if (safe) {
//该方法只会清空MessageQueue消息池中所有的延迟消息,
//并将消息池中所有的非延迟消息派发出去让Handler去处理,
//quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
removeAllFutureMessagesLocked();
} else {
//该方法的作用是把MessageQueue消息池中所有的消息全部清空,
//无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
无论是调用了quit
方法还是quitSafely
方法,MessageQueue
将不再接收新的Message
,此时消息循环就结束,MessageQueued
的next
方法将返回null
,结束loop()
的死循环.这时候再通过Handler
调用sendMessage
或post
等方法发送消息时均返回false
,表示消息没有成功放入消息队列MessageQueue
中,因为消息队列已经退出了。
Message
Message.obtain()和new Message()如何选择
Message
提供了obtain
等多个重载的方法来创建Message
对象,那么这种方式和直接new
该如何选择。下面看看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(); //只有当从对象池里取不出Message才去new
}
void recycleUnchecked() {
//清除所有使用过的痕迹
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++;
}
}
}
从上面代码可以看出,通过obtain
方法是从对象池取,而new
是创建了一个新的对象。我们应该使用obtain
来创建Message
对象,每次使用完后都会自动进行回收,节省内存。
未完待续......