(十一)BroadcastReceiver

BroadcastReceiver,也就是广播,简称Receiver。

很多App开发人员表示,从来没用过Receiver。其实吧,对于音乐播放类App,用Service和Receiver还是蛮多的,如果你用过QQ音乐,App退到后台,音乐照样播放不会停止,这就是你写的Service在后台起作用。

在前台的Activity,点击停止按钮,就会给后台Service发送一个Receiver,通知它停止播放音乐;点击播放按钮,仍然是发送这个Receiver,只是携带的值变了,所以Service收到请求后播放音乐。

反过来,后台Service播放每播放完一首音乐,接下来准备播放下一首音乐的时候,就会给前台Activity发Receiver,让Activity显示下一首音乐的名称。

所以音乐播放器的原理,就是一个前后台Activity和Service互相发送和接收Receiver的过程。

Receiver分静态广播和动态广播两种。

在Manifest中声明的Receiver,是静态广播:

在程序中手动写注册代码的,是动态广播:

二者具有相同的功能,只是写法不同。既然如此,我们就可以把所有静态广播,都改为动态广播,这就省的在Manifest文件中声明了,避免了AMS检查。接下来你想到什么?对,Receiver的插件化解决方案,就是这个思路。

接下来我们看Receiver是怎么和AMS打交道的,分为两部分,一是注册,二是发送广播。

你只有注册了这个广播,发送这个广播时,才能通知到你,执行onReceive方法。

我们就拿音乐播放器来举例,在Activity注册Receiver,在Service发送广播。Service播放下一首音乐时,会通知Activity修改当前正在播放的音乐名称。

注册过程如下所示:

1)在Activity中,注册Receiver,并通知AMS。

这里Activity使用了Context提供的registerReceiver方法,然后通过AMN/AMP,把一个receiver传给AMS。

在创建这个Receiver对象的时候,需要为receiver指定IntentFilter,这个filter就是Receiver的身份证,用来描述receiver。

在Context的registerReceiver方法中,它会使用PMS获取到包的信息,也就是LoadedApk对象。

就是这个LoadedApk对象,它的getReceiverDispatcher方法,将Receiver封装成一个实现了IIntentReceiver接口的Binder对象。

我们就是将这个Binder对象和filter传递给AMS。

只传递Receiver给AMS是不够的,当发送广播时,AMS不知道该发给谁啊?所以Activity所在的进程还要把自身对象也发送给AMS。

2)AMS收到消息后,就会把上面这些信息,存在一个列表中,这个列表中保存了所有的Receiver。

注意了,这里忙活半天,都是在注册动态receiver。

静态receiver什么时候注册到AMS的呢?是在App安装的时候。PMS会解析Manifest中的四大组件信息,把其中的receiver存起来。

动态receiver和静态receiver分别存在AMS不同的变量中,在发送广播的时候,会把两种receiver合并到一起,然后以此发送。其中动态的排在静态的前面,所以动态receiver永远优先于静态receiver收到消息。

此外,Android系统每次启动的时候,也会把静态广播接收者注册到AMS。因为Android系统每次启动时,都会重新安装所有的apk,详细流程,我们会在后面PMS的相关章节看到。

- - - - - - - - - - - - - - - 华丽的分界线------------------------

发送广播的流程如下:

1)在Service中,通过AMM/AMP,发送广播给AMS,广播中携带着Filter。

2)AMS收到这个广播后,在receiver列表中,根据filter找到对应的receiver,可能是多个,把它们都放到一个广播队列中。最后向AMS的消息队列发送一个消息。

当消息队列中的这个消息被处理时,AMS就从广播队列中找到合适的receiver,向广播接收者所在的进程发送广播。

3)receiver所在的进程收到广播,并没有把广播直接发给receiver,而是将广播封装成一个消息,发送到主线程的消息队列中,当这个消息被处理时,才会把这个消息中的广播发送给receiver。

我们下面通过图,仔细看一下这3个阶段:

第1步,Service发送广播给AMS

发送广播,是通过Intent这个参数,携带了Filter,从而告诉AMS,什么样的receiver能接受这个广播。

     第2步,AMS接收广播,发送广播。

