前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android消息机制

Android消息机制

原创
作者头像
笔头
修改2024-09-25 14:43:41
380
修改2024-09-25 14:43:41

一、整体了解

Handler是Android中一个重要的组件,它主要负责接收和处理消息,实现线程间的通信。

主要在两种场景下使用:

1.跨线程通信。比较常见的就是在子线程中处理耗时任务,处理完通知主线程更新UI。

2.同线程通信。比较常见的是我们需要执行延时任务,为开发者提供了一种灵活的任务调度方式。

Android消息机制主要涉及几个类

  • Handler。消息处理者,负责发送和处理消息。
  • Message。消息的主体,包含消息的具体内容和类型。
  • MessageQueue。消息队列,用于存储和顺序分发消息。
  • Looper。消息循环器,负责从MessageQueue中取出消息并分发给Handler处理。

消息机制运行流程大概如下

应用进程启动后,默认Looper是创建好的。上图我们可以看出,执行步骤1,不断从MessageQueue中获取Message,当然在未收到当前线程的Handler发起消息前,是拿不到Message的走不到步骤4。

当我们创建Handler(在哪个线程中创建无所谓,关键指定looper)且发送消息后,也就是执行步骤2,把消息存放到looper所在线程的MessageQueue中后。由于步骤1不断从MessageQueue中获取消息,等到拿到消息Message后,执行handler消息回调(Message是持有Handler的,所以能调用handler回调函数)也就是执行步骤4。handler收到通知后,就可以按照我们要求执行相关任务。

基本流程如上。

利用平时我们比较熟悉的快递理解Android消息机制

在看源码之前我们先需要了解下

1.ActivityThread,涉及到Looper什么时候开始工作的

2.ThreadLocal,涉及到,为什么一个进程looper唯一

接下来我们按如上4个步骤结合源码看下,这个消息是如何流转的。

二、具体分析

步骤一、 Looper创建及运作

在启动一个进程后会在主线程创建一个looper,接下来执行Looper.loop()

可以解释为,每个街道(进程)在不同地方(不同线程)设置一个菜鸟站点(Looper)来处理包裹(loop)

我们看下ActivityThread中main函数

Looper创建

我们可以看到进程起来时,在main函数7624行开始创建Looper,在7652行开始loop执行轮寻MessageQueue中是否有Message

我们看下Looper.prepareMainLooper();

可以看出Looper创建不太复杂,prepareMainLooper先检查sThreadLocal是否存在Looper对象,如果不存在,则创建一个Looper对象,同时存放在当前sThreadLocal中。这里我们可以看到MessageQueue是在Looper里面创建的.sThreadLocal 是ThreadLocal

从上述代码可以得知,一个线程中只能有一个looper对象(当前线程就是我们熟悉的UI线程)同时一个线程也只能有一个MessageQueue

loop函数

loop代码稍微有点长,我们分段来看

上面如我们可以得知,155行和166行分别拿到了looper和当前looper对应的MessageQueue

上述代码 for 轮寻当前MessageQueue队列。186行获取当前队列头部Message(后面详细看下MessageQueue)。

其他代码基本和我们正常使用没有多大关系,我们继续看后面代码

这块代码主要涉及2方面:

1.消息分发

2.消息Message回收

227行代码执行消息分发,msg.target是谁?这个target就是handler,target在哪里赋值的(这个我们在分析创建Handler看下),dispatchMessage也是handler里消息分发函数。

其他的代码,我们在正常使用Handler不需要关心

271行代码执行Message recycleUnchecked 消息回收

(上面涉及到 MessagQueue.next 和msg.recycleUnchecked后面详细分析下MessagQueue和Message)

Looper的创建和loop方法逻辑清晰也很好理解。主要逻辑在MessagQueue.next、dispatchMessage和msg.recycleUnchecked

总结下Looper

1、在进程启动后,系统回调用ActivityThread类,在该类中会创建Looper且只会创建一次(通过ThreadLocal实现),创建过程中会创建一个MessageQueue,用来存放Message。

