一、概述
在Android应用程序开发中,我们常常通过一个子线程来完成某些操作,然后把获取的数据通过Handler发送到主线程,通知主线程做相出应的操作。对于这种情况,Android提供了一套异步消息处理机制Handler。
1.1、主要成员
使用Handler方式进行异步消息处理主要由Message,Handler,MessageQueue,Looper四部分组成,怎么去理解这“四大金刚”呢?
那么接下来,我们简单剖析下Handler中的几个主要成员。
二、Looper
2.1、什么是Looper
Looper是Android为线程间异步消息通信提供的一种机制,利用Looper机制可以方便我们实现多线程编程时线程间的相互沟通。Looper通常运行在一个消息的循环队列当中,线程在默认的情况下,不会给我们提供一个消息循环去管理消息队列的。如果想管理消息队列,需要在线程当中调用Looper.prepare()方法使消息循环初始化,并且调用Looper.loop()使消息循环一直处于运行状态,直到停止循环。所以Looper主要就是完成MessageQueue与Handler进行交互的功能。
2.2、窥探Lopper
首先你觉得Android应用程序入口是什么呢?你肯定会说是应用的Application中的onCreate函数。其实读了Android源码才发现application并不是安卓程序的入口,而这个入口就是ActivityThread类,ActivityThread类是Android APP进程的初始类,它的main函数是这个APP进程的入口。ActivityThread类源码点这里
- ActivityThread类关于程序入口main函数部分源码:
public static void main(String[] args) {
//省略...
//创建Looper和MessageQueue对象,用于处理主线程的消息
Looper.prepareMainLooper();
//创建ActivityThread对象,并绑定到AMS(ActivityManagerService)
ActivityThread thread = new ActivityThread();
//建立Binder通道 (创建新线程)
thread.attach(false);
Looper.loop(); //消息循环运行
throw new RuntimeException("Main thread loop unexpectedly exited");
}
- 简单分析:
- 应用程序开始运行开始调用了Looper.prepareMainLooper(),后创建ActivityThread,然后调用Looper.loop()开启循环。
- 我们在Activity用Handler handler = new Handler()来创建Handler就默认绑定到主线程了,是因为上面的代码为我们做了绑定主线程的Looper的事情.
- 主线程中的Looper是不能在程序中调用退出的,如果调用的话,就会抛出异常,throw new RuntimeException("Main thread loop unexpectedly exited")。
2.2.1、Looper.prepare()创建
//将当前线程初始化为应用程序的主循环程序,prepare(false)函数参数表示为不可退出。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//Looper对象保存在ThreadLocal中的。ThreadLocal是一个线程内部的数据存储类,每个线程都有自己独立访问的变量副本。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建Lopper对象
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
//创建MessageQueue消息栈,并且获取和绑定当前线程
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- 通过阅读创建Looper源码可以明白:
- Android应用程序初始化创建了一个默认的Looper,创建默认Looper时,设置参数的为false,即消息队列是不能够退出的,如上述ActivityThread源码Looper.prepareMainLooper()。
- 一个线程只能创建一个Looper,每次调用prepare()都在ThreadLocal寻找当前线程的Lopper实例,不存在则创建,否则会抛出RuntimeException。
- Looper对象保存在ThreadLocal中的,ThreadLocal是一个线程内部的数据存储类。
- Looper创建,即创建了一个内部消息队列MessageQueue,且保存了此MessageQueue的引用,并绑定了当前线程。此MessageQueue属于Looper所在的线程。
2.2.2、 Looper.loop()循环
在ActivityThread类中,我们通过源码看到Looper在调用prepareMainLooper()之后,又调用了loop()函数。我们通过这个疑问来看下SDK源码:
public static void loop() {
......
for (;;) {
//不断取出下一条消息,mgs为null即消息队列退出,若没有消息且没有退出 消息队列一直阻塞的
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
try {
//分派消息,通过Handler处理
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
......
msg.recycleUnchecked();
}
}
public static @Nullable
Looper myLooper() {
return sThreadLocal.get();
}
我们看到在Looper.loop()函数中使用了一个for(;;)死循环,即通过MessageQueue.next()从消息队列中取出Message。next()不断取出下一条消息,mgs为null即消息队列退出,循环停止。若没有消息且没有退出,消息队列一直阻塞的。 当next()返回一条消息,Looper调用msg.target.dispatchMessage(msg)进行分派处理。这里的msg.target就是一个Handler,即调用了Handler的dispatchMessage()方法进行数据分发操作,在dispatchMessage最终调用了handleMessage(msg)方法。这样我们就可以正常处理发送到主线程的消息了。
2.3、Lopper小结
- Looper是循环器(理解为一个传送带,不停轮询),为一个线程运行消息循环,不断检查消息队列中是否有新的消息,并且一个线程只有一个Lopper。
- Looper.prepare()为当前线程创建一个looper,把创建的Lopper实例存放在ThreadLocal中,并在其内部维护一个MessageQueue。
- Looper.loop()即开始轮询获取MessageQueue中的Message,获取后通过dispatchMessage分发。
- Looper.quit() Looper.quitSafely()-退出Looper,自主创建的Looper建议在不使用的时候退出
三、Handler
3.1、什么是Handler
Handler主要包含消息的发送和接收处理(类似快递员,负责收发快递[数据])。
3.2、创建一个Handler
//在UI线程里面,我们可以最简单直接new一个Handler实例
Handler handler = new Handler();
//然后我们点进源码查看构造函数
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
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());
}
}
//获取当前线程仅存的Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//返回Looper对象的消息队列
mQueue = mLooper.mQueue;
//消息回调
mCallback = callback;
mAsynchronous = async;
}
- 通过阅读创建Handler源码可以明白:
- Handler初始化都需要4个参数Looper、Callback(消息回调)、async(是否异步)、MessageQueue(消息队列);
- 如果当前线程没有Looper实例,Handler将无法初始化实例。
3.3、通过Handler发送消息
Handler.sendxxx()相关API
//hand.sendMessage
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
//...此处省略其他发送方法
//沿着源码往下看,发现所有发送消息的函数最后都调用了MessageQueue中的一个函数:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
举个栗子看下如何在子线程中发送Message:
此时Handler发送Message,并且携带了what、obj、arg1、arg2等数据,调用enqueueMessage函数,把Message打包入MessageQueue队列中。此时通过Looper运输数据到了UI线程中,Hanler开始获取数据并做出对应操作。
3.4、Handler计划任务
Handler.postxxx()相关API:
post(Runnable);//提交计划任务马上执行
postAtTime(Runnable, long);//提交计划任务在未来的时间点执行
postDelayed(Runnable, long);//提交计划任务延时Nms执行
接下来我们看看sdk是怎么实现的:
//hand.post
public final boolean post(@NonNull Runnable r) {
//sendMessageDelayed()怎么和Handler.postxxx()调用同样的方式?
return sendMessageDelayed(getPostMessage(r), 0);
}
//...此处省略其他计划任务方法
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
发现Handler.postxx()把Runnable作为一个参数赋值给了Message.callback,返回Message对象,接着和Handler.sendxxx()一样的调用enqueueMessage()把Message依次放到消息队列中。
举个栗子:
3.5、Handler机制扩展
查阅资料发现,Android为了更方便使用Handler消息机制,有几种基于Handler的扩展方式:runOnUiThread(Runnable)、view.post(Runnable)
看下他们的实现方式:
//runOnUiThread(Runnable)
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
//view.post(Runnable)
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
至此,Handler不是负责收发消息吗?别急,我们继续往下:
3.6、通过Handler接收消息
前面我们说到Looper的时候,在Looper中loop()函数,有一个for循环,循环体中有这么一行代码:msg.target.dispatchMessage(msg); 这才是Handler接收数据回调的重点(五星),下面代码就是loop中调用的函数体:
/** 子类必须重写该方法进行接收数据
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
public void dispatchMessage(Message msg) {
// 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
// 则执行handleCallback(msg),即回调Runnable对象里复写的run()
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
// Message对象的callback属性 = 传入的Runnable对象
// 即回调Runnable对象里复写的run()
message.callback.run();
}
代码中告诉我们接收数据时,子类必须重写handleMessage来回调数据给UI线程操作:
3.7、Handler小结
- Handler的创建一定要基于Looper,没有Looper则无法创建Handler;
- Handler通过post/send系列函数来打包Message数据到MessageQueue,仅是将消息加入消息队列中;
- Looper循环通过queue.next()获取到一条消息,再通过Handler的dispatchMessage()分发处理。
四、MessageQueue
4.1、何为MessageQueue
MessageQueue简单来说就是一个消息队列。主要负责携带Handler发送过来的信息入队列(enqueueMessage)和按顺序取出要执行的Message,。消息队列是单链表实现的。
4.2、创建一个MessageQueue
//调用Looper.looper()时就默认创建
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//用于标示消息队列是否可以被关闭,主线程的消息队列不可关闭(分析见上面[2.2、窥探Lopper])
private final boolean mQuitAllowed;
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
4.3、MessageQueue之next()
以下源码注释参考:Handler用法及解析
//不停提取下一条message
Message next() {
final long ptr = mPtr;
//判断是否退出消息循环
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
//代表下一个消息到来前,还需要等待的时长
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//native层阻塞cpu。如果被阻塞,唤醒事件队列
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//如果当前消息是异步消息,都将赋值给prevMsg,过滤掉,直到取到了非异步消息
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//获取到了非异步消息
if (msg != null) {
//任务执行时间大于现在的时间
if (now < msg.when) {
//设置下一次轮询的超时时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;//指定为非阻塞任务
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
//设置消息的使用状态,即flags |= FLAG_IN_USE
msg.markInUse();
return msg; //成功地获取MessageQueue中的下一条即将要执行的消息
}
} else {
//表示消息队列中无消息,会一直等待下去
nextPollTimeoutMillis = -1;
}
......
//IdleHandler为发现线程何时阻塞的回调接口
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; //去除handler引用
boolean keep = false;
//queueIdle返回true会被空闲的处理器处理,false就会被移走
try {
keep = idler.queueIdle(); //idle时执行的方法
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler); //被移走
}
}
}
//重置idle handler个数为0,保证不会再次重复运行
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
next()方法中,做了异步Message消息的判断,特殊的是这个Message没有设置target,即msg.target为null。
4.4、MessageQueue之enqueueMessage()
以下源码注释参考:Handler用法及解析
boolean enqueueMessage(Message msg, long when) {
// 每一个普通Message必须有一个target-handler
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//已在使用状态
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//消息在退出状态->被回收到消息池
if (mQuitting) {
msg.recycle();
return false;
}
//标记使用状态,记录执行时间
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//p为null代表MessageQueue没有消息或者msg的触发时间是队列中最早的
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; //当阻塞时需要唤醒
} else {
//将消息按时间顺序插入到MessageQueue。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
队列中的Message触发时间是有先后顺序的。当消息加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序(内部遍历队列中Message,找到when比当前Message的when大的Message,将Message插入到该Message之前,如果没找到则将Message插入到队列最后)。一般是当前队列为空的情况下,next那边会进入睡眠,需要的时候MessageQueue这边会唤醒next方法。
4.5、MessageQueue之其他
MessageQueue除了让队列中存放/取出数据,还有移除消息数据removeMessages(),removeCallbacksAndMessages()以及退出消息队列quit()等...
五、Message消息
我们通过Handler发送数据的时候,我们把数据包装成一个个Message,那么这个Message有什么特点呢?
5.1、Message简单属性
//消息类别,相当于一个标记码
public int what;
//参数1
public int arg1;
//参数2
public int arg2;
//消息内容
public Object obj;
//消息触发时间
public long when;
在操作Message的时候,我们一般是给Message上述对象赋值我们对应数据。但是在实际开发中,我们不可能频繁去创建我们的Message对象,我们看源码得知,Message提供了一个消息池:
//从全局池返回一个新的Message实例。 在许多情况下,我们可以避免分配新对象。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
//消息池中如有,则取出一个Message
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
// 当消息池为空时,直接创建Message对象
return new Message();
}
六、Handler系列之总结
6.1、Handler运行图示意
如上述运行流程图:要使用Handler实现异步消息处理,首先我们需要在主线程中创建Handler对象并重写handleMessage()方法,然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handlerr将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后通过handler.dispatchMessage(msg)分发回Handler的handleMessage()方法中。由于Halldler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,从而实现子线程通过Handler机制实现UI线程操作的目的。
6.2、Handler系列总结
- Message,线程之间传递的消息,用于不同线程之间的数据交互。Message中的what字段用来标记区分多个消息,arg1、arg2 字段用来传递int类型的数据,obj可以传递任意类型的字段。
- MessageQueue,消息队列(先进先出),用于存放Handler发送的消息,一个线程只有一个消息队列。
- Handler,用于发送和处理消息。其中的sendMessage()用来发送消息,handleMessage()用于消息处理,进行相应的UI操作。
- Looper,作为消息队列的管理者,当发现MessageQueue中存在消息,Looper就会将消息传递到handleMessage()方法中,同样,一个线程只有一个Looper。
届时,已经浅析完了Handler相关系列“四大金刚”之Message,Handler,MessageQueue,Looper。鄙人水平有限,有什么不对的地方欢迎大佬们批评指正!