Android Handler机制7--消息移除、关闭及其他

移步Android Handler机制详解

1 消息(Message)的移除

1.1 Handler的消息移除

  • 消息(Message)的移除,其实就是根据身份what、消息Runnable或msg.obj移除队列中对应的消息。
  • 例如发送msg,用同一个msg.what作为参数。所有方法最终调用MessageQueue.removeMessages,来进行时机操作的。

代码如下,因为不复杂,我就合并在一起了

// Handler.java
public final void removeCallbacks(Runnable r) {
    mQueue.removeMessages(this, r, null);
}

public final void removeCallbacks(Runnable r, Object token) {
    mQueue.removeMessages(this, r, token);
}

public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}

public final void removeMessages(int what, Object object) {
    mQueue.removeMessages(this, what, object);
}

public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

所以我们知道,Handler里面的删除工作,其实本地都是调用MessageQueue来操作的。

1.2 MessageQueue的消息移除

MessageQueue的消息移除在其类类的方法如下


Android Handler机制7--消息移除、关闭及其他_第1张图片
5713484-834f223bde4df3bd.png

一共有5个方法如下:

  • 移除方法1:void removeMessages(Handler , int , Object )
  • 移除方法2:void removeMessages(Handler, Runnable,Object)
  • 移除方法3:void removeCallbacksAndMessages(Handler, Object)
  • 移除方法4:void removeAllMessagesLocked()
  • 移除方法5:void removeAllFutureMessagesLocked()

1.2.1 移除方法1:void removeMessages(Handler , int , Object )方法

从消息队列中删除所有符合指定条件的Message
代码在MessageQueue.java 587行

