写给Android App开发人员看的Android底层知识(6)
(十一)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)的更多相关文章
- 写给Android App开发人员看的Android底层知识(1)
这个系列的文章一共8篇,我酝酿了很多年,参考了很多资源,查看了很多源码,直到今天把它写出来,也是战战兢兢,生怕什么地方写错了,贻笑大方. (一)引言 早在我还是Android菜鸟的时候,有很多技术我都 ...
- 写给Android App开发人员看的Android底层知识(2)
(五)AMS 如果站在四大组件的角度来看,AMS就是Binder中的Server. AMS全称是ActivityManagerService,看字面意思是管理Activity的,但其实四大组件都归它管 ...
- 写给Android App开发人员看的Android底层知识(3)
(七)App启动流程第2篇 书接上文,App启动一共有七个阶段,上篇文章篇幅所限,我们只看了第一阶段,接下来讲剩余的六个阶段,仍然是拿斗鱼App举例子. 简单回顾一下第一阶段的流程,就是Launche ...
- 写给Android App开发人员看的Android底层知识(5)
(十)Service Service有两套流程,一套是启动流程,另一套是绑定流程.我们做App开发的同学都应该知道. 1)在新进程启动Service 我们先看Service启动过程,假设要启动的Ser ...
- 写给Android App开发人员看的Android底层知识(7)
(十二)ContentProvider (1)ContentProvider是什么? ContentProvider,简称CP. 做App开发的同学,尤其是电商类App,对CP并不熟悉,对这个概念的最 ...
- 写给Android App开发人员看的Android底层知识(8)
(十)PMS及App安装过程 PMS,全称PackageManagerService,是用来获取Apk包的信息的. 在前面分析四大组件与AMS通信的时候,我们介绍过,AMS总是会使用PMS加载包的信息 ...
- 写给Android App开发人员看的Android底层知识(4)
(八)App内部的页面跳转 在介绍完App的启动流程后,我们发现,其实就是启动一个App的首页. 接下来我们看App内部页面的跳转. 从ActivityA跳转到ActivityB,其实可以把Activ ...
- 一看就懂的Android APP开发入门教程
一看就懂的Android APP开发入门教程 作者: 字体:[增加 减小] 类型:转载 这篇文章主要介绍了Android APP开发入门教程,从SDK下载.开发环境搭建.代码编写.APP打包等步骤 ...
- 谋哥:App开发人员的苦逼不值得怜悯!!
[谋哥每天一干货,第四十篇] 为什么取这个标题呢?由于昨天一些本来"支持"谋哥的人看到谋哥搞收费VIP群,认为谋哥赚苦逼开发人员的钱非常不道德,且说谋哥我写的东西都 ...
随机推荐
- 学习MVC之租房网站(二)-框架搭建及准备工作
在上一篇<学习MVC之租房网站(一)-项目概况>中,确定了UI+Service的“双层”架构,并据此建立了项目 接下来要编写Common类库.配置AdminWeb和FrontWeb 一.编 ...
- 变态版大鱼吃小鱼-基于pixi.js 2D游戏引擎
之前用CSS3画了一条
- 基于MVC和Bootstrap的权限框架解决方案 二.添加增删改查按钮
上一期我们已经搭建了框架并且加入了列表的显示, 本期我们来加入增删改查按钮 整体效果如下 HTML部分,在HTML中找到中意的按钮按查看元素,复制HTML代码放入工程中 <a class=&qu ...
- ios sqlite3的简单使用
第一:创建表格 //创建表格 -(void)creatTab{ NSString*creatSQL=@"CREATE TABLE IF NOT EXISTS PERSIONFO(ID INT ...
- 转账示例(一):Dao层面实现(本例采用QueryRunner来执行sql语句,数据源为C3P0)
缺点:Dao层面把Service层面的操作完成了,不利于后期的代码修改和重构 1.自行创建C3P0Util account数据库 2.jar包 3.Dao层面 接口: package com.lear ...
- 学习MVC之租房网站(三)-编写实体类并创建数据库
在上一篇<学习MVC之租房网站(二)-框架搭建及准备工作>中,搭建好了项目框架,并配置了EF.Log4Net和进程外Session.接下来会编写Eneity类并采用CodeFirst的方式 ...
- STM32学习笔记(二)——串口控制LED
开发板芯片:STM32F407ZGT6 PA9-USART1_TX,PA10-USART1_RX; PF9-LED0,PF10-LED1; 一.串口1配置过程(不使用串口中断): 1.使能时钟,包括G ...
- 【HDOJ 1085】数学问题,母函数
Holding Bin-Laden Captive! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Ja ...
- effective c++ 思维导图
历时两个多月的时间,终于把effective c++又复习了一遍,比较慢,看的是英文版,之前看的时候做过一些笔记,但不够详细,这次笔者是从头到尾的翻译了一遍,加了一些标题,先记录到word里面,然后发 ...
- 计算机程序的思维逻辑 (82) - 理解ThreadLocal
本节,我们来探讨一个特殊的概念,线程本地变量,在Java中的实现是类ThreadLocal,它是什么?有什么用?实现原理是什么?让我们接下来逐步探讨. 基本概念和用法 线程本地变量是说,每个线程都有同 ...