Handler 移除所有 postdelay 问题Android Handler.removeMessage移除所有postDelayed的问题
  HvTJUzsxOBtS 2023年11月22日 18 0


Android Handler.removeMessage暗坑

在Android开发中,Handler的使用应该是比较多的,可以用它在UI线程中进行操作,也可以很方便的使用delay延时动作。

Handler的延时操作分两种:

1.延时执行一个可运行任务

Handler.postDelayed(runnable, 10000)
2.延时发送一条Message消息

Handler.sendEmptyMessageDelayed(0, 10000)
今天开发中遇到一个诡异的问题,handler.removeMessage(0)把所有的延时执行可运行任务都移除掉了,按照这个api的注释来看,应该是移除指定msg.what的延时消息才对。为什么会把其他的可执行任务都移除掉了?

接下来我们来创建一个自定义Handler

class MyHandler extends Handler{
     @Override
     public void handleMessage(Message msg) {
         super.handleMessage(msg);
         Log.d(TAG, "handleMessage: "+msg.what);
     }
 }

然后再创建两个自定义Runnable

Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
        Log.d(TAG, "run: 1");
    }
};

Runnable runnable2 = new Runnable() {
    @Override
    public void run() {
        Log.d(TAG, "run: 2");
    }
};

然后开始执行handler的操作

MyHandler handler = new MyHandler();
handler.postDelayed(runnable1,2000);
handler.postDelayed(runnable2,2000);
handler.sendEmptyMessageDelayed(0,2000);
handler.sendEmptyMessageDelayed(1,2000);
handler.sendEmptyMessageDelayed(2,2000);
handler.removeMessages(0);

最后看一下打印出来的log

D/MainActivity: handleMessage: 1
D/MainActivity: handleMessage: 2

可以看到只有1和2两条延时发送的Message打印出来了,另外3个延时操作全部被取消掉,这里面果然有坑.

那只有看看源码到底是怎么回事了

/**
 * Remove any pending posts of messages with code 'what' that are in the
 * message queue.
 */
public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}

/**
 * Remove any pending posts of messages with code 'what' and whose obj is
 * 'object' that are in the message queue.  If <var>object</var> is null,
 * all messages will be removed.
 */
public final void removeMessages(int what, Object object) {
    mQueue.removeMessages(this, what, object);
}
 

看到源码中实际调用了api的mQueue.removeMessages,并且注释中解释了参数的作用,移除指定msg.what的消息,如果指定了object对象并且该object存在消息队列中则只移除该object。如果object是null,则移除消息队列中所有msg.what等于what参数的消息。

看这段api的注释,好像并没有什么问题,继续查找Handler.postDelayed的源码
 

/**
 * Causes the Runnable r to be added to the message queue, to be run
 * after the specified amount of time elapses.
 * The runnable will be run on the thread to which this handler
 * is attached.
 * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
 * Time spent in deep sleep will add an additional delay to execution.
 *  
 * @param r The Runnable that will be executed.
 * @param delayMillis The delay (in milliseconds) until the Runnable
 *        will be executed.
 *        
 * @return Returns true if the Runnable was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the Runnable will be processed --
 *         if the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 */
public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

/**
 * Enqueue a message into the message queue after all pending messages
 * before (current time + delayMillis). You will receive it in
 * {@link #handleMessage}, in the thread attached to this handler.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the message will be processed -- if
 *         the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 */
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
 

可以看到postDelayed内部调用了sendMessageDelayed方法!也就是说postDelayed的本质也是使Handler发送Message消息。而Message对象的创建则在getPostMessage(r)方法中。继续往下看.

private static Message getPostMessage(Runnable r) {

Message m = Message.obtain();

m.callback = r; return m;

}

public final class Message implements Parcelable {
    public int what;
    ...

    /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
    */
    public Message() {
    }
    ...

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    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();
    }

}
 

getPostMessage方法通过Message.obtain()获得了一个Message对象,并且把可运行任务绑定到message.callback中。通过上面Message源码注释可以了解,obtain()会从Message池中返回一个新的Message对象,再看Message的构造方法,空空如也。

所以问题找到了,一个新的Message对象,它的what变量默认为0!
这就解释了为什么handler.removeMessage(0)会把所有可执行任务都移除掉。

避开removeMessage暗坑方法:
1.自定义handler处理的msg.what消息不要使用默认值0
2.同一个handler不要同时使用postDelayed()和postMessageDelayed()两个方法

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月22日 0

暂无评论

推荐阅读
HvTJUzsxOBtS