只要是做过Android开发的,想必对Handler都不陌生。在我刚工作的时候就去看过Handler的源码,但当时局限于对多线程的理解并没有那么深刻,所以对Handler的理解也就仅仅停留在表面。现在有时间重看Handler源码,猛然发现:我擦,Handler这么简单,竟然好多面试都去问,好多人都是马马虎虎,一知半解。所以我想在这篇博客里,把Handler彻底讲透了,以后妈妈再也不用担心我对Handler的理解了。
其实很多人对Handler的不理解,关键还在于对线程的不理解,所以我决定先简单说下线程的相关的知识。我在这也不能跟你胡说,在Java里一切都是对象,我们先看下Java源码里线程类的注释:
/**
* A {@code Thread} is a concurrent unit of execution. It has its own call stack
* for methods being invoked, their arguments and local variables. Each application
* has at least one thread running when it is started, the main thread, in the main
* {@link ThreadGroup}. The runtime keeps its own threads in the system thread
* group.
*
* <p>There are two ways to execute code in a new thread.
* You can either subclass {@code Thread} and overriding its {@link #run()} method,
* or construct a new {@code Thread} and pass a {@link Runnable} to the constructor.
* In either case, the {@link #start()} method must be called to actually execute
* the new {@code Thread}.
*
* <p>Each {@code Thread} has an integer priority that affect how the thread is
* scheduled by the OS. A new thread inherits the priority of its parent.
* A thread's priority can be set using the {@link #setPriority(int)} method.
*/
我在这也不逐行去翻译了,英语能看懂的就看看,按我的理解就是:线程就是一个执行序,有自己的执行栈。完了,就这么多。我知道这么说对于初学者者来说,肯定会在下面骂了,妈蛋,什么是执行序啊?先别急,把刀放下,咱慢慢说。我们写代码时,这个方法,那个对象,一堆乱七八糟的东西,要执行出一个确定的结果,肯定是要一个确定的顺序。包括多线程,只要逻辑上有先后顺序的信息,也一定要保证按确定的顺序来执行。
现在来想一下我们平时听了八百遍的多线程实现方式,实现 Runnable 接口,或者 new Thread() ,一个新的线程,我们不要忘了去调它的 start() 方法。这时才真正的启动了一个线程,去执行我们写在 run() 方法里的代码。其实从Android APP开发者的角度来讲,Thread的 run()就相当于Java框架给我们的一个执行代码的入口,让CPU来执行我们的代码(这么说当然是扯淡,但是你仅从APP开发者敲代码的角度去看,把编译,链接,虚拟机,操作系统全看成透明的,也就是这么个理)。
我们只要写好自己的代码,代码放进去 run() 的肚子里,然后按下开关(执行 start() 方法),他就一直按我们写好的顺序调下去,直到所有代码执行结束。必须明白的一点是线程就是一个执行序,在Java里,他是一个类,我们要执行的代码要放到他的 run() 的肚子里面。然后调 start() 告诉 Java虚拟机,给我开一线程的执行环境,开启后来执行我写在 run() 里的代码。
有了这个认知以后我们再来看Thread的源码:
public class Thread implements Runnable
这里看到的是Thread实现了Runnable接口,接着看下Runnable接口里面都定了哪些抽象方法:
public interface Runnable {
/**
* Starts executing the active part of the class' code. This method is
* called when a thread is started that has been created with a class which
* implements {@code Runnable}.
*/
public void run();
}
整个Runnable接口就这么可怜的一个 run() 方法。继续看Thread怎么实现的 run() 方法。
Runnable target;
public void run() {
if (target != null) {
target.run();
}
}
这里可以很清楚的看出来,无论是我们继承Runnable接口,还是直接new Thread实际上是没有区别的。都是最终要把需要执行的代码放到 Thread run() 的肚子里。然后调 start() 方法
public synchronized void start() {
checkNotStarted();
hasBeenStarted = true;
nativeCreate(this, stackSize, daemon);
}
start()这里就很显了 nativeCreate() 看名字就知道是调用C或C++去创建一个线程了,而创建线程以后呢,自然去调 run() 方法肚子里我们写的代码来执行了。
这时我们已经理解了我们Java层线程的意义。至于什么去stop线程啦,设置线程优先级,让线程去sleep啦,和start()基本一样的,是交给Java框架(这里框架这个词可能不太好理解,容易误解为J2EE的框架。其实这样说是相当于把JDK当作一种框架来看,就像Android里用的FrameWork框架。)来处理的。不是我们调了这个方法就执行的,而是我们只能通过框架给我们写好的,暴露出来的接口,去跟框架交互,至于它背后是同步,异步,或者等两秒,或者怎样搞,这些只是线程具体实现上的事,跟我们理解这个线程是一个执行序没有一毛钱的关系。如果想对线程有更深入的了解,理解线程特性,可以去看一些JVM的书(比如,深入理解Java虚拟机),里面有具体的线程特性。
到了这里发现简单说下线程,似乎说了好多,并没有收住,还将继续说下去,其实还是想尽力说的明白些,因为从个人的经验来看,对线程的理解直接决定了对Handler的理解,懂线程的根本就不会不懂Handler,懂Handler不懂线程的,绕了几圈还是会晕。而且,我以前在学习Android的时候也被好多不负责任的博客折磨的死去活来。所以我最讨厌那种上来就甩你一脸定义,代码,专有名词,乱七八糟的东西,老子懂这些的话还看你在这逼逼?
扯远了,继续说线程,刚刚说线程就是执行序,那么主线程呢?(在Android里一般主线程,UI线程,main线程都是在说一个玩意:主线程)。主线程既然是线程,它也是一个执行序,它也是由Java框架创建一个线程,然后执行写在它的 run() 方法肚子里的代码。现在要注意了,重点来了:
当我们点击桌面图标,桌面实际也是一个APP,上面放几个View,这些View的Image资源都是每个APP的图标,点击后就去执行的代码就是new一个进程,进程的资源什么乱七八糟的东西有一堆是根据你在APP的Manifest文件里面配置生成的。这一部分在这不讲,只需要知道,进程的必然要有一个主线程来执行APP里面写的代码吧。
在这里我们不去装逼的拿源码去讲什么第一个Activity的启动,还是按照上面的一个线程创建出要执行 run() 肚子里的代码。假如我们在里面写怎样去加载View,怎样去绘制View,然后展示出来,再然后呢,当View也绘制好了,展示出来了,界面都呈现出来了,这时代码执行序不就结束了么,按道理APP也就关掉了啊,不可能等在那让我们点击啊。其实这就是关键了,我们要想让这个主线程不执行结束,不死掉,应该还是很好解决的。比如直接在前面代码执行完以后,View显示出来了,让主线程去执行一个Whlie(true)的循环,这时不就万事大吉了么。View显示出来,一直显示着。
但是这样还有一个问题,就是主线程一直在while循环里跑,用户无法交互,主线程也无法继续执行这代码。其实这时候我们自己都能想到了,让主线程跑在while循环里,让他每次循环都去访问某个地方,看看有没有什么事要做,有就拿来做了再跑循环,没有就继续跑循环直到有事情做。其实Android整个UI机制就是这样的,装逼点说就是基于事件的机制,Windows其实也是这样的。
主线程跑在一个循环里面,而这个循环就是Looper的 loop() 方法。是时候让主角之一登场了:
说实话Looper的源码加注释真的写的相当清楚明白,而且代码量极少,我们直接看最前面关于Looper的注释:
/**
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
*/
最前面很显的写着:Looper这个类一般用于让线程去做一个消息的循环,线程里面本来是没有一个消息循环和线程相关联的。如果要创建一个有loop循环的线程,就要在线程的run方法里去调执行loop,让它在loop的过程中去处理消息。怎么样,和我前面讲的基本一样,我以宋吉吉的人格发誓,我之前没仔细看过这段注释,果然英雄所见略同。然后,注释又写了个例子,教你怎样写一个有Looper的线程。抬眼一瞅,这不就是在run方法的肚子里执行 Looer.loop() 么。。。
根据我们上面讲的线程的原理,即使不看这个 loop() 方法,先来猜一发,看看它里面会怎么写,无非是一个 while(ture) 循环,然后在里面每次有一个去消息,把消息里的代码执行的操作,好,上源码,接下来就是见证奇迹的时刻:
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
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();
}
}
额额,loop() 方法是。。。日,竟然是个 for(;;) ,啪啪,这个不算打脸吧,和while(true) 一样嘛。继续看for循环里面的代码:
Message msg = queue.next();
这行就是从MessageQueue里面取一个Message,也就是从消息队列里取一个消息。如果消息是null,就丢弃掉,然后接下来自然不出我们所料要处理消息了:
msg.target.dispatchMessage(msg);
这行自然就是处理消息了,这里面为什么这样写也是有技巧的,让我来解释一下(装一下逼):
因为线程在执行 loop() 的时候,并不知道要在每次循环去执行什么代码,所以在定义这个google在定义这个框架的时候,必须留一个接口,以你来写代码,框架来调用的形式定义。不看源码一切都是瞎逼逼,直接看源码,这个msg是一个Message对象,也就是一个消息,这没什么好说的。Message里有一个叫target的属性是Handler类型的:
/*package*/ Handler target;
在Handler发送这个消息的时候会把Handler会把自己的引用放到这个消息的target属性里:
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;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
所以最终loop循环如果在消息队列里取到一个消息,就去执行发送这个消息的Handler的 dispatchMessage(msg) 方法,如果我们在去看这个方法,他一定会预留一个接口让我去写代码:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
看到这最后调的这个方法我们应该就很熟悉了,我们平时new一个Handler一般都是这两种方式嘛:
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
或者
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
这两种方法并没有什么鸟区别,一个用了接口,都是在 handleMessage(Message msg) 方法肚子里写我们要执行的代码。
再回到前面说的,线程在loop中循环,每次循环,没有消息就继续循环,取到消息就执行执行发送该消息的Handler的 dispatchMessage(Message msg) ,而这个方法最终执行的是我们填入代码的 handleMessage(Message msg) 方法。这样就实现了预留出接口,通过循环,让主线程一直取消息,然后我们通过发消息到MessageQueue,并写好收到消息后要执行的操作,让主线程来调,然后在循环过程中执行不同的代码,实现交互。
其实明白了主线程在loop循环的过程中是怎么工作的,Handler也就简单的一笔了,但是它里面还是有一些设计思想在里面。我准备多写几篇,具体到每行代码,把MessageQueue,Message,Handler的源码都拿过来晒晒,然后理解每一行代码的作用。虽然可能开发中使用不需要这么细致的理解,但是Android源码里面的设计思想还是有很多值得借鉴的地方,看着还是挺有意思的。
如果在这篇有没有看懂的地方也不用着急,因为后面会更细致的把整个涉及的知识讲完。
(本篇完,下篇待续)