2、创建完成后执行loop方法,循环获取MessageQueue里面的Message(具体怎么获取后面看下MessageQueue的next方法),获取到有效Message后,调用该Message发送者handler的dipatchMessage(具体dipatchMessage如何处理后面看下Handler的dipatchMessage

3、消息处理完成后,执行消息回收(具体如何回收后面看下Message的recycleUnchecked方法

步骤二 、Handler创建、发送消息和处理消息

步骤2里面主要是Handler创建及发消息和消息处理回调

Handler创建

Handler创建主要分两种,

1.Handler(Looper looper) ,指定Looper,确定是在哪个线程处理消息(不指定looper,默认是主线程 )

2.Handler(Callback callback),指定callback,callback不为空的话,先执行callback再执行handleMessage

创建没有什么复杂逻辑

主要3件事,指定Looper、拿到Looper当前MessageQueue,指定callback

looper是确定是在哪个线程处理消息。(确定快递员属于哪个区域的)

MessageQueue是后面发送消息用的(MessageQueue从looper中获取),用来存放当前准备发送的Message(Message目前还没分析,后面分析);(确定快递员快递送到哪个仓库)

callback主要是在dispatchMessage中用到(这个callback相当于这个快递员有个副手,有些事可以先让副手去处理,处理不过来可以让快递员处理)

下面我们来看下消息发送

Handler消息发送

发送消息接口很多,主要分为2类

第一类

sendMessage(Message msg)

sendEmptyMessage(int what)(没有Message只有what,系统会帮忙创建Message{Message.obtain()},同时指定Message参数what就是该what)

sendEmptyMessageDelayed(int what, long delayMillis)

第二类

post(Runnable r) (这种Runnable参数,系统会帮忙创建Message,同时指定Message参数callback就是该Runnable)

这两类接口最终是调用sendMessageAtTime(Message msg, long uptimeMillis)

但是分发消息有点区别,后面在消息分发dispatchMessage里分析下。我们接着看消息存储

723行拿到MessageQueue,这个mQueue就是在创建Handler时从指定Looper中拿到的。

730行调用enqueueMessage,把msg存放到MessageQueue队列里面

775行设置msg持有target,也就是当前handler,还记得上面在loop中在消息分发时调用msg.target.dispatchMessage(msg)接口嘛,这里的target就是handler,也就是发送Message消息的Handler。

781行,调用MessageQueue的enqueueMessage接口。这个是队列添加接口(具体Message添加和查询后面在MessageQueue分析

Handler消息回调处理-包裹处理后通知快递员

99行,如果msg中存在callback,那就执行message.callnack.run(),这个callback,我们可以在消息发送那边看下,属于第二种消息发送方式post(runnable)方式

103行,判断mCallback,这个变量是在创建Handler中传入的,也就是快递员副手,如果存在副手,那就先让副手处理。副手如果还想让快递员处理,那mCallback.handleMessage返回false,反之,返回true。

109行,hanleMessage(msg)是个空方法,我们在创建Handler时,会重写该方法。

以上dispatchMessage分析,也就是包裹处理完后通知快递员。是包裹处理完成

总结Handler

1、Handler的创建大概有2种类型,一种带有Callback一种不带,如果带有Callback参数,那么在消息处理时优先处理Callback,Handler创建过程中,获取到当前Looper中的队列MessageQueue,用来后面发送消息用。

2、Handler消息发送,把Message添加到MessageQueue中

3、Handler消息回调,dispatchMessage可以处理sendMessage发送的消息也可以处理post发送的消息,这两个区别就是看Message中是否包含callback(runnable),post接口是有callback参数的。如果不是post发送的话,则判断当前Handler是否创建了Callback参数,如果有,那么优先回调Callback.handleMassage,这个接口返回布尔值,如果为true,那么就不会执行Handler中的handlerMessage方法,如果返回false(默认返回fasle)那么接下来执行Handler中的handlerMessage方法

下面我们过下MessageQueue和Message吧

MessageQueue分析--快递仓库

接下来我们就过下MessageQueue吧,上面我们在loop中涉及到next方法以及handler中enqueue方法。

1.MessageQueue分析之enqueueMessage--仓库存放入口

563行,这里判断当前队列是否退出,如果退出了,那就不处理消息,同时把当前msg回收,在Looper.loop中我们还记得在消息处理完成后需要回收嘛,这两个是一样的。

570~573行,拿到待存放的msg和msg的延时时间when,以及目前消息队列mMessages。

mMessages是个Message,MessageQueue并不是我们平时说的那个队列,MessageQueue是通过Message来实现队列效果的Message是个单链表。

574~578行,判断当前队列是否为空,延时时间是否小于0,或者当前队列消息when大于待存入的msg,满足这些条件,则把待存入的msg放在队列的头部,同时更新当前队列,对头为待存入的msg

580~595行,如果不满足上述条件,则查找队列的next,看他是不是空(也就是对尾)或者待存入msg的when小于next的when,那就停止遍历。

594行,把待存入的msg安排在p(满足条件的msg)前面。595行把待存入的msg安排在p前面msg的next中。就是在Message中遍历,找到when小于待存入msg的when,然后插入其中。

来个图来回味下

2.MessageQueue分析之next

226行,控制是否要进入休眠,休眠时间是由nextPollTimeoutMillis控制

1.如果nextPollTimeoutMillis=-1,这个时候msg为空,一直阻塞不会超时。

2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回。

3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。

首次nextPollTimeoutMillis=0,不会阻塞。

344~350,如果消息不为空且handler为空,那就找消息屏障

继续看

找到msg消息,比较msg的when和当前时间。

如果when大于当前时间,那计算下时间差,更新nextPollTimeoutMillis,设置下次next阻塞时间

如果when小于当前时间,返回当前msg。

找不到msg消息,nextPollTimeoutMillis=-1,意味下,next下一次就无期限阻塞。

接着看

所有待处理的消息均处理完成, 接下来处理闲时任务

390~393行,闲时任务列表为空,或者不是第一次执行到这里。进入下一次轮寻

396~427行 ,pendingIdleHandlerCount=0,后面不会进入闲时任务。nextPollTimeoutMillis=0,这个只有在第一次闲时任务才会生效。因为执行了闲时任务花费了一段时间(迭代开始处的阻塞方法还未执行到所以还未阻塞),此时再根据之前计算出的阻塞时长阻塞线程显然不合适。

next就这些了。

上面看了MessageQueue,next和enqueue,主要就是针对sMessage操作,sMessage是个单链表。

总结下MessageQueue

1、MessageQueue是一个队列,内部就是个Message单链表,主要功能就是针对Message进行next查询、enqueue插入、hasMessage、removeMessage等操作。

2、enqueue操作主要就是按照待插入MSGD的when找到当前sMessage(Message)when比他大的节点,然后插入到该节点的前面。

3、next操作主要就是查询sMessage的头部节点,拿到该节点后,计算下该节点的when与当前时间差(每次查询前休眠一段时间就是按照这个时间差),然后调用该节点所在的Handler的dispatchMessage方法(Handler环节已分析),最后再回收该Message(下面Message分析环节分析)。这个过程中还处理了闲时任务

接下来,我们看下Message,他主要有两个方法,创建msg和回收msg

Message分析--快递包

我们在调用handler发送消息时,这个消息msg可以直接new一个,但是这个耗资源,android系统已经提供一个方法obtain,可以服用msg,避免每次都要new

1.Message分析之obtain--快递袋复用

我们来看下obtain 消息复用

对应流程图

1.Message分析之recycleUnchecked--快递袋回收

对应流程图

总结下Message

1、Message主要功能有2个,复用Message、回收Message

2、复用Message就是获取当前消息池sPool的头部节点,sPool长度减1

3、回收Message,先清空当前Message,然后插入sPool头部,sPool长度加1,sPool最大长度是50

整个android消息机制如上,这种机制在我们生活中也存在,比如快递处理中心

来看看哈,进程启动创建Looper,执行loop方法,每个进程相当于一个城市,一个城市有很多片区相当于线程,每个线程只能有一个Looper相当于每个片区只有一个菜鸟中心,创建Looper时同时创建了MessageQueue相当于每个菜鸟中心只能有一个仓库,loop相当于菜鸟中心开始工作了。

Handler相当于快递员,每个handler必须指定Looper相当于每个快递员只能为指定的菜鸟中心送包裹,Handler发送消息方式有多种post、sendMessage相当于快递员可以骑摩托车、电瓶车送,handler发送消息是往MessageQueue中添加Message相当于快递员送货到菜鸟中心的仓库里,Handler可以设置Callback参数相当于快递员副手有些包裹处理可以优先给副手处理减轻快递员压力

Message相当于包裹,obtain相当于包裹重复利用,快递处理完,这个包裹不能丢的,还可以在下次包装快递用,recycleUnchecked属于包裹回收,每次处理完快递,包裹需要回收

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、整体了解
  • 二、具体分析
    • 步骤一、 Looper创建及运作
      • Looper创建
      • loop函数
      • 总结下Looper
    • 步骤二 、Handler创建、发送消息和处理消息
      • Handler创建
      • Handler消息发送
      • Handler消息回调处理-包裹处理后通知快递员
      • 总结Handler
      • MessageQueue分析--快递仓库
      • 总结下MessageQueue
      • Message分析--快递包
      • 总结下Message
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档