接收广播和发送广播是不同步的。AMS每接收到一个广播,就把它扔到广播发送队列中,至于发送是否成功,它就不管了。

因为receiver分为无序receiver和有序receiver,所以广播发送队列也分为两个,分别发送这两种广播。

AMS发送广播给客户端,这又是一个跨进程通信,还是通过ATP,把消息发给APT。因为要传递Receiver这个对象,所以它也是一个Binder对象,才可以传过去。我们前面说过,在把Receiver注册到AMS的时候,会把Receiver封装为一个IIntentReceiver接口的Binder对象。那么接下来,AMS就是把这个IIntentReceiver接口对象传回来。

第3步,App处理广播

这个流程描述如下:

1)消息从AMS传到客户端,把AMS中的IIntentReceiver接口对象转为InnerReceiver对象,这就是receiver,这是一个AIDL跨进程通信。

2)然后在ReceiverDispatcher中封装一个Args对象(这是一个Runnable对象,要实现run方法),包括广播接收者所需要的所有信息,交给ActivtyThread来发送

3)接下来要走的路就是我们所熟悉的了,ActivtyThread把Args消息扔到H这个Hanlder中,向主线程消息队列发送消息。等到执行Args消息的时候,自然是执行Args的run方法。

4)在Args的run方法中,实例化一个Receiver对象,调用它的onReceiver方法。

5)最后,在Args的run方法中,随着Receiver的onReceiver方法调用结束,会通过AMN/AMP发送一个消息个AMS,告诉AMS,广播发送成功了。AMS得到通知后,就发送广播给下一个Receiver。

注意:InnerReceiver是IIntentReceiver的stub,是Binder对象的接收端。

广播的种类

Android广播按发送方式分类有三种:无序广播、有序广播(OrderedBroadcast)和粘性广播(StickyBroadcast)。

1)无序广播是最普通的广播。

2)有序广播区别于无序广播,就在于它可以指定优先级。

这两种receiver存在AMS不同的变量中,可以认为是两个receiver集合,发送不同类别的广播。

3)粘性广播是无序广播的一种。

粘性广播,我们平常见的不多,但我说一个场景你就明白了,那就是电池电量。当电量小于20%的时候,就会提示用户。

而获取电池的电量信息,就是通过广播来实现的。

但是一般的广播,发完就完了。我们需要有这样一种广播,发出后,还能一直存在,未来的注册者也能收到这个广播,这种广播就是粘性广播。

由于动态receiver只能在Activity的onCreate()方法调用时才能注册再接收广播,所以当程序没有运行就不能接受到广播;但是静态注册的则不依赖于程序是否处于运行状态。

至此,关于广播的所有概念,就全都介绍完了,就连我都比较惊讶的是,我居然没贴什么代码。希望上述这两千多字,能引导App开发人员进入一个神奇的世界。

下一篇文章,我们去看看ContentProvider。

