Handler概述
Handler是Android消息机制的上层接口,最常见的应用就是通过Handler在子线程更新UI。
从创建Handler对象实例入手
- 创建Handler对象
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
在这里覆写了 handleMessage()方法。
- 点进去看Handler的构造函数,发现以上代码调用的一个参数的Handler构造方法,在内部调用了两个参数的构造方法。
-
mLooper = Looper.myLooper()
; Handler构造函数的第一行有效代码,调用了Looper类的静态方法获取Looper实例。点进去的具体代码如下:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
> Return the Looper object associated with the current thread. Returns null if the calling thread is not associated with a Looper.
从注释可以看出,这个返回的Looper是每一个不同线程的Looper对象,也就是说不同的线程获得的Looper都是相互没有干扰的副本。
那么是如何实现同一数据类型的不同线程副本,具体分析`sThreadLocal.get()`这行代码
#### ThreadLocal ####
* 首先sThreadLocal是一个ThreadLocal的对象,先来一小demo:
public static void main(String[] args) {
final ThreadLocal test = new ThreadLocal<>();
test.set(1);
System.out.println(Thread.currentThread().getName() + "当前值 = " +test.get());
new Thread(){
public void run() {
test.set(2);
System.out.println(Thread.currentThread().getName() + "当前值 = " +test.get());
};
}.start();
new Thread(){
public void run() {
test.set(8);
System.out.println(Thread.currentThread().getName() + "当前值 = " +test.get());
};
}.start();
new Thread(){
public void run() {
System.out.println(Thread.currentThread().getName() + "当前值 = " +test.get());
};
}.start();
}
打印结果:
main当前值 = 1
Thread-0当前值 = 2
Thread-1当前值 = 8
Thread-2当前值 = null
从代码和结果中可以看出,ThreadLocal类中有set/get方法,不同的线程获取的数据都与其自己设置的相同,同时如果没有调用set方法设置,返回的结果就是null,也就是说不同的线程拥有互不干扰的对象副本。
ThreadLocal工作原理
现在来看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 = values(currentThread)
返回了一个localValues
,这里涉及到ThreadLocal中的一个静态类ThreadLocal.Values。 - 接下来进行判断,如果
localValues
为null,则调用initializeValues方法进行初始化,否则会调用put方法将value存进去。
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;
}
}
}
可以看出,ThreadLocal的值就保存在LocalValues内部名为table的Object数组中,Threadlocal的reference字段后面紧跟着value,在这里也就是我们传进来的Looper。接下来就看看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);
}
get方法先根据当前线程获取其中的ThreadLocal.Values,从values中得到Object数组table,最后从table中找到存储的对象。
- 接下来继续分析Handler的构造方法,获取Looper对象之后会判断,获取的Looper对象是否为null,如果为null,则抛出异常。
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
从异常的内容不难看出,在创建handler对象之前,必须要调用Looper.prepare()方法对Looper对象初始化。
-
mQueue = mLooper.mQueue;
接着又从上一步获取的当前线程的Looper实例中获取了其保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。 -
mCallback = callback;
保存了创建Handler实例时覆写的callback回调方法。 -
mAsynchronous = async;
保存了创建Handler实例时对异步的设置。 - 以上就创建handler实例的代码,显然已经成功了。其实具体实现又要看looper内部的实现,以下来分析looper类。
looper
- 在以上创建Handler实例的时候,提到必须要先调用Looper.prepare()方法进行初始化,以下代码:
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));
}
在代码中,sThreadLocal是一个ThreadLocal对象,也就是对Looper对象进行了保存,同时进行了一次判断,判断sThreadLocal是否为null,否则抛出异常,这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程只有一个Looper实例。
- 先看
new Looper(quitAllowed)
Looper的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
1). 创建了一个消息队列`new MessageQueue(quitAllowed);`
2). 获取了当前线程实例。
核心方法loop()
- 现在让我们才看看Looper中的核心方法loop()。
public static void loop() {
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.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.recycleUnchecked();
}
}
在代码开头,调用Looper类的静态方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper()方法必须在prepare()之后方能运行。紧接着获取到了Looper实例中的mQueue(消息队列),之后就进入了无限循环。从消息队列中取出一条消息,如果没有消息就阻塞。
关键代码->msg.target.dispatchMessage(msg);
把消息交给了msg的target的dispatchMessage()方法去处理。点开target,可以发现target其实就是handler对象,最后释放消息占据的资源msg.recycleUnchecked();
。
加入消息队列
好了说了这么多,那么消息是怎么加入到消息队列MessageQueue中的呢
- 那么现在具体看看平时使用的sendMessage(Message msg)方法具体是如何实现的。
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);
}
而enqueueMessage
方法中
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在进入方法的第一行,enqueueMessage中首先为meg.target赋值为this,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,同时可以看出最终调用了queue.enqueueMessage
方法,那么queue又是什么呢,其实就是MessageQueue的对象实例,也就是说handler发出的消息,最终会保存到消息队列中去。
- 接下来再来看
dispatchMessage
方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
就会发现其中的handleMessage(msg);
方法中是空的,其实就是需要我们覆写的最终消息处理方法。
在这里还有一个问题,在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,结果Handler还是成功的创建了呢,其实这个因为在Activity的启动代码中,已经在当前的UI线程调用了Looper.prepare()和Looper.loop()方法。
最后对handler机制做一个总结:
- 首先Looper.prepare()在本线程保存一个Looper实例,然后在该实例中创建并保存了一个MessageQueue对象,同时因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程只会存在一个。
- Looper.loop()方法会让当前线程进入一个无限循环,不断的从MessageQueue的实例中读取消息,然后回调
msg.target.dispatchMessage(msg)
方法。 - 而target就是Handler的实例,那么再看Handler的构造函数,其首先得到了当前线程保存的Looper实例,进而又从Looper实例中获取了保存的MessageQueue实例,与其相关联。
- 接下来Handler的sendMessage方法,会给msg的target赋值为handler本身,然后加入到MessageQueue中。
- 那么回过头来看Looper.loop()获取到了消息调用的dispatchMessage(msg)方法其实最终调用了我们覆写的handleMessage方法。
学习的时间不长,有什么不对还请指出,谢谢指教。