编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 1.1App启动流程梳理
  • 1.2ActivityThread分析
  • 1.3Context设计思想
  • 2.1Activity基础介绍
  • 2.2Activity启动流程
  • 2.3Activity布局创建
  • 2.4Activity布局绘制
  • 2.5Service基础介绍
  • 2.6Service启动流程
  • 2.7Receiver广播基础
  • 2.8Receiver深入原理
  • 2.9ContentProvider分析
  • 2.10Fragment实践
  • 2.11Intent深入思考
  • 3.1Paint和Canvas
  • 3.2View的绘制基础
  • 3.3onMeasure流程设计
  • 3.4onLayout流程设计
  • 3.5onDraw流程设计
  • 3.6View工作原理
  • 3.7View刷新设计流程
  • 3.8自定义View控件
  • 3.9自定义ViewGroup控件
  • 4.1Handler基础使用
  • 4.2消息机制流程分析
  • 4.3Handler深度解析
  • 4.4Message深度理解
  • 4.5MessageQueue解析
  • 4.6Looper深度解析
  • 4.7理解Handler同步屏障
  • 4.8ThreadLocal分析
  • 4.9ThreadLocal场景
  • 5.1View事件设计思考
  • 5.2View滑动冲突处理
  • 5.3View事件源码分析
  • 5.4View事件总结案例
  • 6.1DecorView设计思想
  • 6.2视图的载体View
  • 6.3视图管理者Window
  • 6.4窗口管理服务WMS
  • 6.5布局解析者Inflater
  • 7.1AsyncTask深入介绍
  • 7.2HandlerThread设计
  • 7.3IntentService设计
  • 8.1IPC通信方式介绍
  • 8.2AIDL进程间通信
  • 8.7Binder通信机制设计
  • /zh/android/basic/9.1注解设计思想和原理.html
  • 9.2APT技术设计详解
  • 9.3APT多种案例实践

2.8Receiver深入原理

目录介绍

  • 01.广播实现原理
  • 02.使用流程
  • 03.抽象方法onReceive()
  • 04.广播的类型
  • 05.内存泄漏
  • 06.Receiver周期
  • 07.如何耗时操作

01.广播实现原理

  • Android中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。
    • 因此,Android将广播的发送者 和 接收者 解耦,使得系统方便集成,更易扩展
  • 模型中有3个角色:
    • 1.消息订阅者(广播接收者)
    • 2.消息发布者(广播发布者)
    • 3.消息中心(AMS,即Activity Manager Service)
  • 原理描述:
    • 1.广播接收者 通过 Binder机制在 AMS 注册
    • 2.广播发送者 通过 Binder 机制向 AMS 发送广播
    • 3.AMS 根据 广播发送者 要求,在已注册列表中,寻找合适的广播接收者
      • 寻找依据:IntentFilter / Permission
    • 4.AMS将广播发送到合适的广播接收者相应的消息循环队列中;
    • 5.广播接收者通过 消息循环 拿到此广播,并回调 onReceive()
  • 特别注意:
    • 广播发送者 和 广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到;

02.使用流程

  • 具体使用流程如下:

03.抽象方法onReceive()

  • 继承自BroadcastReceivre基类,必须复写抽象方法onReceive()方法
    • 1.广播接收器接收到相应广播后,会自动回调onReceive()方法
    • 2.一般情况下,onReceive方法会涉及与其他组件之间的交互,如发送Notification、启动service等
    • 3.默认情况下,广播接收器运行在UI线程,因此,onReceive方法不能执行耗时操作,否则将导致ANR。
  • 代码范例
    public class CustomBroadcastReceiver extends BroadcastReceiver {
    
      //接收到广播后自动调用该方法
      @Override
      public void onReceive(Context context, Intent intent) {
        //写入接收广播后的操作
        }
    }

04.广播的类型

  • 广播的类型主要分为5类:
    • 普通广播(Normal Broadcast)
    • 系统广播(System Broadcast)
    • 有序广播(Ordered Broadcast)
    • 粘性广播(Sticky Broadcast)
    • App应用内广播(Local Broadcast)
  • ①. 普通广播(Normal Broadcast)
    • 即开发者自身定义intent的广播(最常用)。发送广播使用如下:
    Intent intent = new Intent();
    //对应BroadcastReceiver中intentFilter的action
    intent.setAction(BROADCAST_ACTION);
    //发送广播
    sendBroadcast(intent);
    • 若被注册了的广播接收者中注册时intentFilter的action与上述匹配,则会接收此广播(即进行回调onReceive())。如下mBroadcastReceiver则会接收上述广播
    <receiver 
        //此广播接收者类是mBroadcastReceiver
        android:name=".mBroadcastReceiver" >
        //用于接收网络状态改变时发出的广播
        <intent-filter>
            <action android:name="BROADCAST_ACTION" />
        </intent-filter>
    </receiver>
    • 若发送广播有相应权限,那么广播接收者也需要相应权限
  • ②. 系统广播(System Broadcast)
    • Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播
    • 每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:
    • 注:当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播
  • ③. 有序广播(Ordered Broadcast)
    • 定义:发送出去的广播被广播接收者按照先后顺序接收。有序是针对广播接收者而言的
    • 广播接受者接收广播的顺序规则(同时面向静态和动态注册的广播接受者)
      • 1.按照Priority属性值从大-小排序;
      • 2.Priority属性相同者,动态注册的广播优先;
    • 特点
      • 1.接收广播按顺序接收
      • 2.先接收的广播接收者可以对广播进行截断,即后接收的广播接收者不再接收到此广播;
      • 3.先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播
    • 具体使用
      • 有序广播的使用过程与普通广播非常类似,差异仅在于广播的发送方式:
      sendOrderedBroadcast(intent);
  • ④. App应用内广播(Local Broadcast)
    • 背景
      • Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true)
    • 冲突可能出现的问题:
      • 其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
      • 其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息; 即会出现安全性 & 效率性的问题。
    • 解决方案:使用App应用内广播(Local Broadcast)
      • 1.App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。
      • 2.相比于全局广播(普通广播),App应用内广播优势体现在:安全性高 & 效率高
    • 具体使用1 - 将全局广播设置成局部广播
      • 1.注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
      • 2.在广播发送和接收时,增设相应权限permission,用于权限验证;
      • 3.发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
        • 通过 intent.setPackage(packageName) 指定报名
    • 具体使用2 - 使用封装好的LocalBroadcastManager类
      • 使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了LocalBroadcastManager的单一实例
      • 注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册
      //注册应用内广播接收器
      //步骤1:实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver 
      mBroadcastReceiver = new mBroadcastReceiver(); 
      IntentFilter intentFilter = new IntentFilter(); 
      
      //步骤2:实例化LocalBroadcastManager的实例
      localBroadcastManager = LocalBroadcastManager.getInstance(this);
      
      //步骤3:设置接收广播的类型 
      intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
      
      //步骤4:调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册 
      localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
      
      //取消注册应用内广播接收器
      localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
      
      //发送应用内广播
      Intent intent = new Intent();
      intent.setAction(BROADCAST_ACTION);
      localBroadcastManager.sendBroadcast(intent);
  • ⑤. 粘性广播(Sticky Broadcast)
    • 由于在Android5.0 & API 21中已经失效,所以不建议使用,在这里也不作过多的总结。

