第一章 四大组件 之 BroadcastReceiver(四)

第一章 四大组件

第四组件 BroadcastReceiver

(1)定义

广播,是一个全局的监听器,属于Android四大组件之一,Android 广播分为两个角色:广播发送者、广播接收者

(2)作用

监听 / 接收 应用 App 发出的广播消息,并 做出响应

(3)应用场景

3.1)Android不同组件间的通信(含 :应用内 / 不同应用之间)
3.2)多线程通信
3.3)与 Android 系统在特定情况下的通信。如:电话呼入时、网络可用时

(4)实现原理

Android中的广播使用了设计模式中的观察者模式:基于消息的发布 / 订阅事件模型
Android将广播的发送者 和 接收者 解耦,使得系统方便集成,更易扩展
模型中有3个角色:
1、消息订阅者(广播接收者)
2、消息发布者(广播发布者)
3、消息中心(AMS,即Activity Manager Service)
在这里插入图片描述
观察者模式:
观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。

(5)使用流程

【附录2:Boardcast Receiver使用流程】

5.1 自定义广播接受者BroadcastReceiver

继承BroadcastReceivre基类,必须复写抽象方法onReceive()方法
1.广播接收器接收到相应广播后,会自动回调 onReceive() 方法
2.一般情况下,onReceive方法会涉及 与 其他组件之间的交互,如发送Notification、启动Service等
3.默认情况下,广播接收器运行在 UI 线程,因此,onReceive()方法不能执行耗时操作,否则将导致ANR

// 继承BroadcastReceivre基类
public class mBroadcastReceiver extends BroadcastReceiver {
    // 复写onReceive()方法
    // 接收到广播后,则自动调用该方法
    @Override
    public void onReceive(Context context, Intent intent) {
        //写入接收广播后的操作
    }
}

如下为监听网络状态变化的广播接收器

    class NetworkChangeReceiver extends BroadcastReceiver{//广播接收器类

        @Override
        public void onReceiver(Context context,Intent intent){

            //这里需要权限,需要在AndroidManifest.xml中进行网络访问权限申请:
            //<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
            ConnectivityManager connectionManager = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();

            if (networkInfo != null && networkInfo.isAvailable()) {

                  //有网
                  Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();

            } else {

                  //无网
                  Toast.makeText(context, "network is unavailable",
                  Toast.LENGTH_SHORT).show();
            }
        }

    }
5.2 广播接收器注册

注册的方式分为两种:静态注册、动态注册

(1)静态注册

使用:在AndroidManifest.xml里通过标签声明
特点:常驻、不受任何组件的生命周期影响(应用程序关闭后,如果有信息广播,程序依旧会被系统调用),耗电,占内存
应用场景:需要时刻监听广播

<receiver
    android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
    //默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
    android:exported=["true" | "false"]
    android:icon="drawable resource"
    android:label="string resource"
    //继承BroadcastReceiver子类的类名(广播具体标识)
    android:name=".mBroadcastReceiver"
    //具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
    android:permission="string"
    //BroadcastReceiver运行所处的进程
    //默认为app的进程,可以指定独立的进程
    //注:Android四大基本组件都可以通过此属性指定自己的独立进程
    android:process="string" >

    //用于指定此广播接收器将接收的广播类型
// IntentFilter翻译成中文就是“意图过滤器”,主要用来过滤隐式意图。当用户进行一项操作的时候,Android系统会根据配置的 “意图过滤器” 来寻找可以响应该操作的组件,服务。
    //本示例中给出的是用于接收网络状态改变时发出的广播
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

当此 App首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。

(2)动态注册

使用:在代码中调用Context.registerReceiver()方法
特点:非常驻、灵活、跟随组件的生命周期变化(组件结束,广播结束。故在组件结束前,必须移除广播接收器)
应用场景
2.1)源码

    // 选择在Activity生命周期方法中的onResume()中注册
    @Override
    protected void onResume(){
        super.onResume();
        // 1. 实例化BroadcastReceiver子类 &  IntentFilter(意图过滤器,接受广播类型)
        mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        // 2. 设置接收广播的类型(网络变化广播)
        intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
        // 3. 动态注册:调用Context的registerReceiver()方法
        registerReceiver(mBroadcastReceiver, intentFilter);
    }
