专栏首页Android机动车从源码角度看广播

从源码角度看广播

简介

几乎每个安卓应用都无可避免的使用到广播。例如监听WIFI的开启状态、时间的获取,甚至是我们最常用的闹钟功能,都是结合着AlarmManager与广播来实现的。理解广播的注册、发送与接收实现源码将使我们更加懂安卓系统,同时,基于对广播的理解,我们也能很快的掌握AMS中其它组件的实现原理。

网上对于广播源码的分析数以千计,其中不乏精品的文章。但这些文章大多都太过枯燥、太抽象、太冗长,我最初看这些文章时,看了很多遍后才能慢慢理解。这篇文章里,我将画出几张简明扼要的图,简略的列出广播注册、发送中涉及到重要类。每副图后都会有我的一些简洁的理解,都是我在平日里开发中积累的精华。初学者能够通过这些对广播源码有个迅速的大体印象,熟悉广播源码的同学也能够查漏补缺。受语言与我自己理解的局限,如果文章中出现错误还希望大家指正。

文章最后,我会附上一张广播实用adb日志的输出对应图,希望能帮大家对于广播日志有个cheat sheet的作用:-)

重要类

  • ReceiverDispatcher
  • BroadcastQueue
  • BroadcastRecord
  • BroadcastFilter
  • ResolveInfo
  • InnterRceiver
  • ReceiverList

广播注册

概括图

未包含的点

我这张图为了避免信息太多内容晦涩,有两个重要的过程没有画出来:

  1. 动态注册广播操作过程中,首先会检查sticky广播进行检查操作
  2. 静态广播的注册逻辑在PMS中,涉及对manifest文件的解析,以后再分析吧

这里我详细说说动态注册时,对sticky的特殊处理。

动态广播注册阶段中,第一步就是对sticky广播进行检查。 如果AMS中的mStickyBroadcasts存在符合过滤条件的Intent,那么这个广播在注册阶段就会被派发。当从registerReceiver传参进来的receiver为NULL,那么这个最新的sticky Intent将直接被返回。值得注意的一点是,在注册阶段发送出的广播是不会出现在dump日志历史记录中的。

具体解析

再看这张图, 我将从左到右对每个重要图像进行解释:

  • mReceivers: 维护在App中的一个列表,用户存储BroadcastReceiver与ReceiverDispatcher之间的对应关系,同一个BroadcastReceiver对应的Binder Stub将不会被反复创建
  • InnerReceiver: 实现在App中的Binder”服务端”,它的父类是Binder Stub,当广播在AMS调度时,AMS将在system_server端调用它的代理对象binder call到客户端,以在App端触发广播的onReceive方法
  • mRegisteredReceivers: 动态广播注册的核心对象,是一个HashMap,这个Map以IBinder为键,每个BroadcastReceiver将会在AMS中对应这一个ReceiverList
  • ReceiverList: 继承自一个泛型为IntentFilter的ArrayList,保存着IntentReceiver句柄,同时有着匹配广播Intent的作用

mRegisteredReceivers中的数据在App进程死亡或App调用unregisterReceiver反注册接口才会被清除。

广播发送

广播入队

  • IntentResolver: 动态注册的广播都存储在这个对象中
  • registeredReceiver: 动态广播接收者列表
  • PMS:负责解析与存储静态广播信息
  • receivers: 景泰广播接收者列表
  • BroadcastRecord: 进行广播入队的基本单位,定义了这条广播的类型、属性和所有的接收者列表
  • BroadcastQueue: AMS中目前维护了前台、后台两种类型的队列,每个队列有两个List,分为并行与串行列表;同时,BroadcastQueue也是dump日志中最重要的打印单位,保存着历史的广播派发记录

广播入队的过程中,最重要的步骤就是收集接收者的列表,并将它封装成一个BroadastRecord对象,将这个record对象加入到BroadcastQueue中,并调用Handler的sendMessage方法,准备派发广播

广播派发

这张图中的BroadcastQueue, BroadcastRecord, BroadcastFilter, ResolveInfo和ReceiveList在前面的队列中都已经出现过了,我就不做解释了,只对App端的几个对象进行解释:

  • ActivityThread: 客户端的”主线程”,本质上不是线程,当新进程在Zygote成功创建后,会调用ActivityThread的main方法,而这个方法将会启动一个Looper,所谓的客户端主线程就运行在这个Looper上,main方法调用Looper.loop后将进入无限循环,等待新的消息进行处理
  • ApplicationThread: 继承自ApplicationThreadNative,是App端与AMS进行binder call的服务端,AMS调用到App端都是ONE WAY的方式,AMS不需要等待客户端执行完成
  • ActivityThread.H: 动态广播将运行在这个Hanlder中
  • LoadedApk.Args: 实现了Runnable方法, 静态广播的onReceive方法在这里进行执行

