Handler是我们在Android开发过程中经常会使用到或者与之打交道的一个类,比如我们会在子线程发起网络请求,然后到主线程进行UI的刷新。虽然现在有很多能够自由进行线程切换的相关库,比如RxJava,但了解Handler的原理还是很有必要的,也能够帮助我们更好的去使用相关库,其中的设计思想也特别重要。
1、Handler是如何创建的?
What?这也太简单了吧,看我们平常经常写的一段代码
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
这不是就创建了一个Handler么?但是,我们所写的这段代码究竟干了什么事呢?我们看Handler的构造函数
//当我们new 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());
}
}
//获取到Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {//注意这个异常,点1
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
以上代码就创建了一个Handler,其中需要注意的是点1,获取一个Looper对象,并且如果mLooper == null的时候会抛出异常。这就尴尬了啊,我们好像没有对Looper实例化啊,那这个Looper是在什么时候创建的呢?其实我们如果在主线程创建Handler的时候,其实得到的是主线程的Looper。如果我们将创建Handler的时候,如果我们在子线程创建Handler,如果没有调用Looper.prepare(),那么就会抛出上面点1的那个异常。所以如果我们是在子线程创建Handler需要这样创建。
//准备Looper
Looper.prepare();
//创建Handler
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
//开启消息循环
Looper.loop();
通过以上我们就创建出了我们的Handler。那么问题点又来了,我们在主线程创建Handler的时候好像并没有调用Looper.prepare();Looper.loop();这2个方法啊,为什么没抛异常呢?其实答案就是并不是没有调用,而是不需要我们手动去调用,系统已经自动帮我们调用了这2个方法。在ActivityThread这个类的main方法,也是整个应用程序的主入口处。我们其实都知道java代码的运行是需要main函数作为主入口的,而我们自己写的时候也没有写这个方法,而能运行起来的关键也是系统帮我们做了这件事。代码如下:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
............//此部分忽略很多代码
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
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");
}
好了,我们的Handler已经被创建了,那么消息是怎么放入,又是怎么取出的呢?接下来我们先来看消息的放入。
一、消息如何放入
我们平常使用Handler发消息有2个系列,sendMessage和post系列,其不同点在于send是直接发送一个Message,回调在我们 new Handler重写handleMessage的地方。而post系列需要传入一个Runnable,会回调到此Runnable里进行消息处理。sendMeage有很多不同参数的形态,我们以sendMessageDelayed作为例子看下代码。
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
为什么要看这个方法呢?其实看源码我们发现就算我们调用sendMessage其实它也是调用sendMessageDelayed这个方法。这段代码唯一需要注意的是SystemClock.uptimeMillis(),这个方法得到的是开机到现在所经历的时间毫秒数,然后加上我们所希望延迟的毫秒数,不就实现了一个延时执行的效果么。然后最终又调用了sendMessageAtTime这个方法,我们看下这个方法又做了什么。
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
//mQueue是我们创建Handler的时候得到的
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);
}
//上面的方法调用了此方法 传入queue message time
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);
}
//这方法是MessageQueue里的哈
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标记为可用状态
msg.markInUse();
//希望在何时执行
msg.when = when;
//mMessages可用理解为上一次入队的Message
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//p == null MessageQueue的Message为空 说明存储Message的链表是空的,我们的Message应该放在第一个
//when == 0 希望立即执行 需要放在第一个
//when < p.when 希望执行的时间比mMessage的时间小,也是要放在第一个
// New head, wake up the event queue if blocked.
//将当前Message的next指向mMessage
msg.next = p;
//更新mMessage
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();
//if里条件不满足的时候,说明消息需要做插队处理
Message prev;
for (;;) {
//先将mMessage指向prev 注意此时的mMessage就是上次入队的Message并且一定不为空
prev = p;
//将p指向p的下一个Message
p = p.next;
//p==null 说明后面没有Message了,可用插入此位置
//when < p.when 说明希望执行的时间比p的时间小,就应该插入此位置
//当满足以上2个条件的任意一个,说明已经找到了要插入的位置,跳出循环
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//将msg的next执向p
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.enqueueMessage(Message msg, long when)之后,就完成了我们Message的入队操作。里面的核心步骤也做了相应的注释,有的步骤理解起来有点傲,需要耐心的去慢慢推导。
二、消息如何取出
消息的取出其实就是Looper.loop()开启之后,然后进入一个死循环,去尝试取出我们的Message。我们Looper.loop()的代码。
public static void loop() {
//得到Looper自己
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//得到queue
final MessageQueue queue = me.mQueue;
.................//此部分代码省略
for (;;) {
//去queue里面获取Message 具体看后面的代码next()方法
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
............//此部分代码省略
try {
//dispatchMessage 调用的是Handler的方法,在下面的代码讲解
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
..............//此部分代码省略
msg.recycleUnchecked();
}
}
//MessageQueue里面的方法
Message next() {
.........//此部分代码省略
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//这是本地方法,主要是做休眠唤醒,节约资源
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//msg不为空且msg.target为空 这个target其实就是Handler 说明msg不可用,要重新去拿
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
//开启循环去拿消息,直到拿到满足我们条件的消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//这里面是拿到了消息,做一些善后的事
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
//最终会将我们的msg返回回去
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...............//此部分代码省略
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
//msg.target.dispatchMessage(msg); 调用的就是此方法
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {//msg.callback其实是通过post系列进来的 callback其实是一个Runnable
handleCallback(msg);
} else {//这部分是send系列的回调处理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//是不是特熟悉?不就是new Handler我们会重新的接收消息的方法么
handleMessage(msg);
}
}
以上贴出的为关键部分的代码,通过以上的流程就可以成功的取到消息了。
以上我们明确了Handler的创建,消息如何入队、出队。那么Handler、Message、MessageQueue、Looper在其中他们各自扮演了什么角色?还有我们常说的Handler导致内存泄漏又是怎么一回事呢?我们先看下这个几个类各自的持有对象情况。
Handler:
Looper mLooper;//Looper对象
MessageQueue mQueue;//MessageQueue对象
Callback mCallback;//回调 在new Handler中可以传入
Message:
Handler target;//持有的Handler
Runnable callback;//post系列传进来的Runnable
Message next;//当前Message的下一个Message
MessageQueue:
Message mMessages;//上一次入队的Message
//MessageQueue最主要负责了入队、出队的方法,以及一些Native方法
Looper:
Looper sMainLooper;//主线程Looper
MessageQueue mQueue;//MessageQueue对象
Thread mThread;//当前的线程对象
ThreadLocal sThreadLocal;//可以理解为Map存储,线程数据隔离
Handler通过send或者post系列,发送Message,MessageQueue负责Message的入队、出队操作,Looper负责震整个消息调度,最核心的为loop()方法,不断的从里面取出消息,发送到负责处理消息的Handler。
关于会引起内存泄漏,我们看下对象的引用线:Looper->MessageQueue->Message->Handler(message.target)->Activity 如果Handler非静态类,那么就是这样一条引用线,也就是说如果不需要的Message不移除,那么所持有的Handler就不会移除,而Handler又持有了Activity的引用,直到消息被出队。所以我们需要在Activity的onDestory中调用handler.removeCallbacksAndMessages(null);将Message移除。
关于Message的创建,new Message()和Message.obtain() ,前一种是直接申请一个Message内存空间,而后一种会在废弃Message池中复用Message对象,而好处就是可以内存复用,减少gc次数。这种内存复用池的方法在很多框架设计中都会用到,属于内存优化的范畴。