Handler的使用
在日常开发过程中,Handler
多用来进行切换线程的操作。一般的场景是,在子线程中做完了耗时的操作,然后会使用 Handler
来通知主线程进行 UI
的更新。
例如我们会在主线程中定义一个静态内部类 Handler
,这里命名为 MyHandler
。
private static MyHandler extends Handler {
@override
public void handleMessage(Message msg) {
//todo 在这里根据msg处理并进行UI的更新
}
}
定义 MyHandler
之后,然后生成一个 MyHandler
实例。
MyHandler mHandler = new MyHandler();
然后在子线程中使用 mHandler
发送消息。
Message msg = new Message();
msg.what = 0;
msg.obj = myObject;
mHandler.sendMessage(msg)
当执行 sendMessage
方法后,最终会执行 MyHandler
的 handleMessage
方法,handleMessage
方法的Message
就是从 sendMessage
中传递进来的,所以可以根据 Message
对象判断并处理 UI
更新。
Handler
的使用还是挺简单的,但其内部的实现原理到底是怎样的呢?我们还是通过源码分析来一探究竟。
源码分析
我们从 Handler
的构造函数出发,看看构造函数做了什么。
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());
}
}
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;
}
一开始就来检测内存泄漏是否可能存在。如果继承 Handler
的类是匿名内部类、非静态内部类,或者是本地内部类,则会提示该类有可能存在内存泄漏。为什么呢?因为 Handler
一般是在 Activity
或者 Fragment
中使用,匿名内部类、非静态内部类、本地内部类都会持有 Activity
对象,当 Handler
的生命周期比 Activity
长时,会导致 Activity
对象释放不了,导致内存泄露。
然后执行到 Looper.myLooper
方法获得一个 Looper
对象。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
sThreadLocal
是一个静态的 ThreadLocal
对象,关于 ThreadLocal
的作用请参考 【源码解析】ThreadLocal的工作原理,这里就是获取了当前线程下的 Looper
对象。
那 sThreadLocal
是什么时候将当前线程对应的 Looper
设置进去的呢?我们来看 Looper.prepare
方法。
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到在 prepare
方法中构造了一个 Looper
对象,Looper
对象构造了一个 MessageQueue
对象,并通过 Thread.currentThread
方法引用当前线程对象,然后将 Looper
对象塞到 sThreadLocal
中。
因此在生成 Handler
实例之前,必须要先去调用 Looper.prepare
方法,否则 Looper
对象找不到会抛出运行时异常。
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
讲到这里,也许大家可能会有疑问,为什么在平时开发使用那么多次 Handler
,都没有见到过要主动调用 Looper.prepare
方法?这个问题我们先hold一下,留到后面说。
我们接着分析 Handler
的构造函数,在得到 Looper
对象后,然后将 Looper
对象的 MessageQueue
赋值给 Handler
的 mQueue
变量,而 mQueue
变量的类型就是 MessageQueue
。
Handler
的分析到此告一段落,构造函数执行完后,下一步就是使用 Handler
对象发送消息。不管 Handler
是通过 post、sendMessage
还是其他方法,最终调用的都是 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);
}
方法中又执行了 enqueueMessage
方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这段代码主要目的是将 Message
消息添加到 MessageQueue
这个消息队列。而 MessageQueue
消息队列就是在上面 Handler
构造函数中 new
出来的,由于 Looper
是 ThreaLocal
持有的,而 Looper
又持有 MessageQueue
,那么 ThreadLocal
持有 MessageQueue
。也就是说每一个线程对应着一个 MessageQueue
对象。
那么添加到 MessageQueue
的 Message
什么时候才能得到执行呢?Looper
有一个 loop
的方法。
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
...
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.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
...
}
}
myLooper
方法获取当前线程的 Looper
对象,然后再获取 Looper
对象的 MessageQueue
对象,通过 for
循环,循环不断的从 MessageQueue
队列中获取 Message
消息,Message
消息的 target
就是一个 Handler
对象,所以这里执行了 Handler
对象的 dispatchMessage
方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
如果 Message
消息定义了 callback
对象,则执行 handleCallback
方法,也就是会执行 Message
消息的 callback
对象的 run
方法,否则如果 Handler
的 mCallback
对象存在,则会执行 mCallback
的 handleMessage
方法,当两者都不存在或者 mCallback
的 handleMessage
方法返回为 false
时,则会执行 Handler
的 handleMessage
方法。
由于 Handler
发送消息实际上是往该 Handler
持有的线程的消息队列中添加消息,当该消息被执行时,就已经切换到了 Handler
所在的线程了。当 Handler
持有的线程为主线程时,消息的执行就肯定运行在主线程,这样就达到了子线程向主线程切换的效果。
上面分析过程中还遗留了一个问题:为什么我们平时使用Handler的时候不用调用 Looper.prepare
和 Looper.loop
方法呢?
因为这两个方法系统已经帮我们做了。在 ActivityThread
中(Activity
的真正入口)的 main
方法有以下逻辑。
public static void main(String[] args) {
Looper.prepareMainLooper();
...
// 开始循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper.prepareMainLooper
方法就是为主线程构建了一个 Looper
对象,而 Looper.loop
方法就是为主线程开启了消息循环,平时我们创建 Handler
大部分都是在主线程创建的,因此是不需要主动调用 Looper.prepare
和 Looper.loop
方法了。
但是,如果是在子线程创建 Handler
,那么在构造该 Handler
之前,必须要主动调用 Looper.prepare
和Looper.loop
方法,否则会抛出运行时异常。
总结
通过分析 Handler
源码可知,Handler
的原理并不高深,它主要是通过了 ThreaLocal
原理和不断从对应线程的消息队列中取消息的机制,将消息的执行抛到相对应的线程中去执行,从而达到了线程切换的效果。我们平时使用 Handler
切换到主线程去更新 UI
仅仅是其的一小部分功能实现。