Android四大控件之一:BroadcastReceiver详解

目录

01.什么是广播Broadcast

02.广播Broadcast作用

03.广播Broadcast分类

04.静态注册BroadCast

05.动态注册BroadCast

06.发送本地广播

07.使用私有权限

08.系统广播



01.什么是广播Broadcast

  • 在 Android 系统中,广播(Broadcast)是在组件之间传播数据的一种机制,这些组件可以位于不同的进程中,起到进程间通信的作用。
  • BroadcastReceiver 是对发送出来的Broadcast进行过滤、接受和响应的组件。首先将要发送的消息和用于过滤的信息(Action,Category)装入一个 Intent对象,然后通过调用Context.sendBroadcast()sendOrderBroadcast() 方法把 Intent 对象以广播形式发送出去。 广播发送出去后,所以已注册的 BroadcastReceiver 会检查注册时的 IntentFilter 是否与发送的 Intent 相匹配,若匹配则会调用 BroadcastReceiver 的 onReceiver() 方法
  • 所以当我们定义一个 BroadcastReceiver 的时候,都需要实现 onReceiver() 方法。BroadcastReceiver 的生命周期很短,在执行 onReceiver() 方法时才有效,一旦执行完毕,该Receiver 的生命周期就结束了。
  • BroadcastReceiver的生命周期,从对象调用它开始,到onReceiver方法执行完成之后结束。另外,每次广播被接收后会重新创建BroadcastReceiver对象,并在onReceiver方法中执行完就销毁,如果BroadcastReceiver的onReceiver方法中不能在10秒内执行完成,Android会出现ANR异常。所以不要在BroadcastReceiver的onReceiver方法中执行耗时的操作.

  • 如果需要在BroadcastReceiver中执行耗时的操作,可以通过Intent启动Service来完成。但不能绑定Service。如果我们在Activity中注册了BroadcastReceiver,当这个Activity销毁的时候要主动撤销注册否则会出现异常
     

02.广播Broadcast作用

  • 用于监听 / 接收 应用发出的广播消息,并做出响应
  • 应用场景
    • a.不同组件之间通信(包括应用内 / 不同应用之间)
    • b.与 Android 系统在特定情况下的通信:如当电话呼入时、网络可用时
    • c.多线程通信

03.广播Broadcast分类

  • Android中的广播分为两种类型,标准广播和有序广播
    • 标准广播
      • 标准广播是一种完全异步执行的广播,在广播发出后所有的广播接收器会在同一时间接收到这条广播,之间没有先后顺序,效率比较高,且无法被截断。
    • 有序广播
      • 有序广播是一种同步执行的广播,在广播发出后同一时刻只有一个广播接收器能够接收到, 优先级高的广播接收器会优先接收,当优先级高的广播接收器的 onReceiver() 方法运行结束后,广播才会继续传递,且前面的广播接收器可以选择截断广播,这样后面的广播接收器就无法接收到这条广播了。

04.静态注册BroadCast

  • 静态注册在清单文件配置
    • 即在清单文件中为 BroadcastReceiver 进行注册,使用**< receiver >**标签声明,并在标签内用 < intent-filter > 标签设置过滤器。这种形式的BroadcastReceiver的生命周期伴随着整个应用,如果这种方式处理的是系统广播,那么不管应用是否在运行,该广播接收器都能接收到该广播。
  • 代码如下所示
    • 首先,继承 BroadcastReceiver 类创建一个用于接收标准广播的Receiver,在 onReceive() 方法中取出 Intent 传递来的字符串
    public class NormalReceiver extends BroadcastReceiver {
    
        public NormalReceiver() {
    
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            String msg = intent.getStringExtra("Msg");
            LogUtils.i("广播NormalReceiver------"+msg);
        }
    
    }
    

4.1 发送标准广播

  • 在清单文件中声明的 BroadcastReceiver ,必须包含值为 NORMAL_ACTION 字符串的 action 属性,该广播接收器才能收到以下代码中发出的广播
  • 发送标准广播调用的是 sendBroadcast(Intent) 方法
    public class MainActivity extends AppCompatActivity {
    
        private final String NORMAL_ACTION = "com.example.normal.receiver";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void sendBroadcast(View view) {
            Intent intent = new Intent(NORMAL_ACTION);
            intent.putExtra("Msg", "逗比");
            sendBroadcast(intent);
        }
    
    }
    
  • 在清单文件中注册 BroadcastReceiver
    <application>
       
        <receiver android:name=".NormalReceiver">
            <intent-filter>
                <action android:name="com.example.normal.receiver" />
            </intent-filter>
        </receiver>
        
    </application>

     

