读前须知
本文是对Android Handler源码的解析,对Handler的基本使用还不了解的朋友可以先快速学习一下Handler再来阅读本文。文章篇幅较长,可以根据需要跳着阅读,或按顺序耐心阅读。
Handler
最直接的创建方法就是不带参数new一个Handler对象了。
Handler handler = new Handler();
//Handler.java
public Handler() {
this(null, false);
}
...
private static final boolean FIND_POTENTIAL_LEAKS = false;
...
public Handler(Callback callback, boolean async) {
\\1
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());
}
}
\\2
mLooper = Looper.myLooper();
\\3
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//4
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
直接跳进构造方法。看到代码块1,它是用来监测Handler创建方式有没有内存泄漏的风险的。一个类继承了Handler类。
A:匿名类 或 内部类 或 局部类
B:不是静态的类
如果A和B都达成,就认为它的对象实例有内存泄漏的风险。其实这很好理解,举个例子。
public class MainActivity extends AppCompatActivity {
private Handler mDownloadHandler = new Handler(){
...
};
...
}
MainAcivity已经destory掉了,mDownloadHandler由于是匿名内部类,它会持有MainAcivity的对象引用。GC时发现这个Activity还被其他可达对象引用,所以不能回收它,这就导致了内存泄漏。
不过FIND_POTENTIAL_LEAKS是false的,且为常量,所以代码块1是不会执行的,这个代码块应该是编写Handler类的程序员拿来测试用的。接下来看到代码2,跳进myLooper看看。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这里是一个关键的地方,从代码可以看出,每个线程都应该有只属于自己的Looper。
继续看代码,到了代码3的会做一个判断,如果没能从这个线程获取到Looper,就抛出异常。
new Thread(){
Handler handler=null;
public void run(){
handler =new Handler();
};
}.start();
在一个普通的线程上new一个Handler是会抛出异常的,因为普通的Thread并没有自己的Looper,不能让整个消息机制运作起来。需要特殊的Thread才能完成这样的任务,那就是HandlerThread,在后面会讲到。
代码3能运行通过,默认Handler构造方法就基本执行完了,代码4,说明Handler和Looper要共同来维护一个消息队列。
...
final MessageQueue mQueue;
...
再看看另一种常用Handler构造方法,直接传入Looper,通常是用来处理异步任务的。
Looper looper = Looper.getMainLooper();
Handler handler = new Handler(looper);
...
//Handler.java
/**
...
@param looper The looper, must not be null.
...
*/
public Handler(Looper looper) {
this(looper, null, false);
}
...
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
构造方法相当简单,就是赋初值,但注释里强调传入的Looper一定不能为空。Handler其他的构造方法也是差不多的。接下来就不得不说Looper和HandlerThread了,从Handler的构造方法就可以看出它们的重要性。
HandlerThread
HandlerThread类的代码其实不长,感兴趣的朋友可以全部读一遍,这里就挑重点来讲。
public class HandlerThread extends Thread {
...
Looper mLooper;
private @Nullable Handler mHandler;
...
@Override
public void run() {
mTid = Process.myTid();
//1
Looper.prepare();
synchronized (this) {
//2
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
//3
Looper.loop();
mTid = -1;
}
...
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
//4
looper.quit();
return true;
}
return false;
}
...
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
//5
looper.quitSafely();
return true;
}
return false;
}
...
快速地把上面的代码阅读完,将代码1234 或 1235串联起来那就是:
1.生成一个Looper
2.获取这个Looper
3.进入Looper死循环,不断监听和处理消息。
4.当不再需要监听时,结束死循环,这Thread结束,整个HandlerThread就是这么运行的。
Looper
从上面的代码可以看出来HandlerThread只是提供了一个运行的线程环境,真正在维护消息机制运作的是Looper。分别分析刚才代码1~5Looper所做的事情。先看代码1的prepare方法。
Looper.prepare();
...
public static void prepare() {
//1.1
//quitAllowed = true
prepare(true);
}
...
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//1.2
sThreadLocal.set(new Looper(quitAllowed));
}
...
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
先看代码1.1,HandlerThread运行的消息处理器是可以被使用者退出的,也就是说Looper的控制权是部分对外开放的。那想想,什么Looper要把quitAllowed弄成false呢,那当然就是一些极为重要的Looper了,例如 Looper.getMainLooper()得到的主线程Looper。代码1.2,ThreadLocal在当前线程保存一个Looper的副本。
再看到代码2,获得的一定是刚刚放进去的Looper对象,因为是在同一个线程里使用ThreadLocal的set和get的,一个HandlerThread始终只对应一个Looper。(对ThreadLocal不了解的朋友,可以先熟悉ThreadLocal的相关知识)。
接下来就是整个消息机制的核心了:代码3的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;
...
Looper.loop()是一个静态方法,方法开始还是一样,先判断一下当前线程是否有Looper,继续看代码。
...
for (;;) {
//1
Message msg = queue.next(); // might block
//2
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
接下来就会进入一个死循环,不断从队列里获取消息。代码1的注释说队列可能会阻塞。如果得到的为空,就退出Looper循环(在调用Looper的quit方法的时候才会得到空值)。这个阻塞队列MessageQueue是Google自己实现的,看看next方法看看是怎么获取消息的。
Message next() {
//1
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
Message prevMsg = null;
//A
Message msg = mMessages;
...
if (msg != null) {
//2
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);
}
//3
else {
// Got a message.
//4
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
//5
msg.markInUse();
return msg;
}
}
...
代码1处,当调用了Looper的quit方法后mPtr就会得到0,next方法返回null,进而使Looper的死循环结束。代码A,mMessages就是消息链表头。
代码2的判断就是在问现在要不要处理此消息(也就是问这是不是一个延时消息,有的消息发送后不是立刻就要处理的,例如:Handler的sendMessageDelayed方法),延时时间还没到就先不处理,否则就进入代码块3立刻处理。
由于有需要立刻处理的消息,所以在代码4处把阻塞标记设为false,由于这个消息要拿来处理,处理完要回收,为了能够更好的管理这个消息(后面会将其回收至Message池),代码5处标上使用标记,具体如下。
/** If set message is in use.
* This flag is set when the message is enqueued and remains set while it
* is delivered and afterwards when it is recycled. The flag is only cleared
* when a new message is created or obtained since that is the only time that
* applications are allowed to modify the contents of the message.
*
* It is an error to attempt to enqueue or recycle a message that is already in use.
*/
/*package*/ static final int FLAG_IN_USE = 1 << 0;
代码5走完就可以返回获取到的消息了。如果没能从消息队列里获取到消息,那就继续往下走。
...
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
// Disposes of the underlying message queue.
// Must only be called on the looper thread or the finalizer.
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
...
如果没有获取到消息,且调用了Looper的quit方法,mQuitting标记就会为true,进而把mPtr设为0,当下次在回到刚刚next方法的代码1处时,Looper死循环就要退出了。
那要是Looper既没有消息(或消息延时还没结束)又不打算退出Looper循环呢?那就做点空闲时才做的事。
...
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
//1
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
//2
mIdleHandlers.remove(idler);
}
}
}
...
代码1从空闲任务列表里取一个空闲任务来处理,如果这个空闲任务返回false,在代码2就把它移出空闲任务列表。添加空闲任务代码如下:
handler.getLooper().getQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
//自己的空闲任务
...
//返回false,这个空闲任务就只会运行一次
return false;
}
});
我们来看看整个next方法的流程图。
实际上橙色方框是在for(;;)这样的死循环中不断运行的,直到有消息才返回。所以next方法所谓 “没有消息就阻塞” 其实并没有把Looper所在的线程阻塞掉,只是让这个线程处理空闲任务或空转罢了。
如果这是MainLooper那就要注意了,不要在空闲任务上做耗时的事,空闲任务太耗时就会导致Looper不能及时响应接收的消息。next方法就分析完了,继续看Looper死循环里接下来做的事。
...
try {
//1
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
看到代码1,target其实就是发送Message的Handler,跳进去dispatchMessage方法看看。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//1
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//2
handleMessage(msg);
}
}
代码2就是我们熟悉的handleMessage方法了,如果message有回调就运行代码1。这么一来,这个消息就到达目的地了。但是,这其实还没完,Message到达目的地、接收者处理完后,Looper还要对这个Message进行回收供下次使用,继续看Looper循环的代码。
...
msg.recycleUnchecked();
这个代码是最后一步。运行完它,Looper死循环就走完一轮了,跳进去看看。
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
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 = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//1
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
首先做的事情很简单,把该初始化的值初始化、该设null的设null。再看到代码块1,这里就是回收Message的地方,sPool是Message池。看完代码块1其实就可以知道这个Message池本质上就是一个链表,sPool是链表头。
Message
提到Message回收,我们再看看Message的获取,如下是获取Message的方式。
//A
Message message = new Message();
//B
Message message1 = Message.obtain();
这两种获取Message的方式都可以,先跳进Message构造方法看看。
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}
没有做任何事情,上面的注释推荐我们用Message.obtain();回顾一下我们刚刚分析的next方法,在获取到一个消息后会标上使用标记。
//5
msg.markInUse();
return msg;
我们通过A方式获取的Message在发送出去后会被标上使用标记。而用完后会被回收进Message池里。那问题就来了,如果每次我们都是通过A方式来获取Message,用完后被回收至Message池,那Message池就会越来越大,容易造成浪费。那B方式好在哪里呢?进去看看。
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
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();
}
代码很简单,先从Message池里找找有没有闲置的Message,没有再new一个,等发送完毕后再回收到Message池里。
接下来再看看消息发送,常用的方式有如下三种
handler.sendEmptyMessage(MSG_UI)
handler.sendMessage(msg);
handler.sendMessageDelayed(msg,1000);
但其实最终都是调用了sendMessageDelayed方法,就去看看。
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
再深入sendMessageAtTime方法。
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
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;
}
//1
return enqueueMessage(queue, msg, uptimeMillis);
}
代码1将消息入列,进去看看。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
再深入MessageQueue的enqueueMessage方法看看。
boolean enqueueMessage(Message msg, long when) {
...
//1
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;
//2
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;
}
...
return true;
}
代码1将消息标上标记,通过new方式得到的Message在这里被标记,使用完毕后回收至Message池。
代码块2的循环是通过时间来决定插入位置的,delay的时间越长越是排在后面。
找到插入位置插入消息就可以了。这么一来sendMessage就完成了。
ActivityThread及MainLooper
更新界面的Handler需要使用主线程的Looper,那主线程的Looper又是怎么初始化的呢?这就要提到另一个拥有Looper的线程:ActivityThread
public final class ActivityThread extends ClientTransactionHandler {
...
final Looper mLooper = Looper.myLooper();
...
public static void main(String[] args) {
...
//1
Looper.prepareMainLooper();
...
//2
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
//3
sMainThreadHandler = thread.getHandler();
}
...
//4
Looper.loop();
//5
throw new RuntimeException("Main thread loop unexpectedly exited");
}
...
这是我们最熟悉的main方法。代码1先准备好MainLooper,进去看看。
/**
* 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();
}
}
...
//MessageQueue.java
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
...
prepare方法传入false,也就是不允许退出,调用MainLooper的quit方法会直接抛出异常,它是非常重要的Looper当然不能随便就quit掉。再看看代码2,ActivityThread的构造方法。
ActivityThread() {
mResourcesManager = ResourcesManager.getInstance();
}
做的事情很简单,就是new一个资源管理器,资源管理器是个单例。接下来是代码3,获取主线程的Handler
...
final H mH = new H();
...
final Handler getHandler() {
return mH;
}
...
主线程的Handler类名就叫H,简单看看它的handleMessage方法做了什么。
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
...
case CREATE_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case UNBIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
handleUnbindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SERVICE_ARGS:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj)));
handleServiceArgs((ServiceArgsData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
handleStopService((IBinder)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
这里看到了Service的相关处理,这也就是为什么说耗时的任务不要直接在Service上运行,这些任务可以在Service中创建子线程来完成或用IntentService。
接着就是代码4,MainLooper启动它的死循环,这样一来,整个APP就可以监听更新UI的Message了。
runOnUiThread方法
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("hello world");
}
});
在Activity里我们可以通过它来更新界面,看看它的源码。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
代码很简单,如果是在主线程上调用runOnUiThread方法,就立即执行Runnable里的代码。否则就放入消息队列排队执行。