Android Handler知识点大汇总
主要成员
-
Message(消息):需要被传递的消息,消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息。
-
MessageQueue(消息队列):负责消息的存储与管理,负责管理由 Handler发送过来的Message。读取后会自动删除消息,单链表维护,插入和删除上有优势。在其next()方法中会无限循环,不断判断是否有消息,有就返回这条消息并移除。
-
Handler(消息处理器):负责Message的发送及处理。主要向消息池发送各种消息事件(Handler.sendMessage())和处理相应消息事件(Handler.handleMessage()),按照先进先出执行,内部使用的是单链表的结构。
-
Looper(消息池/循环机制):负责关联线程以及消息的分发,在该线程下从 MessageQueue获取 Message,分发给Handler,Looper创建的时候会创建一个 MessageQueue,调用loop()方法的时候消息循环开始,其中会不断调用messageQueue的next()方法,当有消息就处理,否则阻塞在messageQueue的next()方法中。当Looper的quit()被调用的时候会调用messageQueue的quit(),此时next()会返回null,然后loop()方法也就跟着退出。
具体流程图:
整个流程可以总结为,把需要发送的内容包装为一个Messager,然后再通过Handler把消息添加到MessageQueue,Looper死循环从MessageQueue中取消息,然后再把消息交给对应的Handler去处理。
一、Message(消息)
Message.class位于android.os.包中。Message的构造函数为无参构造方法,且只有一个构造方法;
除了构造方法可以创建实例对象外,还可以通过内部的静态方法来创建:
public Message() { }
static Message obtain()
static Message obtain(Message orig)
static Message obtain(Handler h)
static Message obtain(Handler h, Runnable callback)
static Message obtain(Handler h, int what)
static Message obtain(Handler h, int what, Object obj)
static Message obtain(Handler h, int what, int arg1, int arg2)
static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)
以上几个静态的方法里面都会首先调用第一个方法来创建一个Message对象,我们来看看源码(API 28)
public static final Object sPoolSync = new Object(); //同步锁对象
private static Message sPool; //全局池消息实例
/**
* 从全局池返回一个新的消息实例,允许我们在许多情况下避免分配新对象。
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
//......
return m;
}
}
return new Message();
}
如果当前全局池的Message实例不为空,则返回第一个消息实例。所以,大多数情况下,使用obtain()来获得一个Message对象,可以避免消耗更多的内存资源。
对于其他 static obtain( ) 的重载方法,通过源码,可以发现,都是进行赋值操作,没有太多的可讨性。唯一得注意一下的是 obtain(Handler h, Runnable callback)这个静态方法:
/*package*/ Handler target;
/*package*/ Runnable callback;
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
可以看到,也是赋值操作,target是保护级别的成员变量,即只有同包名空间可以访问,此变量意义重大。除了target,还有一个callback,这个callback也是配合着Handler来发挥作用的。后面讲到Handler会解释到,请稍安勿躁。
此时,大家先要记住的就几点:
Message有8个静态方法可以创建Message实例
Message有两个重要的成员变量,分别为target 和callback,一个是Handler,一个是Runnable。
Message有4个公开变量what、arg1、arg2、obj 可以存储消息进行传递
Message还有一个包间成员变量next,它是Message类型,后面会使用到,知道有这个next就行
以上就是Message的基本秘密了,很简单,没有什么复杂的东西(作为一个包裹箱,就是这么简单,能装一些东西,然后附带一些关键信息)。
二、Handler(处理机)
Handler.class也位于android.os包中。Handler英文意思为:处理者,管理者,处理机。它在消息传递过程中扮演着重要的角色,是消息的主要处理者,说白了,就是收消息,最终处理消息
1、Handler的构造方法(API 28)
Handler()
Handler(Callback callback)
Handler(boolean async)
Handler(Callback callback, boolean async)
Handler(Looper looper)
Handler(Looper looper, Callback callback)
Handler(Looper looper, Callback callback, boolean async)
通过源码可以发现,上面的构造方法都是上面一个个往下调用的,第一个调用第二个,第二个调用第三个…所以,我们首先把目光放在最后一个方法上:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
这是一个赋值的构造方法。再看另外一个构造方法:
整个构造方法的过程中会确立以下几件事:
获取当前Handler实例所在线程的Looper对象:mLooper = Looper.myLooper()
如果Looper不为空,则获取Looper的消息队列,赋值给Handler的成员变量mQueue:mQueue = mLooper.mQueue
可以设置Callback 来处理消息回调:mCallback = callback
Handler是消息的处理者,但是它并不是最终处理消息的那个大佬,它有且只能有一个上级个领导,就是Looper,Handler是将消息上报给Looper(领导),然后排队等待,等Looper(领导)处理完消息了,就会通知Handler去领取消息,给Handler分配任务,Handler拿到消息后在自行往下分发,Handler只能听命与Looper(领导)。
举个实际运用中的情景:
当你需要在子线程中更新主线程的UI时,你就会在当前的Activity下创建一个Handler对象,然后在它的handleMessage() 中更新UI。
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//... 更新UI
}
};
在你创建这个mHandler 实例的时候,底层做了以下几件事情:
1、拿到mHandler所在线程的Looper,当前mHandler是在Activity中创建的,很明显,当前的线程就是主线程,所以 mHandler的成员变量mLooper = Looper.myLooper(),此处就已经将当前的主线程Looper赋值过去了。
2、紧接着,判断mLooper 是否为空,明显不为空,所以又会将主线程的消息队列赋值给mQueue。告诉Handler,你要是有消息,就送到这个消息队列中来,我(Looper)会一个个按顺序处理,处理完后我就会告诉你,你再处理。
由此我们可以得出结论:
1、Handler有且只能绑定一个线程的Looper;
2、Handler的消息是发送给Looper的消息队列MessageQueue,需要等待处理;
所以,如果你在子线程中声明了一个Handler,是不能直接更新UI的,需要调用Handler相关的构造方法,传入主线程的Looper,这样创建的Handler实例,你才能进行UI的更新操作。另外的,需要注意的是,子线程默认是没有开启专属的Looper,所以,在子线程中创建Handler之前,你必须先开启子线程的Looper,否则就会爆出异常,然后GG。从上面贴出的构造方法中的部分就可以知道:
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
以上就是创建Handler的过程,有了Handler实例了,怎样传递消息呢?
2、Handler sendMessage()相关的方法(API 28)
首先上一幅图来表明sendXXXMessageXXX()的相互调用关系:
可以看出,当我们调用Handler进行发送消息时,最终都会调用sendMessageAtTime()方法,最后调用enqueueMessage( ) 发送到消息队列。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue; //获得当前的消息队列
if (queue == null) { //若是在创建Handler时没有指定Looper,就不会有对应的消息队列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; //这个target就是前面我们说到过的
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg.target = this
在发送消息到消息队列之前,明确的指定了消息的target为当前的Handler,以便于在后面Looper分发消息时用到。
queue.enqueueMessage(msg, uptimeMillis)
然后调用了消息队列的enqueueMessage()方法,并传递了两个参数,一个Message,一个是long型的时间。
以上就是Handler的创建和发送消息的过程。
3、Handler dispatchMessage()方法(API 28)
前面说了消息的发送,交给Looper等待处理,处理完后会重新通知Handler处理,那么,是怎样通知Handler处理消息的呢?秘密就在dispatchMessage()这个方法中:
/**
* 在这里处理系统消息
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
/**
* 子类必须实现这个来接收消息
*/
public void handleMessage(Message msg) {
}
当Looper处理完Message后,会使用到Message的target,即上面说到的target,即发送消息的那个Handler,Looper会调用Handler的dispatchMessage()方法分发消息,所以前面在enqueueMessage()发送消息的时候,为什么非得指明Message的target就是这个道理。
回到dispatchMessage()这个方法:
1、首先会判断Message的callback是否为空,此处的callback就是前面我们在Message中说到的,在静态方法创建Message时,可以指定的callback,若不为空,则将结果回调到callback中;
2、若Handler的mCallback 不为空,也一样的道理。
3、平时我们都没有传入这个callback,而是直接实现handleMessage()这个方法,在这个方法中处理更新UI任务。
以上就是Handler发送和接收消息的基本过程:把消息发送到队列—>然后喝茶等待—>接收消息—>分发消息—>在回调中处理。
三、MessageQueue
前面我们知道,Handler发送消息会调用MessageQueue的enqueueMessage()方法,直接上源码(API 28):
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) { //判断msg的所属Handler
throw new IllegalArgumentException("Message must have a target.");
}
//......
synchronized (this) { //因为是队列,有先后之分,所以用了同步机制
//......
msg.when = when;
Message p = mMessages; //对列中排在最后的那个Message
//......
if (p == null || when == 0 || when < p.when) {
//若队列为空,或者等待时间为0,或者比前面那位的等待时间要短,就插队
msg.next = p; //此处的next就是前面我们在Message提到的,指向队列的下一个结点
mMessages = msg;
//......
} else {
//......
Message prev;
for (;;) {
//此处for循环是为了取出一个空的或者when比当前Message长的一个消息,然后进行插入
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
//......
}
msg.next = p; // 置换插入
prev.next = msg; // 置换插入
}
//......
}
return true;
}
以上就是消息队列插入消息的过程原理,通过单向链表的数据结构来存储消息。既然有了插入消息的方法供Handler插入消息,那么应该有对应的取出消息的方法,供Looper调用取出消息处理,它就是Message.next这个变量(结点),代码就不贴了,自行前往查看,过程还是挺简单的。
四、Looper
Looper在Handler机制中扮演着关键的一环,他是循环处理消息的发动机,永不停息(永动鸡),它不断的从消息队列中取出的消息,处理,然后分发处理事件。每个线程都可以且只能绑定一个Looper。主线程之所以能处理消息,也是因为在APP启动时,在ActivityThread中的main()方法中就已经启动了Looper循环。
点击前往查看Looper在main()方法中的启动流程
下面直接上Looper关键方法loop( )的源码(API 28)
public static void loop() {
final Looper me = myLooper(); //获得当前的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //获取当前Looper的消息队列
//......
for (;;) {
Message msg = queue.next(); //取出队头的消息
if (msg == null) {
// 如果消息为空,则跳过,继续执行下一个message
return;
}
//......
try {
msg.target.dispatchMessage(msg);
//......
} finally {
//......
}
//......
msg.recycleUnchecked(); //回收可能正在使用的消息
}
}
由此可见,Looper的处理消息的循环还是挺简单的,就是拿出消息,然后分发,然后回收 … …
总结
Handler机制可以简述为:
Handler将Message发送到Looper的消息队列中,即MessageQueue,等待Looper的循环读取Message,处理Message,然后调用Message的target,即附属的Handler的dispatchMessage()方法,将该消息回调到handleMessage()方法中,然后完成更新UI操作。
同步屏障
一、消息种类
关于Handler机制的基本原理不了解可以看这里: Handler机制源码解析。
Message分为3种:普通消息(同步消息)、屏障消息(同步屏障)和异步消息。我们通常使用的都是普通消息,而屏障消息就是在消息队列中插入一个屏障,在屏障之后的所有普通消息都会被挡着,不能被处理。不过异步消息却例外,屏障不会挡住异步消息,因此可以这样认为:屏障消息就是为了确保异步消息的优先级,设置了屏障后,只能处理其后的异步消息,同步消息会被挡住,除非撤销屏障。
二、什么是屏障消息
同步屏障是通过MessageQueue的postSyncBarrier方法插入到消息队列的。
MessageQueue#postSyncBarrier
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
//1、屏障消息和普通消息的区别是屏障消息没有tartget。
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
//2、根据时间顺序将屏障插入到消息链表中适当的位置
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
//3、返回一个序号,通过这个序号可以撤销屏障
return token;
}
}
postSyncBarrier方法就是用来插入一个屏障到消息队列的,可以看到它很简单,从这个方法我们可以知道如下:
屏障消息和普通消息的区别在于屏障没有tartget,普通消息有target是因为它需要将消息分发给对应的target,而屏障不需要被分发,它就是用来挡住普通消息来保证异步消息优先处理的。
屏障和普通消息一样可以根据时间来插入到消息队列中的适当位置,并且只会挡住它后面的同步消息的分发。
postSyncBarrier返回一个int类型的数值,通过这个数值可以撤销屏障。
插入普通消息会唤醒消息队列,但是插入屏障不会。
三、屏障消息的工作原理
通过postSyncBarrier方法屏障就被插入到消息队列中了,那么屏障是如何挡住普通消息只允许异步消息通过的呢?我们知道MessageQueue是通过next方法来获取消息的。
Message next() {
//1、如果有消息被插入到消息队列或者超时时间到,就被唤醒,否则阻塞在这。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {//2、遇到屏障 msg.target == null
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());//3、遍历消息链表找到最近的一条异步消息
}
if (msg != null) {
//4、如果找到异步消息
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;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 如果没有异步消息就一直休眠,等待被唤醒。
nextPollTimeoutMillis = -1;
}
//。。。。
}
}
可以看到,在注释2如果碰到屏障就遍历整个消息链表找到最近的一条异步消息,在遍历的过程中只有异步消息才会被处理执行到 if (msg != null){}中的代码。可以看到通过这种方式就挡住了所有的普通消息。
四、如何发送异步消息
Handler有几个构造方法,可以传入async标志为true,这样构造的Handler发送的消息就是异步消息。不过可以看到,这些构造函数都是hide的。
/**
* @hide
*/
public Handler(boolean async) {}
/**
* @hide
*/
public Handler(Callback callback, boolean async) { }
/**
* @hide
*/
public Handler(Looper looper, Callback callback, boolean async) {}
当调用handler.sendMessage(msg)发送消息,最终会走到:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);//把消息设置为异步消息
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到如果这个handler的mAsynchronous为true就把消息设置为异步消息,设置异步消息其实也就是设置msg内部的一个标志。而这个mAsynchronous就是构造handler时传入的async。除此之外,还有一个公开的方法:
Message message=Message.obtain();
message.setAsynchronous(true);
handler.sendMessage(message);
在发送消息时通过 message.setAsynchronous(true)将消息设为异步的,这个方法是公开的,我们可以正常使用。
五、移除屏障
移除屏障可以通过MessageQueue的removeSyncBarrier方法:
//注释已经写的很清楚了,就是通过插入同步屏障时返回的token 来移除屏障
/**
* Removes a synchronization barrier.
*
* @param token The synchronization barrier token that was returned by
* {@link #postSyncBarrier}.
*
* @throws IllegalStateException if the barrier was not found.
*
* @hide
*/
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
//找到token对应的屏障
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
final boolean needWake;
//从消息链表中移除
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
//回收这个Message到对象池中。
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);//唤醒消息队列
}
}
原文链接:https://blog.youkuaiyun.com/start_mao/article/details/98963744
ThreadLocal的原理
Looper的构造函数是私有的,外界不能直接new对象,创建Looper对象的唯一方式就是Looper中的prepare方法。
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");
}
sThreadLocal.set(new Looper(quitAllowed));
}
获取Looper通过myLooper()方法。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
每个线程只能有一个Looper,那Looper类怎么存储不同线程的Looper呢,获取的时候又是怎么获取到当前线程的Looper的呢?这就需要用到ThreadLocal类了,上文中的sThreadLocal就是专门存储不同线程Looper的。
ThreadLocal是什么?
ThreadLocal 是一个线程内部的数据存储类,通过它可以在 指定的线程中 存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用 ThreadLocal。
如何使用?
参考:https://www.jianshu.com/p/5dbddcd4fd9b
Looper是如何做到阻塞的
我们先看Looper中的loop()方法(此处省略大量无关内容)。
public static void loop() {
final Looper me = myLooper();
me.mInLoop = true;
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
}
}
}
loop()方法中有一个死循环,用于从MessageQueue中取消息,在分发给对应的handle进行处理,第8行中通过next()方法从消息队列中取消息,这个方法是一个阻塞的方法,我们到这个方法里面看一看。
Message next() {
for (;;) {
//如果有消息被插入到消息队列或者超时时间到,就被唤醒,否则阻塞在这。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//下面的代码就是从消息头取消息,取到消息就返回,取不到就阻塞
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
// 在处理完所有挂起的消息后处理退出消息。
if (mQuitting) {
dispose();
return null;
}
}
}
第4行代码nativePollOnce是一个本地方法,该方法实现阻塞,有消息就会取出来交给Looper进行分发,没有就阻塞在这里,那么问题来了,这个阻塞该怎么唤醒呢?从上面的代码可以看出要想退出loop的消息循环next方法就必须返回空,那怎样让next返回null呢?
MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
上面的代码是向消息队列中添加消息的方法,我们直接看第52行,nativeWake(mPtr)该方法是一个本地方法用于唤醒阻塞,唤醒之后next方法又可以继续从消息队列中取消息,交由Looper的loop方法进行分发。
Looper.java
public void quit() {
mQueue.quit(false);
}
MessageQueue.java
void quit(boolean safe) {
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
第8行中把mQuitting属性置为true,从而让上文next方法39行的判断成立,从而使得next方法返回null,从而使loop退出循环,要想主动结束消息循环就调用Looper的 quit方法就可以了(当消息队列中的消息都处理完成之后才会退出消息循环)。
ldlehandle是什么怎么使用
参考:https://blog.youkuaiyun.com/jdsjlzx/article/details/110532500
疑问?
- Looper的loop方法是个死循环为什么不会卡死主线程?
App中处处都会产生消息,例如点击屏幕,屏幕每隔16ms刷新,等等都需要用到消息,所以主线程的消息是无穷无尽的,始终有消息处理。
主线程的主要方法就是消息循环,一旦退出消息循环,那么你的应用也就退出了,Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。
造成ANR的不是主线程阻塞,而是主线程的Looper消息处理过程发生了任务阻塞,无法响应手势操作,不能及时刷新UI。
阻塞与程序无响应没有必然关系,虽然主线程在没有消息可处理的时候是阻塞的,但是只要保证有消息的时候能够立刻处理,程序是不会无响应的。
- 为什么主线程要使用loop阻塞线程?
主线程结束了,那程序就结束了,试想一下你的app在执行完主线程代码就闪退了,你能忍?所以需要loop开启消息循环让主线程一直运行下去。
- 子线程Looper的loop会卡死子线程吗?
子线程不同于主线程,子线程的loop会卡死子线程,有消息就处理没有就阻塞。
- 使用Handler的postDealy后消息队列会有什么变化?
如果队列中只有这个消息,那么消息不会被发送,而是计算到时唤醒的时间,先将Looper阻塞,到时间就唤醒它。但如果此时要加入新消息,该消息队列的对头跟delay时间相比更长,则插入到头部,按照触发时间进行排序,队头的时间最小、队尾的时间最大