4.2 发送有序广播

  • 首先,继承 BroadcastReceiver 类创建三个用于接收有序广播的Receiver,名字依次命名为 OrderReceiver_1、OrderReceiver_2、OrderReceiver_3。
  • 此外,既然 Receiver 在接收广播时存在先后顺序,那么 Receiver 除了能从发送广播使用的 Intent 接收数据外,优先级高的 Receiver 也能在处理完操作后向优先级低的 Receiver 传送处理结果。
    public class OrderReceiver_1 extends BroadcastReceiver {
    
        private final String TAG = "OrderReceiver_1";
    
        public OrderReceiver_1() {
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG, "OrderReceiver_1被调用了");
    
            //取出Intent当中传递来的数据
            String msg = intent.getStringExtra("Msg");
            Log.e(TAG, "OrderReceiver_1接收到的值: " + msg);
    
            //向下一优先级的Receiver传递数据
            Bundle bundle = new Bundle();
            bundle.putString("Data", "(Hello)");
            setResultExtras(bundle);
        }
    }
    
    public class OrderReceiver_2 extends BroadcastReceiver {
    
        private final String TAG = "OrderReceiver_2";
    
        public OrderReceiver_2() {
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG, "OrderReceiver_2被调用了");
    
            //取出上一优先级的Receiver传递来的数据
            String data = getResultExtras(true).getString("Data");
            Log.e(TAG, "从上一优先级的Receiver传递来的数据--" + data);
    
            //向下一优先级的Receiver传递数据
            Bundle bundle = new Bundle();
            bundle.putString("Data", "(叶应是叶)");
            setResultExtras(bundle);
        }
    }
    
    public class OrderReceiver_3 extends BroadcastReceiver {
    
        private final String TAG = "OrderReceiver_3";
    
        public OrderReceiver_3() {
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG, "OrderReceiver_3被调用了");
    
            //取出上一优先级的Receiver传递来的数据
            String data = getResultExtras(true).getString("Data");
            Log.e(TAG, "从上一优先级的Receiver传递来的数据--" + data);
        }
    }

     

  • 在清单文件中对三个 Receiver 进行注册,指定相同的 action 属性值,Receiver 之间的优先级使用 priority 属性来判定,数值越大,优先级越高
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    
    	
        <receiver android:name=".OrderReceiver_1">
            <intent-filter android:priority="100">
                <action android:name="com.example.order.receiver" />
            </intent-filter>
        </receiver>
    	
        <receiver android:name=".OrderReceiver_2">
            <intent-filter android:priority="99">
                <action android:name="com.example.order.receiver" />
            </intent-filter>
        </receiver>
    	
        <receiver android:name=".OrderReceiver_3">
            <intent-filter android:priority="98">
                <action android:name="com.example.order.receiver" />
            </intent-filter>
        </receiver>
    	
    </application>
    
  • 发送有序广播调用的是 **sendOrderedBroadcast(Intent,String)**方法,String参数值在自定义权限时使用,下边会有介绍
    public class MainActivity extends AppCompatActivity {
    
        private final String NORMAL_ACTION = "com.example.normal.receiver";
    
        private final String ORDER_ACTION = "com.example.order.receiver";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void sendBroadcast(View view) {
            Intent intent = new Intent(NORMAL_ACTION);
            intent.putExtra("Msg", "Hi");
            sendBroadcast(intent);
        }
    
        public void sendOrderBroadcast(View view) {
            Intent intent = new Intent(ORDER_ACTION);
            intent.putExtra("Msg", "Hi");
            sendOrderedBroadcast(intent, null);
        }
    
    }
    

