Android基础之四大组件-BroadcastReceiver解析

1 介绍

1.1 定义

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

1.2 作用

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

1.3 应用场景

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

2 实现原理

2.1 采用模型

  Android中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。因此,Android将广播的发送者 和 接收者 解耦,使得系统方便集成,更易扩展。

2.2 模型讲解

  模型中有3个角色:消息订阅者(广播接收者)、消息发布者(广播发布者)、消息中心(AMS,即Activity Manager Service)。
  示意图 & 原理如下:
这里写图片描述
图片来源于:Android四大组件:BroadcastReceiver史上最全面解析

2.3 流程

这里写图片描述
图片来源于:Android四大组件:BroadcastReceiver史上最全面解析

3 使用流程

3.1 自定义全局广播接收者BroadcastReceiver

// 继承BroadcastReceivre基类
public class mBroadcastReceiver extends BroadcastReceiver {

  // 复写onReceive()方法,接收到广播后,则自动调用该方法
  @Override
  public void onReceive(Context context, Intent intent) {
        // 默认情况下,广播接收器运行在UI线程,因此onReceive()方法不能执行耗时操作,否则将导致ANR.
        // 写入接收广播后的操作
    }
}

3.2 广播接收器注册

3.2.1 静态注册

(1)属性说明

<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" >

    //用于指定此广播接收器将接收的广播类型
    //本示例中给出的是用于接收网络状态改变时发出的广播
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

(2)注册示例

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

3.2.2 动态注册

// 选择在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);
 }

// 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
// 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
@Override
protected void onPause() {
     super.onPause();
     //销毁在onResume()方法中的广播
     unregisterReceiver(mBroadcastReceiver);
}

3.2.3 两种注册方式的区别这里写图片描述

图片来源于:Android四大组件:BroadcastReceiver史上最全面解析

4 广播发送者向AMS发送广播

4.1 广播的发送

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

4.2 广播的类型

4.2.1 普通广播(Normal Broadcast或无序广播)

  即开发者自身定义intent的广播(最常用)。发送广播使用如下:

Intent intent = new Intent();
//对应BroadcastReceiver中intentFilter的action
intent.setAction(BROADCAST_ACTION);
//发送广播
sendBroadcast(intent);
<receiver 
    //此广播接收者类是mBroadcastReceiver
    android:name=".mBroadcastReceiver" >
    //用于接收网络状态改变时发出的广播
    <intent-filter>
        <action android:name="BROADCAST_ACTION" />
    </intent-filter>
</receiver>

4.2.2 系统广播(System Broadcast)

  Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播。每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:
  具体广播类型:Android常用系统广播
  注:当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播

4.2.3 有序广播(Ordered Broadcast)

(1)定义:发送出去的广播被广播接收者按照先后顺序接收,有序是针对广播接收者而言的。
(2)广播接受者接收广播的顺序规则(同时面向静态和动态注册的广播接受者)

  • 按照Priority属性值从大-小排序;
  • Priority属性相同者,动态注册的广播优先;

(3)特点

  • 接收广播按顺序接收;
  • 先接收的广播接收者可以对广播进行截断,即后接收的广播接收者不再接收到此广播;
  • 先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播。

(4)具体使用:有序广播的使用过程与普通广播非常类似,差异仅在于广播的发送方式

sendOrderedBroadcast(intent);

4.2.4 App应用内广播(Local Broadcast)

(1)背景:Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true)
(2)冲突

  • 其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
  • 其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息,即会出现安全性 & 效率性的问题。

(3)解决方案:使用App应用内广播。App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。相比于全局广播(普通广播),App应用内广播优势体现在:安全性高&效率高。
(4)具体使用(不能静态注册):使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的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)

4.2.5 粘性广播(Sticky Broadcast)

  由于在Android5.0 & API 21中已经失效,所以不建议使用,在这里也不作过多的总结。