// 注册广播后,要在相应位置记得销毁广播
// 即在onPause() 中unregisterReceiver(mBroadcastReceiver)
// 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
// 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
    @Override
    protected void onPause() {
        super.onPause();
        //销毁在onResume()方法中的广播
        unregisterReceiver(mBroadcastReceiver);
    }
}

2.2)动态广播最好在Activity 的 onResume()注册、onPause()注销。

  1. 动态广播有注册必须有注销,否则会导致内存泄露;重复注册,注销也不允许。
  2. 在onResume()注册、onPause()注销是因为onPause()在App死亡前一定会被执行,从而保证广播在App死亡前一定会被注销,从而防止内存泄露。
5.3 广播发送者向AMS发送广播
(1)广播的发送

广播 是 用”意图(Intent)“标识,定义广播的本质 = 定义广播所具备的“意图(Intent)”
广播发送 = 广播发送者 将此广播的“意图(Intent)”通过sendBroadcast()方法发送出去

(2)广播类型
a)普通广播(Normal Broadcast)

开发者自身定义 intent的广播(最常用)。
1、发送者发送自定义广播

Intent intent = new Intent();
//对应BroadcastReceiver中intentFilter的action
intent.setAction(BROADCAST_ACTION);
//发送广播
sendBroadcast(intent);

2、接受者注册广播

    <receiver
    //此广播接收者类是mBroadcastReceiver
    android:name=".mBroadcastReceiver" >
    //用于接收网络状态改变时发出的广播
    <intent-filter>
        <action android:name="BROADCAST_ACTION" />
    </intent-filter>
</receiver>

若被注册了的广播接收者中注册时intentFilter的action与上述匹配,则会接收此广播(即进行回调onReceive())。

b)系统广播(System Broadcast)

Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播
每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:(部分)

系统操作 Action
监听网络变化 android.net.conn.CONNECTIVITY_CHANGE
电池电量低 Intent.ACTION_BATTERY_LOW
屏幕锁屏 Intent.ACTION_CLOSE_SYSTEM_DIALOGS
屏幕被关闭 Intent.ACTION_SCREEN_OFF
屏幕被打开 Intent.ACTION_SCREEN_ON

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

c)有序广播(Ordered Broadcast)

发送出去的广播被广播接收者按照先后顺序接收(按照Priority属性值从大-小排序)

sendOrderedBroadcast(intent);

有序广播与无序广播区别:

  • 无序广播
    context.sendBroadcast(Intent)方法发送的广播,不可被拦截,当然发送的数据,接收者是不能进行修改的。
  • 有序广播
    context.sendOrderBroadcast(Intent)方法发送的广播,可被拦截,而且接收者是可以修改其中要发送的数据,修改和添加都是可以的,这就意味着优先接收者对数据修改之后,下一个接收者接受的数据是上一个接收者已经修改了的。
d)本地广播(Local Broadcast)

Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true),App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。
(1)将全局广播设置为局部广播
(a)注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
(b)在广播发送和接收时,增设相应权限permission,用于权限验证;
(c)发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。(通过intent.setPackage(packageName)指定报名)
(2) 使用封装好的LocalBroadcastManager类
使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了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);

注:对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的
(3) 本地广播与全局广播的差别
(3.1)定义
全局广播BroadcastReceiver是针对应用间、应用与系统间、应用内部进行通信的一种方式
本地广播LocalBroadcastReceiver仅在自己的应用内发送接收广播,也就是只有自己的应用能收到,数据更加安全广播只在这个程序里,而且效率更高。
(3.2)使用
全局广播:
1)制作intent(可以携带参数)
2)使用sendBroadcast()传入intent;
3)制作广播接收器类继承BroadcastReceiver重写onReceive方法(或者可以匿名内部类啥的)
4)在java中(动态注册)或者直接在Manifest中注册广播接收器(静态注册)使用registerReceiver()传入接收器和intentFilter
5)取消注册可以在OnDestroy()函数中,unregisterReceiver()传入接收器
本地广播:
1)LocalBroadcastReceiver不能静态注册,只能采用动态注册的方式。
2)在发送和注册的时候采用,LocalBroadcastManager的sendBroadcast方法和registerReceiver方法

(6)源码角度分析广播机制

