详解Android消息机制完整的执行流程

作者:长安皈故里 时间:2021-10-14 18:11:00 

从Handler.post()说起

Handler.post()是用来发送消息的,我们看下Handler源码的处理:

public final boolean post(@NonNull Runnable r) {
  return sendMessageDelayed(getPostMessage(r), 0);
}

首先会调用到getPostMessage()方法将Runnable封装成一条Message,然后紧接着调用sendMessageDelayed()方法:

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
   if (delayMillis < 0) {
       delayMillis = 0;
   }
   return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

这里我们介绍下sendMessageDelayed()的第二个参数delayMillis,这个表示消息延时执行的时间,而post()方法本身代表着非延迟执行,所以这里delayMillis的值为0.

而如果是我们另一个常用的函数postDelay(),这里的delayMillis的值就是传入的延迟执行的时间

继续往下走,会调用到Handler.sendMessageAtTime()方法:

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
   MessageQueue queue = mQueue;
   //...
   return enqueueMessage(queue, msg, uptimeMillis);
}

获取到Looper对应的消息队列MessageQueue,继续往下走,作为参数传给enqueueMessage()方法,这个方法主要是对上面封装的Message进行填充:

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
       long uptimeMillis) {
   msg.target = this;
   msg.workSourceUid = ThreadLocalWorkSource.getUid();

if (mAsynchronous) {
       msg.setAsynchronous(true);
   }
   return queue.enqueueMessage(msg, uptimeMillis);
}

比如将Message被负责分发的target赋值成当前Handler对象,然后根据是否为异步Handler来决定是否给Message添加异步标识。

MessageQueue.enqueueMessage()添加消息至队列中

boolean enqueueMessage(Message msg, long when) {
   //...
   synchronized (this) {
       //...
       msg.markInUse();
       msg.when = when;
       Message p = mMessages;
       boolean needWake;
       //1.
       if (p == null || when == 0 || when < p.when) {
           msg.next = p;
           mMessages = msg;
           needWake = mBlocked;
       } else {
           needWake = mBlocked && p.target == null && msg.isAsynchronous();
           Message prev;
           //2.
           for (;;) {
               prev = p;
               p = p.next;
               if (p == null || when < p.when) {
                   break;
               }
               if (needWake && p.isAsynchronous()) {
                   needWake = false;
               }
           }
           msg.next = p;
           prev.next = msg;
       }
       //3.
       if (needWake) {
           nativeWake(mPtr);
       }
   }
   return true;
}

这个方法的使用很明确,就是将Message添加到消息队列中,下来我们主要讲解这个方法的三个核心点,对应上面的注释标识:

1.如果当前消息队列本来为null、消息执行的时间戳为0、消息执行的时间小于消息队列队头消息的执行时间,只要满足上面三个条件之一,直接将该条Message添加到消息队列队头;

这里说下消息执行的时间戳什么时候会为0,就是调用Handler.sendMessageAtFrontOfQueue()这个方法,就会触发将当前发送的Message添加到消息队列队头。

2.如果上面的三个条件都不满足,就遍历消息队列,比较将要发送的消息和消息队列的消息执行时间戳when,选择适当的位置插入;

3.判断是否需要唤醒当前主线程,开始从消息队列获取消息进行执行;

Looper.loop()分发消息

这个方法会开启一个for(;;)循环,不断的从消息队列中获取消息分发执行,没有消息时会阻塞主线程进行休眠,让出CPU执行权。

for(;;)循环会不断的调用Looper.loopOnce(),开始真正的消息获取和分发执行:

private static boolean loopOnce(final Looper me,
       final long ident, final int thresholdOverride) {
   Message msg = me.mQueue.next(); // might block
   if (msg == null) {
       return false;
   }
   try {
       msg.target.dispatchMessage(msg);
   }
   msg.recycleUnchecked();
   return true;
}

上面是经过简化的代码,首先调用MessageQueue.next()从消息队列中获取消息,然后调用关键方法msg.target.dispatchMessage(msg)开始消息的分发执行,这个方法之前的文章有进行介绍,这里就不再过多介绍了。

