您现在的位置是:首页 > 编程语言学习 > 其他编程语言 > 文章正文 其他编程语言

Android移除Message的方法分享

2022-10-12 10:18:37 其他编程语言

简介大家都知道,消息机制在Android系统运行中扮演着重要的角色,通过消息发送、添加消息队列、分发等一整个流程驱动Android的运行。主线程是在...

大家都知道,消息机制在Android系统运行中扮演着重要的角色,通过消息发送、添加消息队列、分发等一整个流程驱动Android的运行。

主线程是在ActivityThread.main()中调用了Looper.loop(),开启消息循环遍历执行的,这个消息循环可以退出吗,接下来我们仔细研究下;

上源码:

  1. void quit(boolean safe) { 
  2. //1.不允许退出就抛出异常 
  3. if (!mQuitAllowed) { 
  4. throw new IllegalStateException("Main thread not allowed to quit."); 
  5.  
  6. synchronized (this) { 
  7. if (mQuitting) { 
  8. return
  9. //2. 
  10. mQuitting = true
  11.  
  12. if (safe) { 
  13. //3.安全退出 
  14. removeAllFutureMessagesLocked(); 
  15. else { 
  16. //4.非安全退出 
  17. removeAllMessagesLocked(); 
  18.  
  19. nativeWake(mPtr); 

1.对于主线程而言,mQuitAllowed的值是false,也就是说主线程的Looper循环不允许手动调用quit()退出,否则就抛出异常;

2.将退出标识mQuitting置为true,这样当从消息队列中取消息时,会先判断下这个标识mQuitting是否为true,是就会经过调用链一步步退出Looper消息循环;

3.安全的退出Looper开启的消息循环,深入下removeAllFutureMessagesLocked()看下:

  1. private void removeAllFutureMessagesLocked() { 
  2. final long now = SystemClock.uptimeMillis(); 
  3. Message p = mMessages; 
  4. if (p != null) { 
  5. //1. 
  6. if (p.when > now) { 
  7. removeAllMessagesLocked(); 
  8. else { 
  9. Message n; 
  10. for (;;) { 
  11. n = p.next; 
  12. if (n == null) { 
  13. return
  14. if (n.when > now) { 
  15. break
  16. p = n; 
  17. p.next = null
  18. do { 
  19. p = n; 
  20. n = p.next; 
  21. p.recycleUnchecked(); 
  22. while (n != null); 
  • 首先如果消息队列队头的消息的执行时间戳when大于当前时间,则直接调用 removeAllMessagesLocked()方法移除所有的消息 ,这个方法之后会讲解;
  • 上面条件不满足,就不断的遍历消息队列,直到找出执行时间戳大于当前时间的消息,然后通过do-while()循环,将该消息及之后的消息全部进行回收处理,放入到我们之前讲解的对象池;

4.非安全的退出是直接调用了removeAllMessagesLocked()方法,我们深入看下:

  1. private void removeAllMessagesLocked() { 
  2. Message p = mMessages; 
  3. while (p != null) { 
  4. Message n = p.next; 
  5. p.recycleUnchecked(); 
  6. p = n; 
  7. mMessages = null

可以看到这种移除方法大杀特杀,不会去比较消息执行的时间戳啥的,直接全部干翻回收到消息对象池,简单粗暴。

这里对于非安全和安全退出Looper循环做个总结:

安全退出Looper循环只会移除回收大于当前时间戳的消息,而不大于当前时间戳的消息都可以保证正常执行;而非安全的退出比较粗暴,直接清空回收整个消息队列。这两种情况大家根据需要选择性的使用。

removeXXXMessages()移除指定的消息

可以看到移除消息的方法一大堆,比如通过指定Messagewhatobjcallback等信息移除指定Message,这里我们就以removeCallbacksAndMessages()举例。

removeCallbacksAndMessages()方法大家应该很梳理,是我们在某个界面中使用Handler发送消息时,避免发生内存泄漏的一种方式,接下来我们深入分析下:

  1. void removeCallbacksAndMessages(Handler h, Object object) { 
  2. //... 
  3. synchronized (this) { 
  4. Message p = mMessages; 
  5. //1.从头移除消息 
  6. while (p != null && p.target == h 
  7. && (object == null || p.obj == object)) { 
  8. Message n = p.next; 
  9. mMessages = n; 
  10. p.recycleUnchecked(); 
  11. p = n; 
  12.  
  13. //2. 从中间移除消息 
  14. while (p != null) { 
  15. Message n = p.next; 
  16. if (n != null) { 
  17. if (n.target == h && (object == null || n.obj == object)) { 
  18. Message nn = n.next; 
  19. n.recycleUnchecked(); 
  20. p.next = nn; 
  21. continue
  22. p = n; 

如果这个方法传入的object不为null,就会移除指定的Message,如果指定为null,就会移除传入的Handler发送的所有消息。

上面的源码中可以看到,移除Message分为两个部分,为什么要这么做呢?

假设消息队列中存在下面一系列消息集合:

如果Message1满足移除条件,那么直接回收这条消息,并将消息队列的队头指针指向下一个消息即可mMessages = mMessages.next,对应上面源码中前半部分移除消息的逻辑。

但假设Message1不满足移除条件,Message2满足移除条件,这样移除就不是直接将消息队列的队头指针指向next即下一个Message就能简单解决的。

正确的做法是:先要保存Message1,然后通过Message2.next获取到Message3的引用保存起来,最后将Message1.next指向上面保存的Message3引用。这部分就对应上面源码中后半部分移除消息的逻辑。

总结

本篇文章主要是对MessageQueue提供的各种移除Message的方法做了一个简单的介绍,方法很多主要分为两种:移除指定标识的Handler发送的Message和移除所有Handler发送的Message

相关文章

站点信息