zoukankan      html  css  js  c++  java
  • 17 个必须掌握的 BroadcastReceiver 知识点「建议收藏」

    Android

    前言


    • 距离上次更新过去一周多了,打破了之前两到三天一更的惯例,主要是要总结的内容太多了。
    • 本篇文章将对 BroadcastReceiver 开发中,可能用到的知识点,可能遇到的问题进行总结。
    • 希望本文能帮助你揭开 Android 开发过程中的难题。

    最后,希望大家都能有所收获,欢迎食用!

    文章目录


    文章目录

    方便大家学习,我在 GitHub 上建立个 仓库


    一、BroadcastReceiver

    • BroadcastReceiver,顾名思义就是“广播接收者”的意思,它是Android四大基本组件之一。
    • 这种组件本质上是一种全局的监听器,用于监听系统全局的广播消息。
    • 它可以接收来自系统和应用的的广播。

    1.1 什么是 BroadcastReceiver

    什么是 BroadcastReceiver

    • 是四大组件之一, 主要用于接收 app 发送的广播
    • 内部通信实现机制:通过 android 系统的 Binder 机制.

    1.2 广播分为两种

    广播分为两种

    1.2.1 无序广播

    无序广播

    • 也叫标准广播,是一种完全异步执行的广播。
    • 在广播发出之后,所有广播接收器几乎都会在同一时刻接收到这条广播消息,它们之间没有任何先后顺序,广播的效率较高。
    • 优点: 完全异步, 逻辑上可被任何接受者收到广播,效率高
    • 缺点: 接受者不能将处理结果交给下一个接受者, 且无法终止广播.

    1.2.2 有序广播

    有序广播

    • 是一种同步执行的广播。
    • 在广播发出之后,同一时刻只有一个广播接收器能够收到这条广播消息,当其逻辑执行完后该广播接收器才会继续传递。
    • 调用 SendOrderedBroadcast() 方法来发送广播,同时也可调用 abortBroadcast() 方法拦截该广播。可通过 <intent-filter> 标签中设置 android:property 属性来设置优先级,未设置时按照注册的顺序接收广播。
    • 有序广播接受器间可以互传数据。
    • 当广播接收器收到广播后,当前广播也可以使用 setResultData 方法将数据传给下一个接收器。
    • 使用 getStringExtra 函数获取广播的原始数据,通过 getResultData 方法取得上个广播接收器自己添加的数据,并可用 abortBroadcast 方法丢弃该广播,使该广播不再被别的接收器接收到。

    总结

    • 总结
    1. 按被接收者的优先级循序传播 A > B > C ,
    2. 每个都有权终止广播, 下一个就得不到
    3. 每一个都可进行修改操作, 下一个就得到上一个修改后的结果.

    1.2.3 最终广播者

    最终广播者

    • Context.sendOrderedBroadcast ( intent , receiverPermission , resultReceiver , scheduler , initialCode , initialData , initialExtras ) 时我们可以指定 resultReceiver 为最终广播接收者.
    • 如果比他优先级高的接受者不终止广播, 那么他的 onReceive 会执行两次
    • 第一次是正常的接收
    • 第二次是最终的接收
    • 如果优先级高的那个终止广播, 那么他还是会收到一次最终的广播

    1.2.4 常见的广播接收者运用场景

    广播接收者运用场景

    • 开机启动, sd 卡挂载, 低电量, 外拨电话, 锁屏等
    • 比如根据产品经理要求, 设计播放音乐时, 锁屏是否决定暂停音乐.

    1.3 BroadcastReceiver 的种类

    1.3.1 广播作为 Android 组件间的通信方式,如下使用场景:

    对前一部分 “ 请描述一下 BroadcastReceiver ” 进行展开补充

    BroadcastReceiver 使用场景

    • APP 内部的消息通信。

    • 不同 APP 之间的消息通信。

    • Android 系统在特定情况下与 APP 之间的消息通信。

    • 广播使用了观察者模式,基于消息的发布 / 订阅事件模型。广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。

    • BroadcastReceiver 本质是一个全局监听器,用于监听系统全局的广播消息,方便实现系统中不同组件间的通信。

    • 自定义广播接收器需要继承基类 BroadcastReceiver ,并实现抽象方法 onReceive ( context, intent ) 。默认情况下,广播接收器也是运行在主线程,因此 onReceiver() 中不能执行太耗时的操作( 不超过 10s ),否则将会产生 ANR 问题。onReceiver() 方法中涉及与其他组件之间的交互时,可以使用发送 Notification 、启动 Service 等方式,最好不要启动 Activity

    1.3.2 系统广播

    系统广播

    • Android 系统内置了多个系统广播,只要涉及手机的基本操作,基本上都会发出相应的系统广播,如开机启动、网络状态改变、拍照、屏幕关闭与开启、电量不足等。在系统内部当特定时间发生时,系统广播由系统自动发出。

    • 常见系统广播 Intent 中的 Action 为如下值:

    常见系统广播 Intent 中的 Action 值

    1. 短信提醒:android.provider.Telephony.SMS_RECEIVED
    2. 电量过低:ACTION_BATIERY_LOW
    3. 电量发生改变:ACTION_BATTERY_CHANGED
    4. 连接电源:ACTION_POWER_CO             
    • Android 7.0 开始,系统不会再发送广播 ACTION_NEW_PICTUREACTION_NEW_VIDEO ,对于广播 CONNECTIVITY_ACTION 必须在代码中使用 registerReceiver 方法注册接收器,在 AndroidManifest 文件中声明接收器不起作用。
    • Android 8.0 开始,对于大多数隐式广播,不能在 AndroidManifest 文件中声明接收器。

    1.3.3 局部广播

    局部广播

    • 局部广播的发送者和接受者都同属于一个 APP
    • 相比于全局广播具有以下优点:
    1. 其他的 APP 不会受到局部广播,不用担心数据泄露的问题。
    2. 其他 APP 不可能向当前的 APP 发送局部广播,不用担心有安全漏洞被其他 APP 利用。
    3. 局部广播比通过系统传递的全局广播的传递效率更高。
    • Android v4 包中提供了 LocalBroadcastManager 类,用于统一处理 APP 局部广播,使用方式与全局广播几乎相同,只是调用注册 / 取消注册广播接收器和发送广播偶读方法时,需要通过 LocalBroadcastManager 类的 getInstance() 方法获取的实例调用。

    1.4 BroadcastReceiver 注册方式

    BroadcastReceiver 注册方式

    1.4.1 静态注册

    AndroidManifest.xml 文件中配置。

    <receiver android:name=".MyReceiver" android:exported="true">
    	<intent-filter>
    		<!-- 指定该 BroadcastReceiver 所响应的 Intent 的 Action -->
            <action android:name="android.intent.action.INPUT_METHOD_CHANGED"
    		<action android:name="android.intent.action.BOOT_COMPLETED" />
    	</intent-filter>
    </receiver>
    
    • 两个重要属性需要关注:

    两个重要属性

    1. android: exported
      其作用是设置此 BroadcastReceiver 能否接受其他 APP 发出的广播 ,当设为 false 时,只能接受同一应用的的组件或具有相同 user ID 的应用发送的消息。这个属性的默认值是由 BroadcastReceiver 中有无 Intent-filter 决定的,如果有 Intent-filter ,默认值为 true ,否则为 false
    2. android: permission
      如果设置此属性,具有相应权限的广播发送方发送的广播才能被此 BroadcastReceiver 所接受;如果没有设置,这个值赋予整个应用所申请的权限。

    1.4.2 动态注册

    • 调用 ContextregisterReceiver ( BroadcastReceiver receiver , IntentFilter filter ) 方法指定。

    1.5 在 Mainfest 和代码如何注册和使用 BroadcastReceiver ? ( 一个 action 是重点 )

    Mainfest 和代码如何注册和使用 BroadcastReceiver

    1.5.1 使用文件注册 ( 静态广播 )

    • 只要 app 还在运行,那么会一直收到广播消息

    使用文件注册 ( 静态广播 )

    • 演示:
    1. 一个 app 里: 自定义一个类继承 BroadcastReceiver 然后要求重写 onReveiver 方法
    public class MyBroadCastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("MyBroadCastReceiver", "收到信息,内容是 : " + intent.getStringExtra("info") + "");
        }
    }
    
    1. 清单文件注册,并设置 Action , 就那么简单完成接收准备工作
    <receiver android:name=".MyBroadCastReceiver">
        <intent-filter>
            <action android:name="myBroadcast.action.call"/>
        </intent-filter>
    </receiver>
    

    1.5.2 代码注册 ( 动态广播 )

    • 当注册的 Activity 或者 Service 销毁了那么就会接收不到广播.

    代码注册 ( 动态广播 )

    • 演示:
    1. 在和广播接受者相同的 app 里的 MainActivity 添加一个注册按钮 , 用来注册广播接收者
    2. 设置意图过滤,添加 Action
    //onCreate创建广播接收者对象
    mReceiver = new MyBroadCastReceiver();              
    
    //注册按钮
    public void click(View view) {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("myBroadcast.action.call");
        registerReceiver(mReceiver, intentFilter);
    }
    
    1. 销毁的时候取消注册
    @Override
    protected void onDestroy() {
        unregisterReceiver(mReceiver);
        super.onDestroy();
    }
    

    1.5.3 在另一个 app , 定义一个按钮, 设置意图, 意图添加消息内容, 意图设置 action( ... ) 要匹配 , 然后发送广播即可.

    代码注册 ( 动态广播 )

    public void click(View view) {
        Intent intent = new Intent();
        intent.putExtra("info", "消息内容");
        intent.setAction("myBroadcast.action.call");
        sendBroadcast(intent);
    }
    
    • 运行两个 app 之后:
    1. 静态注册的方法: 另一 app 直接发广播就收到了
    2. 动态注册的方法: 自己的 app 先代码注册,然后另一个 app 直接发广播即可.-

    1.6 BroadcastReceiver 的实现原理是什么?

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

    BroadcastReceiver 的实现原理

    • 模型中主要有 3 个角色:
    1. 消息订阅者( 广播接收者 )
    2. 消息发布者( 广播发布者 )
    3. 消息中心( AMS,即 Activity Manager Service

    1.6.1 原理:

    原理

    • 广播接收者通过 Binder 机制在 AMSActivity Manager Service ) 注册;

    • 广播发送者通过 Binder 机制向 AMS 发送广播;

    • AMS 根据广播发送者要求,在已注册列表中,寻找合适的 BroadcastReceiver ( 寻找依据:IntentFilter / Permission );

    • AMS 将广播发送到 BroadcastReceiver 相应的消息循环队列中;

    • 广播接收者通过消息循环拿到此广播,并回调 onReceive() 方法。

    • 需要注意的是:广播的发送和接受是异步的,发送者不会关心有无接收者或者何时收到。

    1.7 本地广播

    在这里插入图片描述

    • 本地广播机制使得发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接受来自本应用程序发出的广播,则安全性得到了提高。

    • 本地广播主要是使用了一个 LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。

    • 开发者只要实现自己的 BroadcastReceiver 子类,并重写 onReceive ( Context context, Intetn intent ) 方法即可。

    • 当其他组件通过 sendBroadcast()sendStickyBroadcast()sendOrderBroadcast() 方法发送广播消息时,如果该 BroadcastReceiver 也对该消息“感兴趣”,BroadcastReceiveronReceive ( Context context, Intetn intent ) 方法将会被触发。

    • 使用步骤:

    1. 调用 LocalBroadcastManager.getInstance() 获得实例
    2. 调用 registerReceiver() 方法注册广播
    3. 调用 sendBroadcast() 方法发送广播
    4. 调用 unregisterReceiver() 方法取消注册

    1.7.1 注意事项:

    注意事项

    1. 本地广播无法通过静态注册方式来接受,相比起系统全局广播更加高效。
    2. 在广播中启动 Activity 时,需要为 Intent 加入 FLAG_ACTIVITY_NEW_TASK 标记,否则会报错,因为需要一个栈来存放新打开的 Activity
    3. 广播中弹出 Alertdialog 时,需要设置对话框的类型为 TYPE_SYSTEM_ALERT ,否则无法弹出。
    4. 不要在 onReceiver() 方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当 onReceiver() 方法运行了较长时间而没有结束时,程序就会报错。

    1.8 Sticky Broadcast 粘性广播

    Sticky Broadcast 粘性广播

    • 如果发送者发送了某个广播,而接收者在这个广播发送后才注册自己的 Receiver ,这时接收者便无法接收到刚才的广播

    • 为此 Android 引入了 StickyBroadcast ,在广播发送结束后会保存刚刚发送的广播( Intent ),这样当接收者注册完 Receiver 后就可以继续使用刚才的广播。

    • 如果在接收者注册完成前发送了多条相同 Action 的粘性广播,注册完成后只会收到一条该 Action 的广播,并且消息内容是最后一次广播内容。

    • 系统网络状态的改变发送的广播就是粘性广播。

    1. 粘性广播通过 ContextsendStickyBroadcast ( Intent ) 接口发送,需要添加权限
    2. uses-permission android:name=”android.permission.BROADCAST_STICKY”
    3. 也可以通过 ContextremoveStickyBroadcast ( Intent intent ) 接口移除缓存的粘性广播

    1.9 LocalBroadcastManager 详解

    1.9.1 特点:

    LocalBroadcastManager 特点

    1. 使用它发送的广播将只在自身APP内传播,因此你不必担心泄漏隐私数据;

    2. 其他 APP 无法对你的 APP 发送该广播,因为你的APP根本就不可能接收到非自身应用发送的该广播,因此你不必担心有安全漏洞可以利用;

    3. 比系统的全局广播更加高效。

    1.9.2 源码分析 :

    LocalBroadcastManager 源码分析

    1. LocalBroadcastManager 内部协作主要是靠这两个 Map 集合:MReceiversMActions ,当然还有一个 List 集合 MPendingBroadcasts ,这个主要就是存储待接收的广播对象。

    2. LocalBroadcastManager 高效的原因主要是因为它内部是通过 Handler 实现的,它的 sendBroadcast() 方法含义并非和我们平时所用的一样,它的 sendBroadcast() 方法其实是通过 handler 发送一个 Message 实现的;

    3. 既然它内部是通过 Handler 来实现广播的发送的,那么相比于系统广播通过 Binder 实现那肯定是更高效了,同时使用 Handler 来实现,别的应用无法向我们的应用发送该广播,而我们应用内发送的广播也不会离开我们的应用;

    1.9.3 BroadcastReceiver 安全问题

    BroadcastReceiver 安全问题

    • BroadcastReceiver 设计的初衷是从全局考虑可以方便应用程序和系统、应用程序之间、应用程序内的通信,所以对单个应用程序而言BroadcastReceiver 是存在安全性问题的 ( 恶意程序脚本不断的去发送你所接收的广播 ) 。为了解决这个问题 LocalBroadcastManager 就应运而生了。

    • LocalBroadcastManagerAndroid Support 包提供了一个工具,用于在同一个应用内的不同组件间发送 BroadcastLocalBroadcastManager 也称为局部通知管理器,这种通知的好处是安全性高,效率也高,适合局部通信,可以用来代替 Handler 更新 UI

    1.9.4 广播的安全性

    • Android 系统中的广播可以跨进程直接通信,会产生以下两个问题:
    1. 其他 APP 可以接收到当前 APP 发送的广播,导致数据外泄。
    2. 其他 APP 可以向当前 APP 放广播消息,导致 APP 被非法控制。

    广播的安全性

    • 发送广播
    1. 发送广播时,增加相应的 permission ,用于权限验证。
    2. Android 4.0 及以上系统中发送广播时,可以使用 setPackage() 方法设置接受广播的包名。
    3. 使用局部广播。
    • 接受广播
    1. 注册广播接收器时,增加相应的 permission ,用于权限验证。
    2. 注册广播接收器时,设置 android:exported 的值为false。
    • 使用局部广播
    1. 发送广播时,如果增加了 permission
    2. 那接受广播的 APP 必须申请相应权限,这样才能收到对应的广播,反之亦然。

    1.9.5 使用 BroadcastReceiver 的好处

    使用 BroadcastReceiver 的好处

    1. 因广播数据在本应用范围内传播,你不用担心隐私数据泄露的问题。

    2. 不用担心别的应用伪造广播,造成安全隐患。

    3. 相比在系统内发送全局广播,它更高效。

    1.10 如何让自己的广播只让指定的 app 接收?

    让自己的广播只让指定的 app 接收

    • 在发送广播的 app 端,自定义定义权限, 那么想要接收的另外 app 端必须声明权限才能收到.
    1. 权限, 保护层级是普通正常.
    2. 用户权限
    <permission android:name="broad.ok.receiver" android:protectionLevel="normal"/>
    <uses-permission android:name="broad.ok.receiver" />
    
    1. 发送广播的时候加上权限字符串
    public void click(View view) {
        Intent intent = new Intent();
        intent.putExtra("info", "消息内容");
        intent.setAction("myBroadcast.action.call");
        sendBroadcast(intent, "broad.ok.receiver");
        //sendOrderedBroadcast(intent,"broad.ok.receiver");
    }
    
    1. 其他app接收者想好获取广播,必须声明在清单文件权限
    <uses-permission android:name="broad.ok.receiver"/>
    

    1.11 广播的优先级对无序广播生效吗?

    广播的优先级对无序广播生效

    • 优先级对无序也生效.

    1.12 动态注册的广播优先级谁高?

    动态注册的广播优先级谁高

    • 谁先注册,谁就高

    1.13 如何判断当前的 BrodcastReceiver 接收到的是有序还是无序的广播?

    判断当前的 BrodcastReceiver 接收到的是有序还是无序

    • onReceiver 方法里,直接调用判断方法得返回值
    public void onReceive(Context context, Intent intent) {
        Log.d("MyBroadCastReceiver", "收到信息,内容是 : " + intent.getStringExtra("info") + "");
        boolean isOrderBroadcast = isOrderedBroadcast();
    }
    

    1.14 BroadcastReceiver 不能执行耗时操作

    BroadcastReceiver 不能执行耗时操作

    • 一方面
    1. BroadcastReceiver 一般处于主线程。
    2. 耗时操作会导致 ANR
    • 另一方面
    1. BroadcastReceiver 启动时间较短。
    2. 如果一个进程里面只存在一个 BroadcastReceiver 组件。并且在其中开启子线程执行耗时任务。
    3. 系统会认为该进程是优先级最低的空进程。很容易将其杀死。

    总结


    1. 本文应该是全网最全面的 BroadcastReceiver 知识总结了,如果有什么遗漏的地方,欢迎大家在评论区指出。
    2. 前前后后投入了大量时间来完成。希望大家通过本次阅读都能有所收获。
    3. 重点:关于 Android 的四大组件,到现在为止我才总结完 ActivityServiceBroadcastRecevierContentProvider等,以及事件分发、滑动冲突、新能优化等重要模块,进行全面总结,欢迎大家关注 _yuanhao 的 博客园 ,方便及时接收更新

    码字不易,你的点赞是我总结的最大动力!


    Android

  • 相关阅读:
    pip不是内部或外部命令也不是可运行的程序或批处理文件的问题
    动态规划 leetcode 343,279,91 & 639. Decode Ways,62,63,198
    动态规划 70.climbing Stairs ,120,64
    (双指针+链表) leetcode 19. Remove Nth Node from End of List,61. Rotate List,143. Reorder List,234. Palindrome Linked List
    建立链表的虚拟头结点 203 Remove Linked List Element,82,147,148,237
    链表 206 Reverse Linked List, 92,86, 328, 2, 445
    (数组,哈希表) 219.Contains Duplicate(2),217 Contain Duplicate, 220(3)
    重装系统
    java常用IO
    端口
  • 原文地址:https://www.cnblogs.com/yuanhao-1999/p/11817955.html
Copyright © 2011-2022 走看看