美文网首页Android
Android基础:BroadcastReceiver —— 实

Android基础:BroadcastReceiver —— 实

作者: Marker_Sky | 来源:发表于2017-11-02 18:33 被阅读335次
    BroadcastReceiver目录.png

    一. BroadcastReceiver简介

    1.1 BroadcastReceiver定义(What?)

    Android四大组件之一,是一种全局的监听器。

    1.2 BroadcastReceiver作用(Why?)

    • 监听系统或应用发出(或接收)的广播信息,然后根据相应信息做逻辑处理。

    常用监听系统广播信息如有电话打来时、网络状态发生变化时、手机电量发生变化时等。

    • 发送或接收少量或发送频率较低的数据。

    因为使用BroadcastReceiver发送/接收大量数据开销较大,且由于其消息是异步的,所以有可能发生数据接收不到的情况。

    • 其它作用:
    1. 不同组件间的通信,包括同应用和不同应用间。如常用的ActivityService间通信。
    2. 多线程间通信。

    二. 广播(Broadcast)的分类及使用(How?)

    Android中广播主要可以分为五类:

    • 普通广播(Normal Broadcast)
    • 有序广播(Ordered Broadcast)
    • 系统广播(System Broadcast)
    • 本地广播(Local Broadcast)
    • 粘性广播(Sticky Broadcast)

    2.1 普通广播(Normal Broadcast)

    普通广播也就是开发者常用的自定义的广播,使用步骤:

    1. 首先需要一个接收广播的类,BroadcastReceiver是一个抽象类,抽象方法onReceive()必须实现:
    public class MyBroadcastReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context,"MyBroadcastReceiver Get Message",Toast.LENGTH_SHORT).show();
        }
    }
    
    1. 如同其它的组件一样,自定义的BroadcastReceiver也需要在manifest中进行注册,其中<intent-filter>中的<action用于指定该receiver要接收的广播的类型,类型不对就接收不到了。此外还可以动态注册,后面再说:
        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.sky.intent.action.MYBROADCASTRECEIVER"/>
            </intent-filter>
        </receiver>
    

    android:enabled="true"表示是否可用
    android:exported="true"表示是否接收其它应用发送的广播,如果该receiver有<intent-filter>属性,则默认为true,如果没有,则android:exported默认为false。

    1. 然后需要发送广播让MyBroadcastReceiver来接收,简单地定义一个按钮,点击设置Intent对象的action并发送广播。
        <Button
            android:onClick="sendBroadcast"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="发送广播"/>
    

    这里设置Intent的action必须与MyBroadcastReceiver定义的action保持一致,如同其它组件运行一样直接调用。

        public static String ACTION_MYBROADCAST = "com.sky.intent.action.MYBROADCASTRECEIVER";
        ...
        public void sendBroadcast(View view){
            Intent intent = new Intent(ACTION_MYBROADCAST);
            sendBroadcast(intent);
        }
    

    2.2 有序广播(Normal Broadcast)

    定义:顾名思义,就是有顺序的广播,但是要注意的是这里的有序是对广播接收器而言的。也就是说广播的发送并没有顺序,接收者可以设定优先级的高低。
    特点:
    1、广播接收器按照优先级的高低来收到广播,高优先级的接收者先收到广播;
    2、中途可以中断广播的向下传递;
    3、高优先级的receiver可以修改向下传递的广播的内容。
    使用:

    1. manifest中的<receiver中设定广播接收器的优先级,重要的是android:priority="100"
        <intent-filter android:priority="100">
            <action android:name="com.sky.intent.action.MYBROADCASTRECEIVER"/>
        </intent-filter>
    

    然后再定义一个MyBroadcastReceiver2,重写onReceive()并设定优先级android:priority="99"

    1. 发送有序广播时使用以下方法:
        public void sendMyOrderdBroadcast(View view){
            Intent intent = new Intent(ACTION_MYBROADCAST);
            // 第二个参数为receiverPermission
            // 是一个字符串权限,设定后广播接收器注册该权限后才能收到
            sendOrderedBroadcast(intent,null);
        }
    

    这样就可以弹出两次Toast信息。

    1. 拦截广播:在优先级较高的广播接收器MyBroadcastReceiveronReceive方法中调用abortBroadcast()方法。
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context,"MyBroadcastReceiver Get Message",Toast.LENGTH_SHORT).show();
            abortBroadcast();
        }
    

    就可以发现Toast只弹出一次,说明广播被拦截了。

    1. 修改向下传递的广播内容:

    (1) MyBroadcastReceiveronReceive方法中接收信息,然后可以重新设置Bundle对象传递的信息。

        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context,"MyBroadcastReceiver Get Message",Toast.LENGTH_SHORT).show();
            //获取处理的的广播,普通广播不能获取处理
            //true代表如果前面的接收器没有存放数据,则自动创建一个空的Bundle对象
            //false则表示如果前面的接收器如果没有存放任何数据则返回null。
            Bundle bundle = getResultExtras(true);
            bundle.putString("message","Modify Message");
            setResultExtras(bundle);
        }
    

    (2) MyBroadcastReceiver2onReceive中直接获取信息。这样就完成了信息的修改传递。

        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle bundle = getResultExtras(true);
            Toast.makeText(context,"MyBroadcastReceiver2 Get Message"+bundle.get("message"),Toast.LENGTH_SHORT).show();
        }
    

    2.3 系统广播(System Broadcast)

    定义:Android系统内置各种广播,当手机状态发生变化时系统会发送广播。监听系统广播然后可以进行逻辑处理。
    使用:设置广播接收器intent-filter的不同action,可以接收到系统不同状态所发送的广播。
    例如下方是监听系统网络变化接收广播,另外要注意申请相应权限:

        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
        ...
        <receiver android:name=".MySysBroadcastReceiver">
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
            </intent-filter>
        </receiver>
    
        @Override
        public void onReceive(Context context, Intent intent) {
            //**判断当前的网络连接状态是否可用*/
            ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo info = connectivityManager.getActiveNetworkInfo();
            if ( info != null && info.isAvailable()){
                //当前网络状态可用
                Toast.makeText(context,"网络连接已恢复",Toast.LENGTH_SHORT).show();//封装的Toast
            }else {
                //当前网络不可用
                Toast.makeText(context,"无网络连接",Toast.LENGTH_SHORT).show();//封装的Toast
            }
        }
    

    不同的action对应不同的系统状态,列举如下:

    系统操作 action name
    监听网络变化 android.net.conn.CONNECTIVITY_CHANGE
    关闭或打开飞行模式 android.intent.action.AIRPLANE_MODE
    充电时或电量发生变化 android.intent.action.BATTERY_CHANGED
    电池电量低 android.intent.action.BATTERY_LOW
    电池电量充满 android.intent.action.BATTERY_OKAY
    系统启动完成后(开机完成) android.intent.action.BOOT_COMPLETED
    按下拍照按键 android.intent.action.CAMERA_BUTTON
    设备当前设置被改变时(界面语言、设备方向等) android.intent.action.CONFIGURATION_CHANGED
    插入耳机时 android.intent.action.HEADSET_PLUG
    未正确移除SD卡但已取出来时(正确移除方法:设置--SD卡和设备内存--卸载SD卡) android.intent.action.MEDIA_BAD_REMOVAL
    插入外部储存装置(如SD卡) android.intent.action.MEDIA_CHECKING
    成功安装APK android.intent.action.PACKAGE_ADDED
    成功删除APK android.intent.action.PACKAGE_REMOVED
    屏幕关闭 android.intent.action.SCREEN_OFF
    屏幕打开 android.intent.action.SCREEN_ON
    用户解锁 android.intent.action.USER_UNLOCKED
    用户锁屏 android.intent.action.USER_PRESENT
    重启设备 android.intent.action.REBOOT
    关闭系统时 android.intent.action.ACTION_SHUTDOWN

    2.4 本地广播(Local Broadcast)

    定义:Android引入的一套本地广播机制,使用该机制发出的广播只能在应用程序内部进行传递,并且广播也只能是APP内部的BroadcastReceiver的接收。
    优点:

    • 高效率:本地广播的发送和接收都只在当前App进行。
    • 安全性高:全局广播存在许多问题,比如携带数据的广播有可能被其它应用截获、其它应用发送垃圾广播影响自身应用运行,而本地广播则没有这些问题。

    使用:

    • 全局广播设置为本地广播:
    1. 注册广播时,将exported属性设置为false,即不予其它应用程序产生联系。
    2. 发送和接收广播时设置权限permission。
    sendBroadcast(intent,"com.sky.broadcast_test.permission");
    

    如果发送的broadcast带有permission,那么只有在那些manifest文件中包含了<uses-permission>的receiver才能够接受到;高版本需要设置权限级别。

        <uses-permission android:name="com.sky.broadcast_test.permission"/>
        <permission
            android:name="com.sky.broadcast_test.permission"
            android:protectionLevel="normal" />
    

    如果在<receiver>中设置了<android:permission="">,那么只有在那些manifest文件中包含了<uses-permission>的broadcast才能够发送给它。

    1. 发送广播时指定广播接收器所在的包名。
    intent.setPackage("packageName");
    
    • 使用LocalBroadcastManager来发送本地广播。步骤:
    1. 自定义一个广播,这里以内部类的形式写到了MainActivity
        class MyLocalBroadcastReceiver extends BroadcastReceiver{
            @Override
            public void onReceive(Context context, Intent intent) {
                Toast.makeText(context,"MyLocalBroadcastReceiver Get Message",Toast.LENGTH_SHORT).show();
            }
        }
    
    1. 获取LocalBroadcastManager实例,创建自定义广播对象,动态注册广播。
        // 获取LocalBroadcastManager实例
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
    
        // 设置IntentFilter的action
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_MY_LOCAL_BROADCAST);
    
        // 动态注册广播
        myLocalBroadcastReceiver = new MyLocalBroadcastReceiver();
        localBroadcastManager.registerReceiver(myLocalBroadcastReceiver,intentFilter);
    
    1. 使用localBroadcastManager发送广播。
        public void sendMyLocalBroadcast(View view){
            // 使用localBroadcastManager发送广播
            Intent intent = new Intent(ACTION_MY_LOCAL_BROADCAST);
            localBroadcastManager.sendBroadcast(intent);
        }
    
    1. 解除注册,防止内存泄漏。
      @Override
        protected void onDestroy() {
            super.onDestroy();
            // 解除注册,预防内存泄漏
            localBroadcastManager.unregisterReceiver(myLocalBroadcastReceiver);
        }
    

    2.5 粘性广播(Sticky Broadcast)

    被废弃,Android5.0 & API 21已经失效,不用管它。

    三. BroadcastReceiver两种注册方式

    3.1 静态注册

    即在清单文件manifest中通过声明<receiver>注册。<receiver有很多属性,下面列表说明:

    属性名称 作用
    android:name="" 类名
    android:enabled = [ true / false] 是否可用
    android:exported= [ true / false] 此接收器是否接收其它应用广播,包含<intent-filter>标签默认为true
    android:icon="" 图标
    android:label="" 标签名称
    android:permission="" 权限,具有该权限的发送者发送的广播才能被它接收
    android:process="" Android四大组件都可以通过这个属性指定独立进程
    <intent-filter> 过滤器

    <intent-filter>中可以指定<action android:name=""/>用来作发送和接收的标记。

    3.2 动态注册

    就是在代码中注册BroadcastReceiver,具体步骤举例:

        // 1.实例化自定义广播,IntentFilter
        MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        // 2.设置IntentFilter的action
        intentFilter.addAction("Your Action");
        // 3.使用Context的registerReceiver动态注册广播
        registerReceiver(myBroadcastReceiver,intentFilter);
        ...
        // 4.解除注册,防止内存泄漏
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unregisterReceiver(myBroadcastReceiver);
        }
    
    

    3.3 两种注册方式的区别

    这里借用一张总结的比较好的图来说明:

    两种注册方式的区别.png

    四. BroadcastReceiver原理

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

    因此,Android将广播的发送者 和 接收者 解耦,使得系统方便集成,更易扩展。

    • 模型中有三个角色:
    1. 消息订阅者(BroadcastReceiver)
    2. 消息发布者(广播发布者)
    3. 消息中心(AMS,即Activity Manager Service
    BroadcastReceiver原理示意.png
    • 原理描述:
    1. 广播接收者(BroadcastReceiver) 通过Binder机制在AMS注册
    2. 广播发送者 通过Binder机制向AMS发送广播
    3. AMS根据 广播发送者 要求,在已注册列表中,寻找合适的广播接收者

    寻找依据:IntentFilter / Permission

    1. AMS将广播发送到合适的广播接收者相应的消息循环队列中;
    2. 广播接收者通过 消息循环 拿到此广播,并回调onReceive()

    特别注意:广播发送者 和 广播接收者的执行 是 异步 的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到;

    五. BroadcastReceiver回调Context

    对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:

    • 对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
    • 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
    • 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。
    • 对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;

    测试项目地址。
    参考资料:

    Android四大组件:BroadcastReceiver史上最全面解析
    《第一行代码(第二版》

    相关文章

      网友评论

        本文标题:Android基础:BroadcastReceiver —— 实

        本文链接:https://www.haomeiwen.com/subject/orgvyxtx.html