06.Receiver周期

  • 进程生命周期
    • 一个进程如果正在执行BroadcastReceiver的onReceive()方法,就会被当做一个前台进程,不易被系统杀死。
    • 当 onReceive() 执行完毕,BroadcastReceiver就不再活跃,其所在进程会被系统当做一个空进程,随时有可能被系统杀死。
  • Receiver 生命周期
    • BroadcastReceiver的生命周期很短,从onReceiver()方法开始执行到结束,为其有效期,之后系统会销毁BraodcastReceiver对象,所以在onReceive方法中执行异步请求操作,很可能请求结果没有返回,BroadcastReceiver就被系统回收了。

07.如何耗时操作

  • 如何正确地在BroadcastReceiver中执行耗时操作:
    • (1)如果要在 BroadcastReceiver 中执行耗时操作,通过创建子线程的方式是不可靠的,因为 BroadcastReceiver 的生命周期很短,一旦结束,其所在进程属于空进程(没有任何活动组件的进程),极易在系统内存不足时优先被杀死,如此,正在工作的子线程也会被杀死。
    • (2)在 BroadcastReceiver 中执行耗时操作,可开启一个 Service 将耗时操作交给 Service ,这样可以提高宿主进程优先级,保证耗时操作执行完成。
  • 如何正确操作
    • 在Android 开发中,一般耗时操作都会尽可能交给 Service 做,如上传图片,因为上传的过程可能应用被置于后台,Activity就会存在被系统回收的可能,如果担心 Service 被回收,还可通过startForeground(int, Notification)提升其优先级。
    • 我们知道,Service是工作在主线程的,所以不能直接在里面执行耗时操作,一般需要开启子线程去做,而IntentService是Service的子类,正是可以用来处理异步请求的,同Service有两个好处:一是其内部开启了一个异步处理工作线程HandlerThread,不用我们自己去 new Thread了;二是不需要考虑什么时候关闭该 Service,当完成所有的任务后会自动关闭。
  • 代码如下所示
    /**
     * 策略二:使用 IntentService处理
     * @param data
     */
    private void sendMsgByIntentService(Context context, String data) {
        Intent intent = new Intent(context, MyIntentService.class);
        intent.setAction("com.yc.startIntentService");
        intent.putExtra("data", data);
        context.startService(intent);
    }
    
    // 该方法是在 HandlerThread异步消息处理线程的消息队列中取出消息进行处理,当所有的msg处理完,messageQueue为空,IntentService 就会调用 stopSelf 方法停止
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "IntentService is handling intent msg");
        if ("com.yc.startIntentService".equals(intent.getAction())) {
            String msg = intent.getStringExtra("data");
    
            Log.d(TAG, Thread.currentThread().getName());
            Log.d(TAG, "Msg received from main handler thread: broadcast send = " + msg);
            // 处理耗时请求
            SystemClock.sleep(5000);
            Log.d(TAG, "Is Broadcast ANR ?");
    
            // 子线程处理消息后,给主线程发消息,说"我执行完了"
            Message mainMsg = new Message();
            mainMsg.what = 0x22;
            mainMsg.obj = "Msg from child handler thread: I have done !";
    
            // 使用广播将处理耗时操作的数据发送给主线程
            Intent intent0 = new Intent(BroadcastTestActivity.ACTION_FROM_INTENTSERVICE);
            intent0.putExtra("data_from_intent_service", "Msg from MyIntentService: I have done !");
            sendBroadcast(intent0);
    
            // 如果消息队列为空,就会直接结束Service,接着执行onDestroyed方法
        }
    }

其他介绍

01.关于博客汇总链接

  • 1.技术博客汇总
  • 2.开源项目汇总
  • 3.生活博客汇总
  • 4.喜马拉雅音频汇总
  • 5.其他汇总

02.关于我的博客

  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/yczbj/activities
  • 简书:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
  • 开源中国:https://my.oschina.net/zbj1618/blog
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
  • segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
  • 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e
贡献者: yangchong211
上一篇
2.7Receiver广播基础
下一篇
2.9ContentProvider分析