说起Handler我们再熟悉不过了,Handler用来进行线程间进行通讯的,但是Handler线程间通讯的机制以及原理是什么样的?下面我们就一起来剖析一下。
(1)一个线程可以有几个Handler,可以有几个Looper?
答:一个线程只有一个Looper,可以有多个Handler。
(2)子线程中能否创建子线程的Handler?
答:可以。但是有个前提,在子线程创建Handler,必须先要创建子线程的Looper,可以看Handler的构造函数的源码:
public Handler() {
this(null, false);
}
public Handler(@Nullable 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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
这里的Looper.myLooper是先看当前Handler有无对应的Looper,如果没有Looper那么就会抛出异常:Can’t create handler inside thread…that has not called Looper.prepare();
所以,在子线程中创建Handler的前提是先创建对应的Looper,如果没有创建Looper就直接创建Handler,那么就会报异常。
调用了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));
}
我们看下这个ThreadLocal是什么?
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
看来,我们在prepare方法中创建Looper之后,并把Looper封装到了ThreadLocal中,我们看一下ThreadLocal类中是怎么存储Looper对象的:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
首先通过Thread.currentThread()获取到当前的线程,然后,通过当前线程获取当前线程的ThreadLocalMap变量:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
我们看Thread类的源码就可以看到,每一个Thread类都有一个ThreadLocalMap类型的变量:
ThreadLocal.ThreadLocalMap threadLocals = null;
而这里需要注意的一点是这个ThreadLocalMap类是ThreadLocal的一个内部类。
一会咱们再详细介绍ThreadLocalMap这个类,这里咱们先知道,通过Looper.prepare方法,是创建了Looper实例,然后把这个实例存储到了当前线程的的ThreadLocalMap变量中。
Looper中获取Looper实例的方法:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
所以在子线程中,创建Handler之前,需要先通过Looper.prepare()创建Looper的实例。
在创建完了Handler对象之后,还需要通过Looper.looper()开始轮询,检查MessageQueue中有无消息,如果有消息,就交给Handler来处理消息。所以,在子线程中创建Handler大概是这个样子的:
public class MyThread extends Thread {
private Handler handler;
@Override
public void run() {
super.run();
// 创建Looper对象并关联到Thread
Looper.prepare();
handler = new Handler(Looper.myLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
Looper.loop();
}
}
这里我们需要注意一下,在创建Looper的时候,会调用Looper的构造函数:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我们发现是在Looper的构造函数中创建了MessageQueue。
回顾上面,这个是在子线程中创建Handler,需要调用Looper.prepare和Looper.looper。
那么在主线程中创建Handler的话,需要调用Looper.prepare和Looper.looper。
首先我们需要了解主线程是在什么时候创建的,每个App在启动的时候,都会创建一个进程,同时也会创建一个主线程或者称为UI线程,即我们常提到的ActivityThread。我们一起看一下ActivityThread这个类的main方法:
public static void main(String[] args) {
.....
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我们发现,这里调用了Looper.prepareMainLooper();方法和Looper.loop()方法。
Looper.loop方法我们已经提到过了,下面我们看一下Looper.prepareMainLooper()这个方法:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
这里我们发现,Looper.prepareMainLooper方法其实也是调用了Looper.prepare方法,而且我们注意到,这个Looper只能有一个,如果当前线程已经有Looper对象,再创建新的Looper对象就会抛出异常。
所以我们可以判定,不管是在主线程还是子线程,创建Handler对象之前都必须先创建Looper对象,主线程和子线程唯一的不同是,主线程已经在ActivityThread的main方法中为我们提前创建好了Looper对象,并且通过Looper.looper方法开启了轮询。所以我们在主线程中可以直接创建Handler对象。而在子线程中却需要我们提前手动创建Looper对象,并且需要手动通过Looper.loop开启轮询。
下面我们来看一下ThreadLocal中的get方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
通过当前线程,获取到Thread中的ThreadLocalMap变量,注意这个ThreadLocalMap类,这个类是ThreadLocal的静态内部类,类似于HashMap,是以this(即当前的ThreadLocal对象)为键,创建的Looper对象为值存储的:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry继承WeakReference,包括两个元素,一个ThreadLocal类型的成员和一个Object类型的成员value。其中ThreadLocal类型的成员是一个弱引用,其特点是,当引用元素无强引用时,JVM GC时会立即回收引用元素。
Entry对象Key为弱引用,当Key所指对象无强引用时,JVM GC时会自动回收该对象,从而造成Entry状态变为STALE,即无效状态。此时,必须对该Entry对象及其Value引用进行擦除,防止内存泄漏。
下面我们一起来看一下Handler的发送消息:
handler.postAtTime();
handler.postDelayed()
handler.sendMessage();
handler.sendMessageDelayed();
handler.sendEmptyMessage()
handler.sendMessageAtTime()
.........
handler发送消息有好多方法,但是这些方法,最终都调用了同一个方法,那就是:
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在enqueueMessage中有一行代码格外重要:
msg.target = this;
通过这行代码,message和Handler做了关联。
最终调用了MessageQueue的enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
在这里开启了对MessageQueue消息的轮询,这里是消息的入列,根据when,时间先后顺序,时间越早,越先进入消息队列。
这里需要注意的一点:调用了msg.recycle();
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
最终调用到了recycleUnchecked方法上,这样做是把消息的属性置空。
上面说的消息入列,下面看一下,消息的出列,消息的出列是通过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;
.......
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
.....
try {
msg.target.dispatchMessage(msg);
.....
} catch (Exception exception) {
.....
} finally {
.....
}
.....
msg.recycleUnchecked();
}
}
由于代码太长,我们只看关键代码,这里通过Looper获取到Looper对应的MessageQueue对象,然后通过一个for循环,调用queue.next()方法不断地从消息队列中拿到Message,拿到之后,就通过msg.target.diapatchMessage(msg);分发消息。这里的msg.target就是我们之前赋值的Handler的对象。dispatchMessage方法:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
关于Handler的回调:
如果调用了
handler.post(new Runnable() {
@Override
public void run() {
}
})
我们看一下Handler的post方法:
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
实际上是通过getPostMessage方法,封装成了一个Message对象,把这个Runnable实例,赋值给了Message的callback对象:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
然后会在Handler的dispatchMessage的回调方法中来进行判断:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
如果callback设置了,那么就会指向handleCallBack方法:
private static void handleCallback(Message message) {
message.callback.run();
}
这里相当于执行了,这里的run方法:
handler.post(new Runnable() {
@Override
public void run() {
}
})
如果没有设置那么我们就会走:
public void handleMessage(@NonNull Message msg) {
}
即
handler = new Handler(Looper.myLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});