(6.1)系统广播(BoardcastReceiver)源码分析
  • 广播接受者BoardcastReceiver,并重写onReceive()方法,通过Binder 机制在AMS注册
  • 广播发送者 通过Binder 机制向AMS发送广播
  • AMS根据广播发送者要求,在已注册列表中,寻找合适的广播接收器(寻找依据:IntentFilter)并将广播发送到合适的广播接受者相应的消息循环队列中
  • 广播接受者通过消息循环,拿到此广播,并回调onReceive()方法。
    其中广播发送者与广播接受者的执行是异步的,即广播发送者不会关心有无接受者接收&也不确定接受者何时才能接收到。
(6.2)本地广播(LocalBoardcastManager)源码分析
1、LocalBroadcastManager源码

(1)构造函数

public static LocalBroadcastManager getInstance(Context context) {
    synchronized (mLock) {
        if (mInstance == null) {
            mInstance = new LocalBroadcastManager(context.getApplicationContext());
        }
        return mInstance;
    }
}
 
private LocalBroadcastManager(Context context) {
    mAppContext = context;
    mHandler = new Handler(context.getMainLooper()) {
 
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_EXEC_PENDING_BROADCASTS:
                    executePendingBroadcasts();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
}

先看构造函数,单例实现因而私有化构造函数。
注意的是基于主线程的 Looper 新建了一个 Handler,handleMessage中会调用接收器对广播的消息进行处理,也是 LocalBroadcastManager 的核心部分,具体见后面executePendingBroadcasts()介绍。
(2)注册接收器

HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
            = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
HashMap<String, ArrayList<ReceiverRecord>> mActions
            = new HashMap<String, ArrayList<ReceiverRecord>>();
 
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    synchronized (mReceivers) {
        ReceiverRecord entry = new ReceiverRecord(filter, receiver);
        ArrayList<IntentFilter> filters = mReceivers.get(receiver);
        if (filters == null) {
            filters = new ArrayList<IntentFilter>(1);
            mReceivers.put(receiver, filters);
        }
        filters.add(filter);
        for (int i=0; i<filter.countActions(); i++) {
            String action = filter.getAction(i);
            ArrayList<ReceiverRecord> entries = mActions.get(action);
            if (entries == null) {
                entries = new ArrayList<ReceiverRecord>(1);
                mActions.put(action, entries);
            }
            entries.add(entry);
        }
    }
}  

mReceivers 存储广播和过滤器信息,以BroadcastReceiver作为 key,IntentFilter链表作为 value。mReceivers 是接收器和IntentFilter的对应表,主要作用是方便在unregisterReceiver(…)取消注册,同时作为对象锁限制注册接收器、发送广播、取消接收器注册等几个过程的并发访问。
mActions 以Action为 key,注册这个Action的BroadcastReceiver链表为 value。mActions 的主要作用是方便在广播发送后快速得到可以接收它的BroadcastReceiver。
(3)发送广播

public boolean sendBroadcast(Intent intent) {
    synchronized (mReceivers) {
        final String action = intent.getAction();
        final String type = intent.resolveTypeIfNeeded(mAppContext.getContentResolver());
        final Uri data = intent.getData();
        final String scheme = intent.getScheme();
        final Set<String> categories = intent.getCategories();
        ……
        ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
        if (entries != null) {
            if (debug) Log.v(TAG, "Action list: " + entries);
 
            ArrayList<ReceiverRecord> receivers = null;
            for (int i=0; i<entries.size(); i++) {
                ReceiverRecord receiver = entries.get(i);
                if (receiver.broadcasting) {
                    if (debug) {
                        Log.v(TAG, "  Filter's target already added");
                    }
                    continue;
                }
 
                int match = receiver.filter.match(action, type, scheme, data,
                        categories, "LocalBroadcastManager");
                if (match >= 0) {
                    if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
                            Integer.toHexString(match));
                    if (receivers == null) {
                        receivers = new ArrayList<ReceiverRecord>();
                    }
                    receivers.add(receiver);
                    receiver.broadcasting = true;
                } else {
                    ……
                }
            }
 
            if (receivers != null) {
                for (int i=0; i<receivers.size(); i++) {
                    receivers.get(i).broadcasting = false;
                }
                mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                    mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                }
                return true;
            }
        }
    }
    return false;
}