void removeMessages(Handler h, int what, Object object) {
        // 第1步
        if (h == null) {
            return;
        }
         // 第2步
        synchronized (this) {
           // 第3步
            Message p = mMessages;

            // Remove all messages at front.

          
            //第4步
            while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            // Remove all messages after front.
            //第5步
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.what == what
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

分为5个步骤如下:

  • 第1步*、,对传递进来的Handler做非空判断,如果传递进来的Handler为空,则直接返回
  • 第2步、,加同步锁
  • 第3步、,获取消息队列链表的头元素
  • 第4步、,如果从消息队列的头部就有符合删除条件的Message,就从头开始遍历删除所有符合条件的Message,并不端更新mMessages指向的Message。
  • 第5步、,因为有了第4步、,前面的的情况不会发生,也就是我们不需要关心指向的问题,现在处理的问题就是删除剩下的符合删除条件的Message。

总结一下:

  • 从消息队列中删除Message的操作也是遍历消息队列然后删除所有符合条件的Message
  • 第一次是先判断符合删除条件的Message是不是从消息队列的头部就开始有了,这时候会设计修改mMessage指向的问题,而mMessage代表的就是整个消息队列
  • 在排除了第一种情况之后,剩下的就是继续遍历队列删除剩余的符合删除条件的Message。
  • 其他重载方法也是同样的操作,唯一条件就是条件不同而已,

1.2.2 void removeMessages(Handler, Runnable,Object)方法

从消息队列中删除所有符合指定条件的Message
代码在MessageQueue.java 604行

void removeMessages(Handler h, Runnable r, Object object) {
        if (h == null || r == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h && p.callback == r
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.callback == r
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

1.2.3 void removeMessages(Handler, Runnable,Object)方法

代码在MessageQueue.java 689行

void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

里面代码和移除方法1:void removeMessages(Handler , int , Object )基本一致,唯一不同就是筛选条件不同而已。

1.2.4 void removeAllMessagesLocked()方法

删除所有的消息
代码在MessageQueue.java 722行

private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }

1.2.5 void removeAllFutureMessagesLocked()

代码在MessageQueue.java 732行

private void removeAllFutureMessagesLocked() {
         // 第1步
        final long now = SystemClock.uptimeMillis();
         // 第2步
        Message p = mMessages;
        if (p != null) {
            // 第3步
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                // 第4步
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
               // 第5步
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }
  • 第1步:获取当前时间(其实从手机开机到现在的时间)
  • 第2步:获取消息队列链表的的头元素
  • 第3步:如果头元素的执行的时间就大于当前时间,因为我们知道链表的排序其实有从当前到未来的顺序排列的,所以但如果头元素大于当前时间,意味着这个链表的所有元素的执行时间都大于当前,则删除链表中的全部元素。
  • 第4步:如果消息队列中的头元素小于或等于当前时间,则说明要从消息队列中截取,从中间的某个未知的位置截取到消息队列链表的队尾。这个时候就需要找到这个具体的位置,这个步骤主要就是做这个事情。通过对比时间,找到合适的位置
  • 第5步:找到合适的位置后,就开始删除这个位置到消息队列队尾的所有元素

2 关闭消息队列

  • 通过前面的文章,我们知道Handler消息机制的停止,本质上是停止Looper的循环,在Android Handler机制2--Looper文章中我们知道Looper的停止实际上是关闭消息队列的关闭,现在我们来揭示MessageQueue是如何关闭的
  • 代码在MessageQueue.java 413行
void quit(boolean safe) {
         // 第1步
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        // 第2步
        synchronized (this) {
            // 第3步
            if (mQuitting) {
                return;
            }
            mQuitting = true;
            // 第4步
            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            // 第5步
            nativeWake(mPtr);
        }
    }
  • 第1步:判断是否允许退出,因为在构造MessageQueue对象的时候传入了一个boolean参数,来表示该MessageQueue是否允许退出。而这个boolean参数在Looper里面设置,Loooper.prepare()方法里面是true,在Looper.prepareMainLooper()是false,由此可见我们知道:主线程的MessageQueue是不能退出。其他工作线程的MessageQueue是可以退出的。
  • 第2步:加上同步锁
  • 第3步:主要防止重复退出,加入一个mQuitting变量表示是否退出
  • 第4步:如果该方法的变量safe为true,则删除以当前时间为分界线,删除未来的所有消息,如果该方法的变量safe为false,则删除当前消息队列的所有消息。
  • 第5步:删除小时后nativeWake函数,以触发nativePollOnce函数,结束等待,这个块内容请在Android Handler机制9之Native的实现
    中,这里就不详细描述了

3 查看消息是否存在

Handler机制也存在查找是否存在某条消息的机制,代码如下:

// Handler.java
public final boolean hasMessages(int what) {
    return mQueue.hasMessages(this, what, null);
}

public final boolean hasMessages(int what, Object object) {
    return mQueue.hasMessages(this, what, object);
}

public final boolean hasCallbacks(Runnable r) {
    return mQueue.hasMessages(this, r, null);
}

3.1 boolean hasMessages(Handler h, int what, Object object) 方法

代码在MessageQueue.java 587行

boolean hasMessages(Handler h, int what, Object object) {
        //第1步
        if (h == null) {
            return false;
        }
        //第2步
        synchronized (this) {
            //第3步
            Message p = mMessages;
            //第4步
            while (p != null) {
                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
                    return true;
                }
                p = p.next;
            }
            return false;
        }
    }

该方法的主要内容可以分为4个步骤

  • 第1步:判断传入进来的Handler是否为空,如果传入的Handler为空,直接返回false,表示没有找到
  • 第2步:加上同步锁
  • 第3步:取出消息队列链表中的头部元素
  • 第4步:遍历消息队里链表中的所有元素,如果有元素消息符合指定条件则return false,如果遍历完毕还没有则返回false

4 阻塞非安全执行

  • 如果当前执行线程是Handler的线程,Runnable会被立刻执行。
  • 否则把它放在消息队列中一直等待执行完毕或者超时,超时后这个任务还在队列中,在后面的某个时刻它仍然会执行,很有可能造成死锁,所以尽量不要用它。
    代码在[http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/Handler.java) 461行
/**
     * Runs the specified task synchronously.
     * 

* If the current thread is the same as the handler thread, then the runnable * runs immediately without being enqueued. Otherwise, posts the runnable * to the handler and waits for it to complete before returning. *

* This method is dangerous! Improper use can result in deadlocks. * Never call this method while any locks are held or use it in a * possibly re-entrant manner. *

* This method is occasionally useful in situations where a background thread * must synchronously await completion of a task that must run on the * handler's thread. However, this problem is often a symptom of bad design. * Consider improving the design (if possible) before resorting to this method. *

* One example of where you might want to use this method is when you just * set up a Handler thread and need to perform some initialization steps on * it before continuing execution. *

* If timeout occurs then this method returns false but the runnable * will remain posted on the handler and may already be in progress or * complete at a later time. *

* When using this method, be sure to use {@link Looper#quitSafely} when * quitting the looper. Otherwise {@link #runWithScissors} may hang indefinitely. * (TODO: We should fix this by making MessageQueue aware of blocking runnables.) *

* * @param r The Runnable that will be executed synchronously. * @param timeout The timeout in milliseconds, or 0 to wait indefinitely. * * @return Returns true if the Runnable was successfully executed. * Returns false on failure, usually because the * looper processing the message queue is exiting. * * @hide This method is prone to abuse and should probably not be in the API. * If we ever do make it part of the API, we might want to rename it to something * less funny like runUnsafe(). */ public final boolean runWithScissors(final Runnable r, long timeout) { // 第1步 if (r == null) { throw new IllegalArgumentException("runnable must not be null"); } // 第2步 if (timeout < 0) { throw new IllegalArgumentException("timeout must be non-negative"); } // 第3步 // 如果为同一个线程,则直接执行runnable,而不需要加入到消息队列。 if (Looper.myLooper() == mLooper) { r.run(); return true; } // 第4步 BlockingRunnable br = new BlockingRunnable(r); return br.postAndWait(this, timeout); }

注释

  • 同步运行指定的任务。
  • 如果当前线程就是Handler的处理线程,则可以不用排队,直接运行这个runnable。否则如果当前线程和Handler的处理编程不是同一个线程则需要发送这个runnable到Handler线程,并且等待它完成后再返回。
  • 使用这个方法是有风险的,使用不当可能会导致死锁。不要在有锁或者可能有锁的代码区域调用这个方法。
  • 这个方法的使用场景通常是,一个后台线程必须等待Handler线程中的一个任务的完成。但是,这往往是不优雅设计才会出现的问题。所以在使用这个方法的时候,请首先考虑改进设计方案。
  • 这个方法的使用场景是:在你建立Handler线程之前,你需要执行一些初始化操作。
    如果发生超时,虽然该方法还是会返回false,但是该
  • 如果超时发生,那么该方法返回 false ,但是runnable仍是会保留在Handler中,并且在一段时间以后会在被执行。
    在使用这个方法的时候,并且要退出一个Looper的时候,请一定要调用quitSafely()这个方法。否则runWithScissors()这个方法可能会无限期挂起。(TODO:我们应该通知MessageQueue去阻止runnable来解决这个问题)

该方法内部的执行流程主要分为4个步骤,如下:

  • 第1步、:Runnable非空判断
  • 第2步、:timeout是否小于0判断
  • 第3步、:如果Looper的线程和Handler的线程是同一个线程
  • **第4步、,构造一个BlockingRunnable对象,并调用该对象的postAndWait(Handler,long)方法

Handler的静态内部类BlockingRunnable
BlockingRunnable是Handler的一个私有内部静态类,利用Object的wait和notifyAll方法实现。

代码在[http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/Handler.java)

private static final class BlockingRunnable implements Runnable {
        private final Runnable mTask;
        private boolean mDone;

        public BlockingRunnable(Runnable task) {
            mTask = task;
        }

        @Override
        public void run() {
            try {
                mTask.run();
            } finally {
                synchronized (this) {
                    mDone = true;
                     // runnable 执行完之后,会通知wait的线程不再wait
                    notifyAll();
                }
            }
        }

        public boolean postAndWait(Handler handler, long timeout) {
            if (!handler.post(this)) {
                return false;
            }

            synchronized (this) {
                if (timeout > 0) {
                    final long expirationTime = SystemClock.uptimeMillis() + timeout;
                    while (!mDone) {
                        long delay = expirationTime - SystemClock.uptimeMillis();
                        if (delay <= 0) {
                            return false; // timeout
                        }
                       // post runnable 之后,将调用线程变为wait状态
                        try {
                            wait(delay);
                        } catch (InterruptedException ex) {
                        }
                    }
                } else {
                    while (!mDone) {
                         // post runnable 之后,将调用线程变为wait状态
                        try {
                            wait();
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            }
            return true;
        }
    }

通过分析源码我们获取的了如下信息:

  • 1、该类实现了Runnable接口
  • 2、构造函数:接受一个Runnable作为参数的构造函数,包含了真正要执行的Task。
  • 3、run函数很简单,直接调用mTask.run(),一个finally内会同步对象本身(因为mDone涉及到多线程,而notifyAll()则需要synchronized配合)
  • 4、postAndWait(Handler, long):首先尝试将BlockingRunnable自己post到handler上,如果post失败,则直接返回false;其次如果上一步的post成功,就需要同步对象本身(为了使用wait());此时,如果timeout>0,那么就一个while循环+wait(long),中间有任何的interrupt都直接catch重新结算wait的时间,只有在任务完成(mDone=true,另外线程的run函数会设置此值)或者任何超时才会返回(true/false);如果imeout <=0,也就无限等待了

参考

Android Handler机制8之消息的取出与消息的其他操作

你可能感兴趣的:(Android Handler机制7--消息移除、关闭及其他)