5 避免在onReceive()中执行耗时操作

  默认情况下,广播接收器是在主线程执行,因此onReceive()方法不能执行耗时操作,方法耗时超过10秒钟,将导致ANR
  **不能使用子线程来解决 , 因为BroadcastReceiver的生命周期很短 , 子线程可能还没有结束,BroadcastReceiver就先结束了。**BroadcastReceiver一旦结束 , 此时BroadcastReceiver所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 )。如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的。
  可以使用IntentService 、 创 建HandlerThread或者调用Context的registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)方法等方式,在其他Wroker线程执行onReceive的方法。
  Android性能优化之IntentService

6 注意

(1)动态广播最好在Activity的onResume()注册、onPause()注销?
  因为onPause()在Activity销毁前一定会被执行(内存不足要回收Activity占用的资源时,Activity在执行完onPause()方法后就会被销毁),从而保证广播在Activity销毁前一定会被注销,从而防止内存泄露。
(2)一个app被杀掉进程后,是否还能收到广播?
  静态注册的常驻型广播接收器还能接收广播,所以本地广播不能用静态注册。
(3)对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:

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

7 转载链接&参考链接

Android四大组件:BroadcastReceiver史上最全面解析

Android:检测网络状态&监听网络变化

8 项目应用例子

(1)定义广播接收器

public class NetStateReceiver extends BroadcastReceiver {
    private Context mContext;
    private boolean mReceiverRegistered = false;

    public NetStateReceiver(Context context) {
        this.mContext = context;
    }
    @Override
    public void onReceive(final Context context, Intent intent) {
        SharedPreferences prefs = PrefsUtils.getPrefs(context);
        boolean mIsWarn = prefs.getBoolean(Constants.PREFS_IS_WARN_NET, false);

        // 是需要提醒 && wif未链接
        if (!mIsWarn && !Utils.isWifiActive(context) && Utils.isMobileActive(context)) {
            RxBus.get().post(BusEvent.CHECK_NETWORK, 0);
        }
    }

    public void registerNetState() {
        mReceiverRegistered = true;
        IntentFilter filter = new IntentFilter();
        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        mContext.registerReceiver(this, filter);
    }

    public void unRegisterNetState() {
        if (mReceiverRegistered) {
            mReceiverRegistered = false;
            mContext.unregisterReceiver(this);
        }
    }
}

(2)注册广播

private NetStateReceiver mNetStateReceiver;

//在onResume()方法注册
@Override
protected void onResume() {
    if (netWorkStateReceiver == null) {
        netWorkStateReceiver = new NetWorkStateReceiver(getApplicationContext());
    }
    mNetStateReceiver.registerNetState();
    super.onResume();
}

//onPause()方法注销
@Override
protected void onPause() {
    mNetStateReceiver.unRegisterNetState();
    super.onPause();
 }

(3)逻辑处理

@Subscribe(tags = {@Tag(BusEvent.CHECK_NETWORK)})
 public void checkNetWork(Integer type) {
    if (type == 0) {
        if (dialog != null && dialog.isShowing()) {
            return;
        }
        View checkBoxView = View.inflate(this, R.layout.checkbox, null);
        final CheckBox checkBox = (CheckBox) checkBoxView.findViewById(R.id.checkbox);
        checkBox.setText("不再提醒");

        DialogInterface.OnClickListener positive = new DialogInterface.OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
                prefs.edit().putBoolean(Constants.PREFS_IS_WARN_NET, checkBox.isChecked()).apply();
                if (isRunning) {
                    videoView.start();
                    startTimer();
                } else {
                    initAllData();
                }
            }
        };
        DialogInterface.OnClickListener negative = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                finish();
            }
        };
        dialog = new AlertDialog.Builder(this)
                    .setMessage("WIFI网络已断开,继续播放将消耗移动网络流量")
                    .setView(checkBoxView)
                    .setPositiveButton("继续播放", positive)
                    .setNegativeButton("停止播放", negative).create();
        dialog.setCanceledOnTouchOutside(false);
        dialog.show();
    }
}

猜你喜欢

转载自blog.csdn.net/chenliguan/article/details/79184225