广播的派发是在BroadcastQueue对象中进行的,它维护着并行与串行两个队列。在派发的过程中,并行广播将会被先派发,随后再派发串行广播

动态广播的派发是取出BroadcastFilter的ReceiverList对象,通过ProcessRecord拿到ApplicationThread的代理对象,binder call调用,随后在App中调用BroadcastReceiver.onReceive方法;静态广播的派发是从ResolverInfo对象中取出processName, 再取出ProcessRecord, 最后在LoadedApk中调用了BroadcastReceiver.onReceive

读懂”adb shell dumpsys activity b”

这里我不想直接贴上这条adb命令的执行样例,因为实在太长了,会让这篇文章显得很水:-)

有兴趣的话大家可以试试这条命令,对应着我画的这个图,就能很清楚明白的理解它的意思

这里我简单解释下:

  • Registered Receivers & Receiver Resolver Table: 广播注册信息
  • Active broadcasts & Pending broadcast: 当前BroadcastQueue的运行状态;正在派发执行的广播
  • Historical broadcasts: BroadcastQueue中的广播派发历史
  • Sticky broadcasts for user: AMS中存储的粘性广播信息
  • mBroadcastScheduled:已经enqueue,但是没有派发的广播

总结

  • 动态广播的注册主要维护的是mRegisteredReceivers这个HashMap表
  • 广播发送中的入队列步骤主要是收集动态注册和静态注册的接收者,封装成一个BroadcastRecord,enqueue到队列中
  • 广播发送中的派发步骤主要是调用binder call到各个客户端,执行BroadcastReceiver.onReceive方法
  • dumpsys activity b中保存着广播注册、发送中涉及的重要数据结构的实时状态与历史状态,对调试很有帮助

本文分享自微信公众号 - Android机动车(JsAndroidClub),作者:贾帅

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-11-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • AndroidStudio3.0多渠道打包:我用一行命令打出N个包

    csdn: http://blog.csdn.net/k_bb_666

    蜻蜓队长
  • MaterialDesign之FloatingActionButton

    就是上面这样了,忘说了一件很重要的事情……FloatingActionButton的监听就是最原始的监听!!!接下来到了重头戏了

    蜻蜓队长
  • 数据结构学习笔记——队列

    线性表有顺序存储和链式存储,栈是线性表,所以有这两种存储方式。同样,队列作为一种特殊的线性表,也同样存在这两种存储方式。

    蜻蜓队长
  • Android高频面试专题 - 基础篇(四)BroadcastReceiver

    Android 中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。

    Android扫地僧
  • Android查缺补漏--BroadcastReceiver的类型与使用

    Broadcast 是一种被用于应用内和应用之间传递信息的机制。一个广播可以对应多个接受者。一个完整的广播机制,需要具有以下三个要素: 发送广播的Broadca...

    codingblock
  • Android中广播实践小结

    Android系统有一套广播消息机制,方便进行每个应用程序之间的消息通知。而且广播接受者作为Android四大组件之一,经常被使用到。我也接触到广播消息机制,因...

    猴哥yuri
  • 使用广播-BroadcastReceiver最详细解析

    广播,大家应该可以理解,我们在学校做眼保健操时,就有个广播,告诉我们要做眼保健操了。广播传递信息告诉我们要做什么,通知信息或传递数据等。广播接收器是用来接收来自...

    达达前端
  • Android基础总结(4)——广播接收器

      在Android中的每个应用程序可以对自己感兴趣的广播进行注册,这样该程序就只会接收自己所关心的广播内容,这些广播可能来自于系统的,也可能来自于其他应用程序...

    mukekeheart
  • Android 四大组件之BroadcastReceiver

    Hi,大家好,又双见面啦,上一期我们讲了如何使用Activity,肯定有不少小伙伴已经创建了属于自己的FirstActivity,那么这一期我们主要为大家介绍第...

    下码看花
  • 从0系统学Android--5.2 发送广播

    前面已经学习了如何接受广播了,下面来学习如何发送自定义广播,广播类型分为:标准广播和有序广播,下面分别来说一下这两种广播如何发送。

    开发者

扫码关注云+社区

领取腾讯云代金券