Android Handler消息机制不完全解析

时间:2023-03-09 05:41:47
Android Handler消息机制不完全解析

1.Handler的作用

Android开发中,我们经常使用Handler进行页面的更新。例如我们需要在一个下载任务完成后,去更新我们的UI效果,因为AndroidUI操作不是线程安全的,也就意味着我们不能在非UI线程中去操作UI,否则会抛出CalledFromWrongThreadException异常。

Handler的基本用法:

Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//do something
}
}
}; Handler handler1 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
//do something
}
return false;
}
});
handler.sendEmptyMessage(1);
handler.post(new Runnable() {
@Override
public void run() { }
});
...

上面是我们熟悉的Handler的用法,通过几天的源码阅读,今天写下我对Handler的不完全解析吧。为什么说不完全呢?因为能力有限,所以并没有包括native代码的解析。手动滑稽。


2.源码不完全解析

提前声明下,这里的解析基本上都是建立在网上查阅资料以及自己源码的理解。如果有问题,请指出。

在这里我们先查看Handler的构造方法

//无参构造方法
public Handler() {
this(null, false);
} //参数为Looper的构造方法
public Handler(Looper looper) {
this(looper, null, false);
} //参数为Callback的构造方法
public Handler(Callback callback) {
this(callback, false);
} //参数为Looper和Callback的构造方法
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}

从构造方法看,他们竟然后面的都有一个值为false的参数,我们随便看一个构造方法。

/**
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
//在子线程中创建Handler的时候会抛出的异常。
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//把Looper的MessageQueue赋值给Handler的mQuene属性
mCallback = callback;//Callback赋值
mAsynchronous = async;//是否为异步的标志
}

看这个方法的对最后一个参数的注释我们可以知道,最后一个参数是是否异步执行的标志,这个方法又是被隐藏的 @hide ,我们无法显示的调用这个构造方法,但是可以通过反射来调用这个构造方法(待测试)。OK,那么我们可以知道,我们所有的构造方法都是同步的,并没有异步执行。

上面的构造方法中包括了一个Looper对象,一个MessageQueue对象,我们来看看这两个到底是什么?什么时候创建的?

先来看看Looper

public final class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
//使用ThreadLocal来确保一个线程中只存在一个Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue;
final Thread mThread;
...
}

我们接着看下 Looper.myLooper() 这个方法:

public static @Nullable Looper myLooper() {
//直接把sThreadLocal中的Looper对象返回
return sThreadLocal.get();
}

我们可以看到该方法直接返回当前线程的Looper,如果为null的话,那么抛出了一个异常Can't create handler inside thread that has not called Looper.prepare(),当然啦,我们的Handler在主线程中创建的,所以我们得到的Looper对象一定是不为null的。为什么这么肯定呢?这要从App的启动流程说起(有点扯远了,也是我下个阶段正在看的)。我们知道Android 系统基于Linux的,我们每个App都相当于一个是系统的子进行。举个栗子:我们的Eclipse(好久不用了)中有很多个工程,当我们执行某一个工程的时候就相当于Eclipse给我们的工程创建了一个进程,通过调用工程的入口方法——main()方法来启动。我们App启动也是这个流程,入口在ActivityThread中。我们来看下main()方法:

public static void main(String[] args) {
...
//创建mainLooper。这里mark下,一会要看
Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread();
thread.attach(false); 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() 这个方法:

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");
}
//创建一个Looper,并和当前线程“绑定”
sThreadLocal.set(new Looper(quitAllowed));
} public static @Nullable Looper myLooper() {
return sThreadLocal.get();
} //ThreadLocal
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

解释下大概意思:创建一个Looper对象,并与当前线程绑定,接下来看下Looper的构造方法:

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

构造方法只是创建了一个MessageQueue对象,这里简单看下MessageQueue的构造方法:

//quitAllowed是否允许停止
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();//native方法
}

我们这里看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 {
//调用Handler的dispatchMessage方法
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();
}
}

从构造方法可以看出,我们的mainLooper是不允许停止的。看到这里我们要转回去看看我们的Handler sendEmptyMessage 是如何处理的:

public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
} public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();//生成一个Message对象
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
} public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
} public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
} 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);//消息入队操作
} private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//这里注意下,把当前Handler对象赋值给target,后面会用到
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

上面的代码也挺简单,执行过程中如果 Messagenull 的话,就生成一个Message,把当前Handler赋值给Message对象的target属性,然后调用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);//唤醒Looper.loop()中的queue.next(),继续执行
}
}
return true;
}

从上面的代码看到,我们的Handler把这条消息放入到MessageQueue的尾部,即先进先出,如果这个队列中没有消息,即当前处于阻塞状态,则唤醒Looper.loop() 中的 queue.next(),继续处理这个消息。我们上面提到了Looper.loop() 这个方法,它是一个死循环,如果当前的消息不为null的时候就去执行msg.target.dispatchMessage(msg),去处理这个消息:

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
//mCallback的handleMessage方法如果返回true,则不会继续执行handleMessage方法
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
} private static void handleCallback(Message message) {
//这个callback是通过handler.post(new Runnable)方式传递过来的,执行其run()方法
message.callback.run();
} public interface Callback {
public boolean handleMessage(Message msg);
} public void handleMessage(Message msg) {
}

在处理消息的时候,先判断msg.callback是不是为null,如果不为null,则执行其run()方法,否则判断Callback是否为null,如果不为null则执行Callback 中的 handleMessage方法,并根据其返回值来判断是否执行HadlerhandleMessage方法。 所有的方法都是在当前线程去执行的。

By the way, ViewRootImpl中有一个checkThread()方法,用来判断是不是处于 当前 线程

由于能力有限这里我没有对native方法进行解析,有机会的话再去深入研究下。


3.我的理解

简单的说,我认为Looper、Handler就是让当前线程执行当前线程中所有的消息,Handler对象在哪个线程被创建,其消息的执行将会在被创建的那个线程中执行。

基于Handler和Looper的源码,我自己写了一个类似Handler消息机制,便于理解和记忆。源码在GitHub上,欢迎讨论。