一直以来,都对Handler不甚了解,如今花点时间自己去阅读源码,看看Handler是怎样运用的。
在系统自带的Launcher2中,就有使用Handler的,下面就看看Handler在其中的运用。
任务的前半生
在Launcher.java (位于packages\apps\launcher2\scr\com\android\launcher2下)
的onCreate()
方法中,会调用LauncherModel.java
的startLoader()
方法,其中调用了Handler
的post(Runnable r)
方法。
//LauncherModel.java
public void startLoader(boolean isLaunching, int synchronousBindPage) {
//此处略去一部分代码
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
//sWorker 是类LauncherModel的一个类成员变量,Handler的一个实例,
//mLoaderTask是实现Runnable接口的类LoaderTask的一个实例
sWorker.post(mLoaderTask);
//此处略去一部分代码
}
我们追踪一下mLoaderTask
的去向。post(Runnable r)
把mLoaderTask
传给了getPostMessage(r)
,而后者返回了一个Message
对象,mLoaderTask
封装其中。
//Handler.java
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
//Handler.java
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
//mLoaderTasker 赋给了Message的callback成员变量
m.callback = r;
return m;
}
接着,封装着mLoaderTask
的 Message
继续被传送,经过sendMessageAtTime()
传入到enqueueMessage()
中,通过跟踪Handler的函数调用我们发现,实际上,Handler
的post*()
方法,辗转都会调用到enqueueMessage()
方法。
//Handler.java
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//Handler,java
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);
}
我们先看看sendMessageAtTime()
函数,其中有一句 MessageQueue queue = mQueue;
,出现了mQueue
是Handler
的一个实例变量,这个mQueue
哪来的我们稍后讨论,在这里把封装mLoaderTask
的Message
和MessageQueue
一同传给了enqueueMessage()
。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
我们看到Handler
把自己也封装给了Message
的target
函数并传给了MessageQueue
,为什么要把自己封装进去呢?我们先带着疑问接着往下看。到这里,我们终于知道,我们 post
的mLoaderTask
被保存在了MessageQueue
维持的一个队列里。貌似Handler
的任务已经完成,mLoaderTask
已经出了服务区,但是它又是怎么被执行的呢?就像快递经过多个转运中心已经到目的地了,接下来谁把他取走呢?
任务的下半辈子
这貌似无从下口啊,咋知道谁把它拿走了?细细推敲,我们发现现在既然把它交给了MessageQueue
的实例mQueue
,我们只能先看看mQueue
哪来的了。
在Handler.java
中,对mQueue
的赋值只有两处地方,分别是Handler
的两个构造函数中,说明mQueue
是在 Handler
实例化的时候赋值的。那我们去看看在哪里创建了Handler
的对象。
通过上面我们知道,我们最初调用post()
是在LauncherModel.java
中,调用的对象是sWorker
,而sWorker
的的赋值语句如下:
//LauncherModel.java
private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
sWorkerThread.start();
}
private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
可以看到,sWorkerThread
、sWorker
都是类变量,也就是说类LauncherModel
创建实例的时候,就会创建一个HandlerThread
实例并运行,并且创建了一个Handler
实例。
上面说说到,mQueue
有两处赋值的地方,分别是Handler
的两个构造函数,而Handler
的所有构造函数,最终都会调用到这两个构造函数其中之一。而我们源码中最终调用到的,是下面这个,
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
这样,我们就弄清楚了,Handler
中的 mQueue
是Looper
维持的。这个Looper
,是通过HandlerThread
的getLooper()
获得。我们来看看 getLooper()
内部的代码,
//HandlerThread.java
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
最后返回的是sWorkerThread
的一个成员函数mLooper
,我们跳转到它定义的地方,Looper mLooper;
看到确实是Looper
的一个引用。接下啦看看它在什么地方赋值,
//HandlerThread.java
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
//此处赋值
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
整个HandlerThread
中只有此处对mLooper
赋值。接下来我们继续进入myLooper
看看到底其中做了什么。
//Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
接下来我们接着看看sThreadLocal
定义的地方,
//
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
发现它是Looper
的一个类变量,还有疑问啊,Looper
变量啥时候放进去的呢?浏览Looper.java
发现只有调用 sThreadLocal.set()
的地方只有一处,就是在prepare()
中。
//Looper.java
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));
}
这回我们也知道了,为什么要先调用prepare()
,因为只有调用此函数后,才会创建Looper
的实例;到这里我们知道了,在调用prepare()
之后,整个类中就有持有了线程中一个唯一的Looper
实例,之后,通过Looper.myLooper()
获得的Looper
的实例是同一个。因此sWorkerThread
、sWorker
就持有的是同一个Looper
实例。
回到上面的run()
函数,接着会执行loop()
函数。
//Looper.java
public static void loop() {
final Looper me = myLooper();
//.......
final MessageQueue queue = me.mQueue;
//.......
for (;;) {
Message msg = queue.next(); // might block
//.......
// 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);
}
//.......
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//......
msg.recycleUnchecked();
}
}
我们发现,在这函数中循环执行到msg.target.dispatchMessage(msg);
咦target
,上面我们发现Handler
的对象在把Message
传给 MessageQueue
的时候把自己封装进了Message
,因此兜了一圈,Message
在队列中呆了一会儿后,又被传回了Handler
里面。
//Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
最终调用到handleCallback();
最终,我们封装的Runnable
的run()
函数就在这执行了。至此,我们post()
的Runnable
走完了全程。
//Handler,java
private static void handleCallback(Message message) {
message.callback.run();
}
简单总结任务的一生?
简单捋一捋,我们想要的执行的Runnable
,先由Handler
层层递送到 了Looper
维护的 MessageQueue
,然后,Looper
又把它取出来交给了Handler
去执行。
我们不禁黑人问号,这样兜了一圈图个啥?
其实这样一圈下来,Runnable
已经从一个线程,传递到了另一个线程。因为原本Handler
是在创建它的线程中,经过传递,通过Message
,他进入到了持有Looper
的 “launcher-loader”线程中,因此他就在另一个线程中执行了。
这里,为了直观感受Runnable
确实传递到了另一个线程,我们来写个demo。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HandlerThread hand = new HandlerThread("demo thread"){
public void run(){
Log.e("can reach?","yes");
super.run();
}
};
hand.start();
Handler handler = new Handler(hand.getLooper());
handler.post(new Runnable() {
@Override
public void run() {
Log.e("Who invoke me:",Thread.currentThread().getName());
}
});
//Log.e("",Thread.currentThread().getName());
}
上面,我们创建了一个名叫“demo thread”的线程,并在主线程中发送了一个Runnable
对象。通过输出日志,我们发现执行的时候确实是在另一个线程中的。我们的结论是对的!
这里,我们仍有一些疑问,有时候我们直接在主线程中创建一个Handler
实例,然而并没有调用Looper.prepare()
,为什么它不挂掉?
这个,就留着下次再分析吧。