05.动态注册BroadCast

  • 动态注册 BroadcastReceiver 是在代码中定义并设置好一个 IntentFilter 对象,然后在需要注册的地方调用 Context.registerReceiver() 方法,调用 Context.unregisterReceiver() 方法取消注册,此时就不需要在清单文件中注册 Receiver 了
  • 这里采用在 Service 中注册广播接收器的形式,分别在注册广播接收器取消注册广播接受器接收到广播时输出Log。
    public class BroadcastService extends Service {
    
        private BroadcastReceiver receiver;
    
        private final String TAG = "BroadcastService";
    
        public BroadcastService() {
        }
    
        @Override
        public void onCreate() {
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(MainActivity.ACTION);
            receiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    Log.e(TAG, "BroadcastService接收到了广播");
                }
            };
            registerReceiver(receiver, intentFilter);
            Log.e(TAG, "BroadcastService注册了接收器");
            super.onCreate();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            unregisterReceiver(receiver);
            Log.e(TAG, "BroadcastService取消注册接收器");
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
    }
    
  • 提供启动服务,停止服务、发送广播的方法
    public class MainActivity extends AppCompatActivity {
    
        public final static String ACTION = "com.example.receiver";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void startService(View view) {
            Intent intent = new Intent(this, BroadcastService.class);
            startService(intent);
        }
    
        public void sendBroadcast(View view) {
            Intent intent = new Intent(ACTION);
            sendBroadcast(intent);
        }
    
        public void stopService(View view) {
            Intent intent = new Intent(this, BroadcastService.class);
            stopService(intent);
        }
    }
    
  • 动态广播最好在Activity的onResume()注册、onPause()注销。
    • 原因:对于动态广播,有注册就必然得有注销,否则会导致内存泄露
    • 重复注册、重复注销也不允许

06.发送本地广播

  • 之前发送和接收到的广播全都是属于系统全局广播,即发出的广播可以被其他应用接收到,而且也可以接收到其他应用发送出的广播,这样可能会有不安全因素
  • 因此,在某些情况下可以采用本地广播机制,使用这个机制发出的广播只能在应用内部进行传递,而且广播接收器也只能接收本应用内自身发出的广播
  • 本地广播是使用 LocalBroadcastManager 来对广播进行管理
    函数 作用
    LocalBroadcastManager.getInstance(this).registerReceiver(BroadcastReceiver, IntentFilter) 注册Receiver
    LocalBroadcastManager.getInstance(this).unregisterReceiver(BroadcastReceiver); 注销Receiver
    LocalBroadcastManager.getInstance(this).sendBroadcast(Intent) 发送异步广播
    LocalBroadcastManager.getInstance(this).sendBroadcastSync(Intent) 发送同步广播
  • 首先,创建一个 BroadcastReceiver 用于接收本地广播
    public class LocalReceiver extends BroadcastReceiver {
    
        private final String TAG = "LocalReceiver";
    
        public LocalReceiver() {
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG, "接收到了本地广播");
        }
        
    }
    
  • 之后就是使用 LocalBroadcastManager 对 LocalReceiver 进行注册和解除注册了
    private LocalBroadcastManager localBroadcastManager;
    
    private LocalReceiver localReceiver;
    
    private final String LOCAL_ACTION = "com.example.local.receiver";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localReceiver = new LocalReceiver();
        IntentFilter filter = new IntentFilter(LOCAL_ACTION);
        localBroadcastManager.registerReceiver(localReceiver, filter);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(batteryReceiver);
        localBroadcastManager.unregisterReceiver(localReceiver);
    }
    
    public void sendLocalBroadcast(View view) {
        Intent intent = new Intent(LOCAL_ACTION);
        localBroadcastManager.sendBroadcast(intent);
    }
    
  • 需要注意
    • 本地广播是无法通过静态注册的方式来接收的,因为静态注册广播主要是为了在程序未启动的情况下也能接收广播,而本地广播是应用自己发送的,此时应用肯定是启动的了

