安卓应用启动时,默认创建UI线程(主线程),默认应用所有操作都放在主线程中执行,但为了保证系统的流畅性,通常把耗时的操作放入子线程中执行,例如网络访问、IO操作,如果在主线程中执行耗时长的操作很有可能出现ANR错误。本文就安卓中的handler消息机制进行源码上的简单解析。
handler消息机制涉及到四个元素,handler、Message、looper、MessageQueue。简言之,handler发送Message到MessageQueue中,looper不断从MessageQueue提取消息到对应的handleMessage代码处理。那么他们内部之间协调是怎么代码实现的?
从handler构造函数开始看起。
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();//获取looper对象
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//获取MeaasgeQueue
mCallback = callback;
mAsynchronous = async;
}
可以看到在handler构造方法中调用Looper.myLooper()方法获取到了looper对象与之关联,而获取MessageQueue对象则是通过mLooper.Queue方法获得对象关联。请看looper源码:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();//通过线程获得looper
}
handler从myLooper方法中获得looper对象,从myLooper方法中可以看到是从sThreadLocal(ThreadLocal用于不同线程间互不干扰地存储并提供数据)中获得looper对象。那么线程和looper又是怎样绑定?
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
上述源码在UI线程创建时也会被调用,调用如下:
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("");
Looper.prepareMainLooper();//创建主线程的looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
在执行prepareMainLooper方法首先会执行prepare(false)方法,看其源码:
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
}
此时可以发现:looper与当前的线程关联,而handler又与looper关联。前文中可以看到handler获取MessageQueue对象时是从looper中获取的,那么MessageQueue对象何时与looper关联?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
从looper构造方法中可以看到,looper构造时创建了一个MessageQueue对象,所以MessageQueue对象是存在于looper中的。
此时,可以总结关系:handler与looper关联,MessageQueue创建于looper中,looper与线程关联,handler与MessageQueue通过looper关联,handler与线程关联。此时可以解释为什么handler需要创建在UI线程中?因为handler需要与UI线程关联,这样handleMessage方法才能执行在UI线程中,UI更新才能线程安全。至此内部关联关系已了解。那么消息机制又是如何运转的?
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("");
Looper.prepareMainLooper();//创建looper
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");
}
上述为UI现场main方法可以看到在创建looper后调用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(); // 从消息队列中获取消息
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//省略部分源码
try {
msg.target.dispatchMessage(msg);//分发消息
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//省略部分源码
msg.recycleUnchecked();//message复用 } }
从上述代码可以看出从looper中获取到消息队列后建立死循环不断从队列中读取messsge通过dispatchMeaasge方法分发处理,处理完成后通过recycleUnchecked方法回收message对象。其中对于消息处理的关键代码是:msg.target.dispatchMessage(msg);//分发消息。可以看一下message源码:
Handler target;
Runnable callback;
Message next;
message对象内部创建了一个handler类型的target对象(为什么创建handler稍后解释),再调用其的dispatchMessage方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
从dispatchMessage源码中可以看到message自带的runnable类型的callback若不为空则调用handleCallback方法,如下:
private static void handleCallback(Message message) {
message.callback.run();//直接在子线程中执行
}
何时runnable不为空?当我们使用handler去post一个runnable时,例如:
handler.post(new Runnable() {
@Override
public void run() {
}
});
看一下一步一步实现的源码:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {//包装为message对象
Message m = Message.obtain();
m.callback = r;
return m;
}
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;//message中的handler与当前handler关联
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//加入消息队列
}
从上述代码中可以看到post一个runnable对象的时候也会将其包装为一个message对象并加入到消息队列中。而之前遇到的在message中创建handler对象在最后一个函数中与当前的handler关联,所以message在被handler发送出去后最终绕了一圈还是与当前handler关联处理。
而如果是普通非runnable的message的话则直接调用:
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
mCallback是一个回传接口,handleMessage是其中的方法:
public interface Callback {
public boolean handleMessage(Message msg);
}
我们在创建handler时重写了的handleMessage中的代码此时被执行。举例如下:
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_PROGRESS:
showProgressBar();
break;
case HIDE_PROGRESS:
hideProgressBar();
break;
case UPDATE_DIALOG:
customDialogAssociateDetail.initView();//更新dialog
default:
break;
}
super.handleMessage(msg);
}
至此,源码解析完成。总结,handler不断产生message添加到messageQueue中,looper不断从messageQueue中取出message给handler dispatch分发后进行处理。
特记下,以备后日回顾。