Android handler机制流程详解
handler机制由以下几部分组成:
.Handler
.Message
.MessageQueue
.Looper
总体流程介绍:
一、进程启动时
在main方法中为主线程创建了对应自己线程的Looper,在Looper被创建的同时会创建一个MessageQueue消息队列并持有。同时在main方法中调用了Looper.loop()进行一个死循环遍历Looper自己持有的消息队列。
二、创建handler
如果在主线程中创建,handler能获取到主线程对应的looper并持有。如果是在子线程中创建handler,则需要自己调用创建looper方法和自己调用Looper.loop。否则会报错。
三、创建Message并发送
创建的Message将创建自己的handler信息封装进Message内部,最终被发送到了MessageQueue消息队列中。
四、接收消息
在Looper.loop中,从MessageQueue中拿出Message,并根据其中对应的handler信息进行分发处理
对应关系:
一条线程Thread对应唯一的一个Looper,其对应相关的信息存储在ThreadLocal中。
一个Looper对象持有一个自己的MessageQueue。
在同一条线程中,无论创建多少handler,都只对应一个Looper和一个MessageQueue。
因此,handler在被创建时就根据Looper与对应的线程进行了绑定,即使在别的线程根据该handler发送了消息,也会被发送到handler对应的Looper的MessageQueue消息队列中并被分发处理。
代码分析:
接下来,举一个简单的例子,并且简单分析一下主流程对应的原码:
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
}
}
};
new Thread() {
public void run() {
Message message = handler.obtainMessage();
message.what = 100;
message.obj = "测试+ : " + i;
handler.sendMessage(message);
}
}.start();
解析:
Handler:负责发送消息和接收返回的消息,主流程部分源码分析
public class Handler {
//主要变量
final Looper mLooper;//与每个线程对应的looper
final MessageQueue mQueue;//消息队列
final Callback mCallback;//是否同步
//构造方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
..
}
mLooper = Looper.myLooper();//根据当前线程信息在ThreadLocal中获取线程对应的looper
注:
.如果是在子线程中创建,需要调用Looper.prepare( boolean quitAllowed)
.该方法的作用是获取调用方法的线程信息,判断当前线程是否有looper,没有则创建。
.在ActivityThread中创建过主线程的looper
if (mLooper == null) {
..
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//从消息对象池中取出消息
public final Message obtainMessage()
{
return Message.obtain(this);
}
//发送消息的各种方法
handler.sendMessage(message);
handler.sendMessageDelayed(
Message msg, long delayMillis);
handler.sendMessageAtTime(
Message msg, long uptimeMillis);
handler.sendEmptyMessage(
int what);
handler.sendEmptyMessageDelayed(
int what, long delayMillis);
handler.sendMessageAtFrontOfQueue(
Message msg);
handler.sendEmptyMessageAtTime(
int what, long uptimeMillis);
//最终都是调用来发送
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//将当前handler信息封装进Message
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
//分发消息
/**
* 该方法在Looper中next()方法中被调用 根据msg.target来区分
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
}
主要发生的大事:
一、当调用:new Handler() 时:
1、Handler中:Looper.myLooper();
在ThreadLocal中获取当前线程对应的looper,主线程对应的looper在main方法中已经被创建
2、mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
取出looper中的消息队列(mQueue)。Callback对象。是否同步。封装进该handler对象
二、当调用:handler.obtainMessage()时:
在消息对象池中获取消息。
三、当调用:sendMessage或者各种send方法时:
最终调用 enqueueMessage,将当前handler对象封装进message.target,
并调用mQueue.enqueueMessage(msg, uptimeMillis),将消息发送给消息队列
Message:负责封装消息,主流程部分源码分析
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
long when;
Bundle data;
Handler target;
Runnable callback;
Message next;
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
//被handler调用
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
/**
* 判断消息对象池中是否有消息,并从消息对象池中获取消息
*/
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();
}
省略其他好多方法...
结论:在handler中使用obtain(this)来获取消息池中消息对象时,将当前this封装进message.target中
Looper:不断循环,从消息队列中获取消息,并根据消息的target来进行消息分发,即调用对应handler的dispatchMessage方法。主流程部分源码分析
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // 主线程looper对象
final MessageQueue mQueue; //消息队列
final Thread mThread;//当前线程
//构造方法:只有一个构造,并且创建mQueue 和给mThread 赋值
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//初始化,如果handler在子线程中被调用,则要先调用此方法
public static void prepare() {
prepare(true);
}
//被上面方法调用
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
//该方法内部根据当前线程信息判断是否创建了looper对象,如果有创建,则返回对应looper对象。
//如果没有创建,则返回空
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建一个looper对象,并将当前线程和该looper绑定
sThreadLocal.set(new Looper(quitAllowed));
}
//主线程在创建的时候在ActivityThread类中main方法中被调用
创建主线程对应的looper和给当前sMainLooper赋值
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//handler中获取looper的方法
public static @Nullable
Looper myLooper() {
//该方法内部根据当前线程信息判断是否创建了looper对象,如果有创建,则返回对应looper对象。
//如果没有创建,则返回空
return sThreadLocal.get();
}
//开启loop循环查询队列,
主线程对应的loop方法在main中被调用,如果在子线程中创建的handler,需要自己调用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;
省略众多代码....
for (; ; ) {
//队列的next()方法中处理基本逻辑,如果有消息:返回。如果没有消息:阻塞||等待
Message msg = queue.next();
if (msg == null) {
return;
}
省略众多代码.....
try {
msg.target.dispatchMessage(msg);//根据targer(即handler)进行分发
..
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//将消息放回消息池
msg.recycleUnchecked();
}
}
}
该类负责做的事情:
1、在prepare()被调用时:
该方法可以定义为:创建调用线程对应的looper,判断被调用的线程是否有looper对象,如果有就提示错误,没有则进行创建并绑定线程和looper。
2、构造被调用时:
创建 MessageQueue 消息队列,和给mThread变量(当前线程)赋值
3、在myLooper()被调用时:
该方法可以定义为获取线程对应的looper,判断被调用的线程是否有looper对象,如果有就返回,没有返回空。
4、prepareMainLooper()方法在main方法中被调用,创建主线程对应的looper。
5、loop()方法在main中被调用,作用为死循环从当前线程对应looper持有的消息队列中取出消息,并根据消息对应的targer进行分发。
注:当源码解析到这里有一个疑问:为什么在主线程里面Looper的死循环不会引发ANR
解释:Looper.loop无限循环为什么没有引发ANR
MessageQueue:消息队列,接收handler发送过来的消息存入队列,死循环遍历消息队列返回给looper,消息 队列没有消息时,阻塞||等待。主流程部分源码分析
public final class MessageQueue {
private final boolean mQuitAllowed;
是否允许退出
private boolean mQuitting;//是否放弃消息队列
@SuppressWarnings("unused")
private long mPtr;
private native static long nativeInit();//返回一个NativeMessageQueue,具体实现过程在Nativity层
private native static void nativeWake(long ptr);//唤醒对应线程
//构造
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;//是否允许退出
mPtr = nativeInit();//返回一个NativeMessageQueue,具体实现过程在Nativity层
}
//该方法就是在handler中调用send系列方法最终调用的方法,将消息放入消息队列
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//msg.target就是发送此消息的Handler
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) {//表示此消息队列被放弃
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;//将延迟时间封装到msg内部
Message p = mMessages;//消息队列的第一个元素
boolean needWake;//是否需要唤醒线程
if (p == null || when == 0 || when < p.when) {
//如果此队列中头部元素是null(空的队列,一般是第一次),
或者此消息不是延时的消息,则此消息需要被立即处理,
此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,
然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理。
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//如果此消息是延时的消息,则将其添加到队列中,
原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,
延迟的时间越长,越靠后,这样就得到一条有序的延时消息链表,
取出消息的时候,延迟时间越小的,就被先获取了。插入延时消息不需要唤醒Looper线程
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; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);//唤醒线程
}
}
return true;
}
//该方法就是在Looper中被调用的方法,返回一个message,如果队列为空,则阻塞
Message next() {
final long ptr = mPtr;
//从注释可以看出,只有looper被放弃的时候(调用了quit方法)才返回null,
mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,
阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (; ; ) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
//如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
//如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用
//nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//正常取出消息
//设置mBlocked = false代表目前没有阻塞
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
//没有消息,会一直阻塞,直到被唤醒
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
}
主要做的事:
MessageQueue() 构造:在Looper创建时被创建
enqueueMessage():handler一系列send方法最终殊途同归到这个方法,将消息放入消息队列
next():在Looper.loop() 方法中被调用,遍历队列并返回消息,如果队列中没有消息则阻塞
来源:CSDN
作者:轻狂书生YT
链接:https://blog.csdn.net/qq_41466437/article/details/104628280