接下来我们看下MessageQueue.next()如何获取消息的。

MessageQueue.next()获取消息

Message next() {
   //...
   for (;;) {
       //1.休眠主线程
       nativePollOnce(ptr, nextPollTimeoutMillis);
       synchronized (this) {
           final long now = SystemClock.uptimeMillis();
           Message prevMsg = null;
           Message msg = mMessages;
           //2.获取异步消息
           if (msg != null && msg.target == null) {
               do {
                   prevMsg = msg;
                   msg = msg.next;
               } while (msg != null && !msg.isAsynchronous());
           }
           //3.获取普通消息
           if (msg != null) {
               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;
                   msg.markInUse();
                   return msg;
               }
           } else {
               nextPollTimeoutMillis = -1;
           }

//...
           if (mPendingIdleHandlers == null) {
               mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
           }
           mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
       }
       //4.执行Idle消息
       for (int i = 0; i < pendingIdleHandlerCount; i++) {
           final IdleHandler idler = mPendingIdleHandlers[i];
           mPendingIdleHandlers[i] = null;

boolean keep = idler.queueIdle();
           if (!keep) {
               synchronized (this) {
                   mIdleHandlers.remove(idler);
               }
           }
       }
       //...
   }
}
  • 如果当前消息队列中没有消息或者还没到下一条消息的执行时间,就调用nativePollOnce()方法休眠主线程,让出CPU执行权;

  • 如果Message的target为null,就代表是一个消息屏障消息,之后就只能从消息队列获取异步消息了,如果不存在,就尝试执行Idle消息;

  • 如果不存在消息屏障,则就从消息队列中正常尝试获取Message,如果不存在,就尝试执行Idle消息;

  • 执行Idle消息,只有在主线程空闲(当前消息队列中没有消息或者还没到下一条消息的执行时间)的情况下才会去尝试执行Idle消息,这种类型的消息非常有用,具体的可以参考我之前写的文章:IdleHandler基本使用及应用案例分析

来源:https://juejin.cn/post/7152667953161322510

标签:Android,消息机制
0
投稿

猜你喜欢

  • java 避免出现NullPointerException(空指针)的方法总结

    2022-08-31 04:15:51
  • C#使用Json.Net对JSON与对象的序列化与反序列化

    2023-04-08 07:22:25
  • c#遍历System.drawing.Color下面的所有颜色以及名称以查看

    2022-05-12 06:33:19
  • Android使用HttpURLConnection实现网络访问流程

    2023-09-04 10:40:31
  • Java 判断字符为中文实例代码(超管用)

    2021-08-24 13:18:31
  • Java卡片布局管理器解释及实例

    2022-03-12 18:26:19
  • Idea如何导入一个SpringBoot项目的方法(图文教程)

    2022-08-10 22:40:49
  • 用c#获得当前用户的Application Data文件夹位置

    2022-02-13 10:05:22
  • spring的jdbctemplate的crud的基类dao

    2022-05-30 13:05:17
  • idea中使用(Revert Commit)图解

    2023-03-22 18:45:58
  • C#不提升自己程序的权限实现操作注册表

    2023-01-24 18:51:31
  • java中fastjson生成和解析json数据(序列化和反序列化数据)

    2023-12-08 17:45:24
  • js事件模型与自定义事件实例解析

    2021-05-26 06:15:06
  • IDEA JavaWeb项目启动运行后出现404错误的解决方法

    2022-05-19 01:22:11
  • SpringBoot访问静态资源的配置及顺序说明

    2022-07-18 13:53:31
  • Java中JavaBean对象和Map的互相转换方法实例

    2021-07-12 04:46:05
  • 使用logback实现按自己的需求打印日志到自定义的文件里

    2022-05-12 16:56:49
  • java实现学生信息管理系统

    2023-11-11 08:22:35
  • java线程池ThreadPoolExecutor的八种拒绝策略示例详解

    2021-06-24 11:31:10
  • Java使用IO流实现音频的剪切和拼接

    2023-09-11 08:47:46
  • asp之家 软件编程 m.aspxhome.com