07.使用私有权限

  • 使用动态注册广播接收器存在一个问题,即系统内的任何应用均可监听并触发我们的 Receiver 。通常情况下我们是不希望如此的
  • 解决办法之一是在清单文件中为 < receiver > 标签添加一个 android:exported="false" 属性,标明该 Receiver 仅限应用内部使用。这样,系统中的其他应用就无法接触到该 Receiver 了
    <permission
        android:name="com.example.permission.receiver"
        android:protectionLevel="signature" />
    • 此外,也可以选择创建自己的使用权限,即在清单文件中添加一个 < permission > 标签来声明自定义权限
  • 自定义权限时必须同时指定 protectionLevel 属性值,系统根据该属性值确定自定义权限的使用方式
    属性值 限定方式
    normal 默认值。较低风险的权限,对其他应用,系统和用户来说风险最小。系统在安装应用时会自动批准授予应用该类型的权限,不要求用户明确批准(虽然用户在安装之前总是可以选择查看这些权限)
    dangerous 较高风险的权限,请求该类型权限的应用程序会访问用户私有数据或对设备进行控制,从而可能对用户造成负面影响。因为这种类型的许可引入了潜在风险,所以系统可能不会自动将其授予请求的应用。例如,系统可以向用户显示由应用请求的任何危险许可,并且在继续之前需要确认,或者可以采取一些其他方法来避免用户自动允许
    signature 只有在请求该权限的应用与声明权限的应用使用相同的证书签名时,系统才会授予权限。如果证书匹配,系统会自动授予权限而不通知用户或要求用户的明确批准
    signatureOrSystem 系统仅授予Android系统映像中与声明权限的应用使用相同的证书签名的应用。请避免使用此选项,“signature”级别足以满足大多数需求,“signatureOrSystem”权限用于某些特殊情况
  • 首先,新建一个新的工程,在它的清单文件中创建一个自定义权限,并声明该权限。protectionLevel 属性值设为“signature
    <permission
        android:name="com.example.permission.receiver"
        android:protectionLevel="signature" />
    
    <uses-permission android:name="com.example.permission.receiver" />
  • 然后,发送含有该权限声明的 Broadcast 。这样,只有使用相同证书签名且声明该权限的应用才能接收到该 Broadcast 了
    private final String PERMISSION_PRIVATE = "com.example.permission.receiver";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    public void sendPermissionBroadcast(View view) {
        sendBroadcast(new Intent("Hi"), PERMISSION_PRIVATE);
    }

     

  • 回到之前的工程。首先在清单文件中声明权限
    <uses-permission android:name="com.example.permission.receiver" />

创建一个 BroadcastReceiver

        

public class PermissionReceiver extends BroadcastReceiver {

    private final String TAG = "PermissionReceiver";

    public PermissionReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "接收到了私有权限广播");
    }
}
  • 然后注册广播接收器。因为 AndroidStudio在调试的时候会使用相同的证书为每个应用签名,所以,在之前新安装的App发送出广播后,PermissionReceiver 就会输出 Log 日志
    private final String PERMISSION_PRIVATE = "com.example.permission.receiver";
    
    private PermissionReceiver permissionReceiver;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        IntentFilter intentFilter1 = new IntentFilter("Hi");
        permissionReceiver = new PermissionReceiver();
        registerReceiver(permissionReceiver, intentFilter1, PERMISSION_PRIVATE, null);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(permissionReceiver);
    }
    

    08.系统广播

  • Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播
  • 每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:
系统操作 action
监听网络变化 android.net.conn.CONNECTIVITY_CHANGE
关闭或打开飞行模式 Intent.ACTION_AIRPLANE_MODE_CHANGED
充电时或电量发生变化 Intent.ACTION_BATTERY_CHANGED
电池电量低 Intent.ACTION_BATTERY_LOW
电池电量充足(即从电量低变化到饱满时会发出广播 Intent.ACTION_BATTERY_OKAY
系统启动完成后(仅广播一次) Intent.ACTION_BOOT_COMPLETED
按下照相时的拍照按键(硬件按键)时 Intent.ACTION_CAMERA_BUTTON
屏幕锁屏 Intent.ACTION_CLOSE_SYSTEM_DIALOGS
设备当前设置被改变时(界面语言、设备方向等) Intent.ACTION_CONFIGURATION_CHANGED
插入耳机时 Intent.ACTION_HEADSET_PLUG
未正确移除SD卡但已取出来时(正确移除方法:设置–SD卡和设备内存–卸载SD卡) Intent.ACTION_MEDIA_BAD_REMOVAL
插入外部储存装置(如SD卡) Intent.ACTION_MEDIA_CHECKING
成功安装APK Intent.ACTION_PACKAGE_ADDED
成功删除APK Intent.ACTION_PACKAGE_REMOVED
重启设备 Intent.ACTION_REBOOT
屏幕被关闭 Intent.ACTION_SCREEN_OFF
屏幕被打开 Intent.ACTION_SCREEN_ON
关闭系统时 Intent.ACTION_SHUTDOWN
重启设备 Intent.ACTION_REBOOT

注:当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播

 

本文参考了好几篇关于BroadcastReceiver的博客和Demo,用于个人复习总结和分享,若有不足之处,欢迎指正。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章