Android中的消息机制
使用handler可以在子线程中发送消息,通知主线程更新UI,但是这只是Handler的一种特殊的使用场景。本质上Handler是可以实现多线程之间的通讯的。
使用Handler消息机制底层是需要用到MessageQueue和Looper的支撑的:Handler发送消息,处理消息,MessageQueue存放消息,Looper循环获取消息并交给Handler处理。他们之间是如何协作呢,我们来分析下。
Looper
Looper作为消息获取者,他的任务便是循环的从MessageQueue中获取消息,如果没有消息便会阻塞,直到有新的消息。Handler的创建和使用是需要Looper的,而普通的线程是不存在Looper的,而我们在使用Handler的时候基本都是Handler handler = new Handler()
并未考虑Handler的构造是可以接收Looper的,主要是因为我们使用Handler一般都是在主线程中创建,而主线程默认是创建了Looper的,当我们创建Handler的时候,Handler就可以直接拿到Looper了。
Looper的创建
先看下Looper是如何在主线程中创建的,Looper中有个prepareMainLooper()方法,他就是为主线程创建Looper使用的,此过程是在ActivityThread中main()方法里完成的当然内部还是调用了prepare()方法。初始化Looper的时候,MessageQueue也会被创建。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
由于Looper构造私有,所以创建Looper时通过Looper提供的prepare()方法的,并设置给了ThreadLocal,当然取也是通过ThreadLocal获取的。ThreadLocal接下来会有分析。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
ThreadLocal
前边说了Looper在创建后,使用ThreadLocal来存取,那Handler 是怎么获取到当前线程的Looper对象的呢,这就用到了Java中 java.lang包下的一个类ThreadLocal,本质ThreadLocal不是一个线程,简单说:ThreadLocal的作用是可以在不同的线程中维护同一T
数据类型的不同value,比如Looper,主线程中的Looper对象,去子线程中去通过ThreadLocal获取时,是获取不到的,使用ThreadLocal就把Looper和当前的线程绑定到一块了,
- 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);
}
首先set方法获取到了当前的线程,并通过values方法拿到了当前线程的localValues,查看Thread类中的localValues 可以看到
ThreadLocal.Values localValues;
Thread是持有ThreadLocal.Values的,如果Thread里的localValues为空的把又调用了initializeValues()方法,这个方法就是new了一个Values赋值给了localValues,
//ThreadLocal的values方法
Values values(Thread current) {
return current.localValues;
}
得到values后就是开始存数据了,下边是put方法的源码,这个put方法具体逻辑不去分析,不过大体可以理解ThreadLocal的原理:每个Thread线程里都有自己的ThreadLocal.Values,Values存放数据用的是Object[] 数组,这也就理清了ThreadLocal是如何在不同的线程中维护同一数据类型的不同数据副本。
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;
}
...
}
}
- 根据分析我们来使用ThreadLocal设置下数据看看输出结果,加深下印象
public class ThreadLocalTest {
class User {
public String Name;
@Override
public String toString() {
return Name;
}
}
public ThreadLocal<User> mThreadLocal = new ThreadLocal<>();
public void startThread() {
System.out.println("start:" + Thread.currentThread().getName() + "--" + mThreadLocal.get());
new Thread("Thread1") {
@Override
public void run() {
super.run();
User user = new User();
user.Name = "线程1";
mThreadLocal.set(user);
System.out.println(Thread.currentThread().getName() + "--" + mThreadLocal.get());
}
}.start();
new Thread("Thread2") {
@Override
public void run() {
super.run();
System.out.println(Thread.currentThread().getName() + "--" + mThreadLocal.get());
}
}.start();
new Thread("Thread3") {
@Override
public void run() {
super.run();
User user = new User();
user.Name = "线程3";
mThreadLocal.set(user);
System.out.println(Thread.currentThread().getName() + "--" + mThreadLocal.get());
}
}.start();
System.out.println("end:" + Thread.currentThread().getName() + "--" + mThreadLocal.get());
}
// 在Main方法里调用startThread()方法后打印出的结果:
//start:main--null
//Thread1--线程1
//Thread2--null
//Thread3--线程3
//end:main--null
Looper.loop()
Looper的作用就是循环,ActivityThread创建了Looper后并调用loop()方法开启了循环,
public final class ActivityThread {
//...
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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();
}
}
可以看到loop()方法是个for的无限循环,循环去调用MessageQueue的next()方法获取消息,获取到并交给了Handler处理:msg.target.dispatchMessage(msg)
。handler在sendMessage的时候,将自己传给了Message对象,所以此时的msg.target就是Handler,如果取不到Message,loop()方法就会堵塞,因为MessageQueue.next()是个堵塞方法,直到next()方法有了新的消息,loop()才会继续。
MessageQueue
消息队列是实际是一个链表的数据结构,来维护消息的插入和删除,
Handler
Handler我们在使用时都是直接new Handler的方式直接使用,其实Handler还有多个构造重载,使用方式比较多,
- Handler创建
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, 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());
}
}
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;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
- 通过构造可以看出我们以直接new Handler()的方式创建,内部会调用的Looper.myLooper();此方法前面介绍过了,取Looper是通过ThreadLocal.get()方法,所以获取到的是当前所在线程的Looper,即主线程中的Looper。Looper将获取到消息传给Handler处理,因为Looper位于主线程,所以Handler在Looper中处理消息,也就是在主线程中处理消息。
- 除此Handler还可以接收一个Looper来创建。这里牵扯到一个问题面试常问的问题:Handler可以在子线程中创建么?答案是可以的,但是创建时要先在子线程中创建Looper,否则直接创建Handler会抛出异常,因为子线程中没有Looper,创建Handler就没有意义。
- 我们实际处理消息是采用重写Handler的
handleMessage()
方法,当然Handler也为我们提供了自己的处理消息回调方式,public Handler(Callback callback)