先根据Action从mActions中取出ReceiverRecord列表,循环每个ReceiverRecord判断 filter 和 intent 中的 action、type、scheme、data、categoried 是否 match(intentFilter的match机制),是的话则保存到receivers列表中,发送 what 为MSG_EXEC_PENDING_BROADCASTS的消息,通过 Handler 去处理。
(4)消息处理

Java
private void executePendingBroadcasts() {
    while (true) {
        BroadcastRecord[] brs = null;
        synchronized (mReceivers) {
            final int N = mPendingBroadcasts.size();
            if (N <= 0) {
                return;
            }
            brs = new BroadcastRecord[N];
            mPendingBroadcasts.toArray(brs);
            mPendingBroadcasts.clear();
        }
        for (int i=0; i<brs.length; i++) {
            BroadcastRecord br = brs[i];
            for (int j=0; j<br.receivers.size(); j++) {
                br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
            }
        }
    }
}

private void executePendingBroadcasts() {
    while (true) {
        BroadcastRecord[] brs = null;
        synchronized (mReceivers) {
            final int N = mPendingBroadcasts.size();
            if (N <= 0) {
                return;
            }
            brs = new BroadcastRecord[N];
            mPendingBroadcasts.toArray(brs);
            mPendingBroadcasts.clear();
        }
        for (int i=0; i<brs.length; i++) {
            BroadcastRecord br = brs[i];
            for (int j=0; j<br.receivers.size(); j++) {
                br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
            }
        }
    }
}

以上为消息处理的函数。mPendingBroadcasts转换为数组BroadcastRecord,循环每个receiver,调用其onReceive函数,这样便完成了广播的核心逻辑。
(5)取消注册

public void unregisterReceiver(BroadcastReceiver receiver) {
    synchronized (mReceivers) {
        ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
        if (filters == null) {
            return;
        }
        for (int i=0; i<filters.size(); i++) {
            IntentFilter filter = filters.get(i);
            for (int j=0; j<filter.countActions(); j++) {
                String action = filter.getAction(j);
                ArrayList<ReceiverRecord> receivers = mActions.get(action);
                if (receivers != null) {
                    for (int k=0; k<receivers.size(); k++) {
                        if (receivers.get(k).receiver == receiver) {
                            receivers.remove(k);
                            k--;
                        }
                    }
                    if (receivers.size() <= 0) {
                        mActions.remove(action);
                    }
                }
            }
        }
    }
}

从mReceivers及mActions中移除相应元素。

(1) LocalBroadcastManager 的核心实现实际还是 Handler,只是利用到了 IntentFilter 的 match 功能,至于 BroadcastReceiver 换成其他接口也无所谓,顺便利用了现成的类和概念而已。
(2) 因为是 Handler 实现的应用内的通信,自然安全性更好,效率更高。

2、LocalBroadcastManager总结

本地广播发送的广播只在自身app传播。不必担心隐私数据泄露。
其他app无法对该app发送广播。不必担心安全漏洞的利用。
本地广播更加高效、安全。

  • 高效:因为它内部是通过Handler实现的,它的sendBroadcast()方法含义并非和系统的sendBroadcast()一样,它的sendBroadcast()方法其实就是通过Handler发送了一个Message而已。
  • 安全:既然它是通过Handler实现广播发送的,那么相比系统广播通过Binder机制实现那肯定更加高效,同时使用Handler来实现,别的app无法向我们应用发送该广播,而我们app内部发送的广播也不会离开我们的app。

LocalBroadcast内部协作主要是靠两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要存储待接收的广播对象。

3、LocalBroadcastManager使用

(1)自定义BroadcastReceiver子类LocalBroadcastReceiver

public class LocalBroadcastReceiver extends BroadcastReceiver {
 
    @Override
    public void onReceive(Context context, Intent intent) {
        localMsg.setText(intent.getStringExtra(MSG_KEY));
    }
}

(2)注册接收器

LocalBroadcastReceiver localReceiver = new LocalBroadcastReceiver();
LocalBroadcastManager.getInstance(context).registerReceiver(localReceiver, new IntentFilter(ACTION_LOCAL_SEND));

(3)发送广播

LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(ACTION_LOCAL_SEND));

(4)取消注册

LocalBroadcastManager.getInstance(context).unregisterReceiver(localReceiver);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李一恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值