(十一)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. H5学习第四周

    本周.我们结束了HTML标签和css样式部分,开始了JS的学习.JS是的内容和css,html基本上没有什么联系而且它比较需要逻辑思考能力,所以要从新开始学习. 使用js的三种方式: 1.html标签 ...

  2. JSON对象、JSON字符串的相互转换

    JSON对象.JSON字符串的相互转换 json的格式: 第一种方式: 单一的json字符串,转换成json对象时,需要 eval('(' + json + ')');这样的格式,中间需要加括号 va ...

  3. nginx学习笔记——http module分析

         源码:nginx 1.12.0           nginx由于其高性能.扩充性好等特点在迅速走红,越来越多的公司采用nginx作web服务器.负载均衡.waf等 工作,一些基于nginx ...

  4. 简单的总结一下iOS面试中会遇到的问题

    1.线程是什么?进程是什么?二者有什么区别和联系?  一个程序至少有一个进程,一个进程至少有一个线程: 进程:一个程序的一次运行,在执行过程中拥有独立的内存单元,而多个线程共享一块内存 线程:线程是指 ...

  5. 2017-4-24 WinForm开发基础、窗体的属性CenterScreen

    WinForm中文名称: Windows窗体,是·Net开发平台中对Windows Form的一种称谓. 客户端应用程序:C/S 客户端很重要的特点:可以操作用户电脑上的文件 窗体属性:窗体种类: + ...

  6. 基于AngularJS的过滤与排序【转载】

    程序设计分析 首先,如果要是先查询过滤,就要使用到AngularJS中的 过滤器filter 了. 直接在表达式的后面使用管道命令符 | ,按照下面的写法就可以达到一个过滤的效果: {{ person ...

  7. Angular2.js——主从结构

    学习这一篇的内容,还回到我们快速起步的应用上来. 主从结构,我们想要做一个英雄的列表,我们希望用户在列表中选中一个英雄,然后这个被选中的英雄出现在详情视图中.主视图是英雄列表,从视图则是被选中英雄的详 ...

  8. poj3067树状数组求逆序数

    Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Jap ...

  9. EmpyoyeeManger_1.0

    整体结构 首先创建一个名为employee的数据库 create database employee; 然后在该数据库下建一张表 CREATE TABLE t_emp( id BIGINT PRIMA ...

  10. IDEA2017使用Maven方式配置Mybatis-Generator

    一,创建Maven项目 至此Maven项目创建完成. 二,在Maven项目的pom.xml中添加mybatis-generator-maven-plugin插件 创建Maven项目后,打开pom.xm ...