写给Android App开发人员看的Android底层知识(6)的更多相关文章

  1. 写给Android App开发人员看的Android底层知识(1)

    这个系列的文章一共8篇,我酝酿了很多年,参考了很多资源,查看了很多源码,直到今天把它写出来,也是战战兢兢,生怕什么地方写错了,贻笑大方. (一)引言 早在我还是Android菜鸟的时候,有很多技术我都 ...

  2. 写给Android App开发人员看的Android底层知识(2)

    (五)AMS 如果站在四大组件的角度来看,AMS就是Binder中的Server. AMS全称是ActivityManagerService,看字面意思是管理Activity的,但其实四大组件都归它管 ...

  3. 写给Android App开发人员看的Android底层知识(3)

    (七)App启动流程第2篇 书接上文,App启动一共有七个阶段,上篇文章篇幅所限,我们只看了第一阶段,接下来讲剩余的六个阶段,仍然是拿斗鱼App举例子. 简单回顾一下第一阶段的流程,就是Launche ...

  4. 写给Android App开发人员看的Android底层知识(5)

    (十)Service Service有两套流程,一套是启动流程,另一套是绑定流程.我们做App开发的同学都应该知道. 1)在新进程启动Service 我们先看Service启动过程,假设要启动的Ser ...

  5. 写给Android App开发人员看的Android底层知识(7)

    (十二)ContentProvider (1)ContentProvider是什么? ContentProvider,简称CP. 做App开发的同学,尤其是电商类App,对CP并不熟悉,对这个概念的最 ...

  6. 写给Android App开发人员看的Android底层知识(8)

    (十)PMS及App安装过程 PMS,全称PackageManagerService,是用来获取Apk包的信息的. 在前面分析四大组件与AMS通信的时候,我们介绍过,AMS总是会使用PMS加载包的信息 ...

  7. 写给Android App开发人员看的Android底层知识(4)

    (八)App内部的页面跳转 在介绍完App的启动流程后,我们发现,其实就是启动一个App的首页. 接下来我们看App内部页面的跳转. 从ActivityA跳转到ActivityB,其实可以把Activ ...

  8. 一看就懂的Android APP开发入门教程

    一看就懂的Android APP开发入门教程 作者: 字体:[增加 减小] 类型:转载   这篇文章主要介绍了Android APP开发入门教程,从SDK下载.开发环境搭建.代码编写.APP打包等步骤 ...

  9. 谋哥:App开发人员的苦逼不值得怜悯!!

    [谋哥每天一干货,第四十篇]         为什么取这个标题呢?由于昨天一些本来"支持"谋哥的人看到谋哥搞收费VIP群,认为谋哥赚苦逼开发人员的钱非常不道德,且说谋哥我写的东西都 ...

随机推荐

  1. ES6 Promise 状态解惑

    Promise的概念在ES6标准推出来之前已经深入人心,很多框架和第三方库都有类似的实现.但在深入理解ES6的Promise对象的时候,受之前经验的影响,很多概念给人似是而非的感觉,其中有一个特别明显 ...

  2. C# SMTP发送邮件

    public void SendMail() { MailMessage mail = new MailMessage(); mail.From = new MailAddress("fro ...

  3. IC卡读卡器web开发,支持IE,Chrome,Firefox,Safari,Opera等主流浏览 器

    IC卡读卡器在web端的应用越来越多,但是早期发布的ocx技术只支持IE浏览器,使用受到了很多的限制.IC卡读卡器云服务的推 出,彻底解决了以上的局限,使得IC卡读卡器不仅可以应用在IE浏览器上,还可 ...

  4. 打造比Dictionary还要快2倍以上的字查找类

    针对一个长度为n的数组. [1,2,3,4,5,6,7,8,9] 最快的通用查找类是Dictionary其采用hashcode算法,复杂度为O(1). 而上大学时,最快的查找法为二分查找法,复杂度为O ...

  5. .net md5

    今天试着写了一下MD5加密,网站注册登录估计都用到MD5,今天就把写的贴出来 public string GetMd5(string user) { using (MD5 md5 = MD5.Crea ...

  6. C++ 编译报错discards qualifiers [-fpermissive]

    声明了一个类 class Card { public: Card(const string&); int m_value; char m_suit; private: const static ...

  7. SQL Server 死锁概念和分析

    锁的概念 锁是什么 锁是数据库中在并发操作情形下保护资源的机制.通常(具体要看锁兼容性)只有锁的拥有者才能对被锁的资源进行操作,从而保证数据一致性. 锁的概念可分为几部分 锁资源(锁住什么) 锁模式( ...

  8. JDBC访问数据库

    一.准备条件 外界条件 在数据库中首先创建表空间 在创建的表中添加数据 代码部分 导入数据库的驱动包(jar) 加载数据库驱动 获取数据库连接 编写sql语句 利用prepareStatement进行 ...

  9. css3实现checkbox变按钮

    css3实现checkbox变按钮 .search_checkbox { margin: 0; padding: 0; margin-left: 15px; display: inline-block ...

  10. webapp万能选择器:iosselect

    iosselect是个什么东西? 移动端浏览器对于select的展示样式是不一致的,ios下是类似原生的picker,安卓下各浏览器展示各异,我们需要一个选择器组件来统一各端下各种浏览器的展示.下面是 ...