Android Handler消息机制应用层详解
Android Handler消息机制应用层详解
Android的Handler消息机制经常会用到,实际上Android程序就是消息机制推动的,在Android程序启动的时候就用到了Handler消息机制。同样Android设计的时候就把Handler消息机制留出API接口来供开发者使用,Handler可以作为很好的工具来使用。所以分析Handler消息机制就分为两部分,一部分是Handler使用流程和原理,另一部分是Android程序启动的时候是怎样使用Handler消息机制的。
看完全部内容相信对这理论会有好的理解。
我们接下来首先分析Handler消息机制涉及的几个角色,然后从源码看Android程序启动时Handler的执行,最后我们再看在使用的时候Handler都干了什么事情,这样我们就会对Handler消息机制有个清晰的理解。
一、参与的角色
Handler消息机制参与的角色也就四个,分别为Handler、Message、MessageQueue(消息队列)、Looper,这四个类都在android.os包下。我们来分析一个这四个类中都有哪些东西。
1、Handler类
1.1、interface Callback
public interface Callback {
public boolean handleMessage(Message msg);
}
1.2、handleMessage方法
public void handleMessage(Message msg) {}
1.3、dispatchMessage方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
看上面的源码:首先会判断方法传入的参数Message是否有callback,(这个callback实际上是Message类中的成员变量,是一个Runnable对象)如果有就会执行handleCallback(msg)方法,然后dispatchMessage方法就结束了,不会进入else语句中。如果没有的话当然会进入else语句中。
我们也看一下这个handleCallback(msg)方法:
private static void handleCallback(Message message) {
message.callback.run();
}
这个方法就是运行传入的参数Message的callback的run()方法。再说一下这个callback就是一个Runnable对象。
接下来就是else语句里:
判断mCallback是否为空,这个mCallback就是1.1中的接口Callback的实现类对象,这个实现类是我们使用的时候可以实现的,(下面详细描述)如果我们使用的时候实现了Handler的这个Callback接口,那么就会调用接口中的handleMessage方法,如果这个handleMessage方法返回true的话就return了,dispatchMessage方法结束了不会执行下面的handleMessage方法了(这个方法就是1.2说的方法)。
可能有点懵,实际上这个方法表达的意思就是,在我们使用的Handler的时候有三种方法去处理消息,第一个是在发送Message的时候传入一个Runnable对象,run方法就是我们处理消息的代码。第二个是我们可以实现Handler中的Callback接口,实现接口中的handleMessage方法,方法中就是我们处理消息的代码,在创建Handler的时候把实现类传入。第三个是创建Handler的时候直接重写Handler中的handleMessage方法,方法中是我们处理消息的代码。这三种方法在dispachMessage中被控制,如果使用了第一种那么其他两种就不要用了,用了也不执行。如果没用第一种,用了第二种,那么在写接口中handleMessage方法的时候如果返回true,第三种就不用写了不会执行,如果返回false,第三种会执行。如果不用第二种,那只能使用第三种了,执行重写的handleMessage方法。
看图更清晰:
这个流程图就说明了dispatchMessage方法的执行流程。这样设计代码的作用是分离代码,上一个可以约束下一个的执行。这样的设计模式很常见,在平时开发的时候也可以使用。
1.4、handler的构造函数
handler的构造函数有7个,实际上调用的就两个,一个有三个参数,一个有两个参数。
1.4.1、三个参数的
public Handler(Looper looper, Callback callback,boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
这个构造器的需要传入Looper,这个Looper需要我们使用的时候自己创建,所以这个构造器的作用就是我们在子线程使用Handler的时候,先创建线程的Looper,然后传入到Handler的构造器中创建Handler对象。(下面会详细讲Looper)。
1.4.2、两个参数的
public Handler(Callback callback,boolean async) {
//从ThreadLocal.get()拿到本线程的looper对象
mLooper = Looper.myLooper();
//拿到主线程的Looper对象的MessageQueue对象
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
这两个参数的构造器其实不是给我们用的,Android系统调用的,在启动主线程的时候系统会自动创建主线程Looper,所以就不需要传入Looper了,只需要直接获得Looper就可以了。
1.5、getMessageName(Message msg)//打印Message信息的,忽略。
1.6、obtainMessage捕获消息方法
这个方法有好几个重载的方法,实际上都是调用Message.obtain()方法。详细到Message中说。
1.7、发送消息的方法
发送消息的方法有好多,但是最后都是调用sendMessageAtTime()和sendMessageAtFrontOfQueue(),这两个的方法区别是传入的时间延迟不同,AtTime表示延迟时间大于0,AtFrontOfQueue表示在消息队列前,传入的时间为0.
其中在众多发送消息的方法中以post开头的都需要传入一个Runnable对象,然后这个Runnable会被一个Message所持有,然后这个Message会在sendMessageAtTime()和sendMessageAtFrontOfQueue()这两个方法中传到上面1.3的dispatchMessage方法中,这个Runnable就是第一个判断的callback。(是不是明白了点,继续!)
回来继续,发送消息的方法最好都调用了sendMessageAtTime()和sendMessageAtFrontOfQueue()这两个方法,这两个方法最后都调用的是一个相同的方法就是enqueueMessage方法。
1.8、enqueueMessage方法
这个方法做了两件事,一件是把传入的Message的target属性设置为本Handler对象,这样是让每个Message中都持有发送它的Handler的引用,意思就是让Message知道哪个Handler发的它。第二件事是都调用了queue.enqueueMessage(msg,uptimeMillis)方法,这个queue是消息队列,这个对象是在上面构造器中传入的MessageQueue。
1.9、removeMessages方法
实际上在调用消息队列的方法
mQueue.removeMessages();
1.10、总结:1)、Handler中捕获消息的方法是调用的Message中的捕获消息的方法。
2)、Handler中发送消息的方法都是调用MessageQueue的enqueueMessage方法。
3)、Handler中移除消息的方法都是调用的MessageQueue的removeMessage方法。
总之它都是在用别人的方法。
2、Message类,实现了Parcelable
2.1、属性
int what、身份标识
int arg1、int arg2、Object obj
int flags、
long when决定社么时候处理消息
Bundle data、
Handler target 发送它的Handler //上面1.8中的target
Messenger replyTo 邮差
Runnable callback //上面1.3和1.7中提到的callback
Message next 持有的下一个Message的引用
//消息池中第一个消息,静态的(全局共享)
因为是全局共享的才有消息池的意义,如果每个线程都有消息池就没有意义了
private static Message sPool;
//消息池容量
private static int sPoolSize = 0;
//最大容量
private static final int MAX_POOL_SIZE = 50;
2.2、消息池
不管是消息池还是线程池的作用都是对象复用和限制上限
消息池的原理是sPool为第一个,头指针放在静态区(全局共享)
然后每个Message都有一个next属性,持有下一个Message对象引用
这样就形成了一个单向链表。
2.3、方法obtain()
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();
}
同步是为了防止多个线程捕获消息出现异常
原理是如果头指针不为空,把头指针赋值给一个局部变量 m ,然后
把 m 的next赋值给sPool,m 的next赋空,把消息池数量减1,然后把
m 返回去。
如果头指针为空就new消息对象
其他有参数的obtain方法都是调用这个无参的,只不过把参数赋值给了
Message的属性,捕获的是属性有值的Message对象。
2.4、recycle()
调用isInUse()方法用来判断消息对象是否正在被使用。
被使用不回收
如果消息对象空闲就回收,调用
recycleUnchecked()方法
在这个方法中首先把所有属性清空
然后判断池中的数量是否达到上限
然后把这个Message对象的next属性作为sPool引用
然后把这个对象作为头指针。消息池加1
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
2.5、 sendToTarget()
把消息自己发送到目标上去这样obtain消息的时候需要指定目标目标就是handler
实际上是调用这个消息对象target属性,也就是Handler的sendMessage方法
target.sendMessage(this);
3、MessageQueue类
3.1、方法enqueueMessage(Message msg, long when)
这个方法是Handler的核心代码,把消息排队到消息队列中。
按传入的时间单向列表对消息进行排队,放到消息队列中。
3.2、方法next()
Message next()这个方法就是从队列中取出消息
这个方法取消息是按Message的when来取,如果又这个时间要执行的消息就取出来,如果没有就休眠到有到时间的消息为止;
如果没有要执行的消息,就睡一会。如果有就把消息拿出来执行。
主线程不是一直都在执行,这个休眠小于16ms,
Android系统每隔16ms会发送一个消息自动刷新一次屏幕.
3.3、构造器
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
这个quitAllowed布尔值的作用是控制创建的这个MessageQueue是否允许被停止,在使用中,如果我们在子线程中用消息机制的话Looper中创建MessageQueue的时候会传入true,表示可以停止,如果是主线程Looper创建MessageQueue的时候就会传入false,不允许消息队列停止。
总结:MessageQueue类就是往队列里按时间存消息、从队列中取消息或者删除消息。
这个类中有native方法,这个不做深入研究。
4、Looper类
4.1、属性
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// ThreadLocal:它是一个类,每个线程有且只有一个自己的Thread Local对象。
// ThreadLocal是全局唯一的
//每一个线程都有一个value是集合来放自己的looper对象,
//value集合是map集合,键是唯一的ThreadLocal对
private static Looper sMainLooper; // 主线程的Looper对象
final MessageQueue mQueue;//消息队列(每个Looper会维护一个消息队列)
final Thread mThread;//该Looper所在的线程对象
4.2.构造器
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
这个构造器是私有的,说明Looper对象是单例的,可以猜测Looper类中肯定提供了静态方法来创建Looper对象。
quitAllowed这个布尔值在上面MessageQueue类的构造器中提过,是从这里传入的。这个mThread就是拿到的当前的线程对象了。
在这里可以看到在Looper的私有构造器中创建了MessageQueue对象,所以消息队列是Looper来维护的,一个Looper就只能有一个MessageQueue。
4.3、私有的prepare方法
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
......
}
sThreadLocal.set(new Looper(quitAllowed));
}
这个方法是关键方法,就在这个方法中创建的Looper对象,从代码中看到,调用Looper的私有构造器创建出Looper对象并且放入到ThreadLocal中去,这个传入的布尔值还是上面提到的会传入到Looper的构造器中,创建MessageQueue时控制队列是否允许停止。ThreadLocal控制一个线程只能有一个Looper,也只能有一个MessageQueue,但是可以有多个Handler。
4.4、公有的prepare方法
public static void prepare() {
prepare(true);
}
这个方法就是调用了私有的prepare方法,传入布尔值true。这个方法是供我们开发者使用的,当我们在子线程使用Handler消息机制的时候,由于子线程是我们自己创建的,所以Looper也需要我们自己创建,所以Android给我们提供这个方法,我们调用这个方法就可以创建出子线程的Looper了,然后它调用私有的prepare方法,Looper对象创建并且同时创建出MessageQueue,然后放到了ThreadLocal中。这样我们就可以用Handler发Message了。
4.5、prepareMainLooper方法
public static void prepareMainLooper(){
prepare(true);
aynchronized(Looper.class){
if(sMainLooper != null){
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
这个方法是Android系统调用的,用来为主线程创建Looper的。可以看到,它也首先调用了私有的prepare方法传入布尔值false,来让主线程的MessageQueue是不可以停止的。然后调用myLooper方法,从ThreadLocal中拿到主线程的Looper对象。
如下:
public static @Nullable Looper myLooper() {
return sThreadLocal.get(); }
4.6、loop方法
public static void loop(){
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for(;;){
Message msg = queue.next();
if(msg == null){
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
这个方法代码长,摘取了主要的代码。这个方法首先调用myLooper方法从ThreadLoacl中获得该线程了Looper对象,然后拿到MessageQueue对象,然后会开始一个无限的for循环,在循环中从MessageQueue中取出Message,然后把找到Message的target属性,也就是找到发送这个消息的Handler,调用该Handler的dispatchMessage方法。这样就回到了最初分析的地方。这就是完全的流程。我们在子线程使用的时候先在线程中调用Looper类的共有方法prepare方方法创建Looper后,然后须调用loop方法,开始循环,然后才能发消息。
在这个方法的循环中当MessageQueue中有消息了,Looper会从消息队列中取出消息去处理,如果没有的话就会阻塞,直到有消息了会唤醒。这个是怎样的原理不是这篇文章要说明的,以后有机会研究。
二、Handler消息机制原理
通过上面的分析,整个流程就很清晰了。我们在子线程中使用Handler的时候,我们首先创建子线程,然后调用Looper类的静态方法prepare方法,这时会调用Looper类中私有的prepare方法,传入布尔值true,方法中会调用Looper的私有构造器,创建Looper对象并且把Looper对象放入ThreadLoacl中,在调用Looper构造器的时间内部会创建一个MessageQueue对象,并且传入开始的布尔true,这样Looper对象就创建好了,同时它内部会维护一个MessageQueue。
然后我们需要调用Looper类的静态方法loop,让循环运行起来,这时候我们还没发消息,所以循环再阻塞。之后我们创建Handler对象,并且复写handleMessage方法,最后Handler开始发送消息,然后调用了Handler类的sendMessageAtTime方法把该Handler赋值给Message的target属性,然后调用Hanler的enqueueMessage方法,内部调用MessageQueue的enqueueMessage方法,把消息放到消息队列中,而Looper的loop方法循环发现有消息了就把这个消息取出,交给这个Message的target去调用dispatchMessage方法,内部调用handleMessage方法,这样就完成了一个消息的发送和处理。而循环还在等待下一个消息。
三、Android程序启动原理
我没在看看Android程序启动的时候是怎样使用Handler的。
1.ActivityThread类,这是一个隐藏类,源码位置在framework/base/core/java/android/app/ActivityThread.java
Android程序启动的时候是从ActivityThread类开始的,程序启动的时候首先从main方法开始的。
public static void main(String[] args){
......
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if(sMainThreadHandler == null){
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
这个方法就是Android程序的入口,可以看到首先调用了Looper的静态方法prepareMainLooper方法,在上面对Looper类的分析可以知道它是创建了主线程的Looper对象,然后在下面调用了Looper的loop方法,开始循环。当在主线程创建Hanlder后,发消息,就会被放到主线程Looper维护的MessageQueue中,然后在loop方法的循环中把消息取出,回调我们复写的handleMessage方法处理消息。
以上就是Android Handler消息机制在应用层的原理。
另外main方法中程序进入loop的循环中是永远不会结束的。而Activity的生命周期方法的调用等原理就是在上面4到8行代码执行的了,以后有机会继续分析这部分原理。