Android四大组件之一: BroadcastReceiver 基本使用与浅析

1.简介

BroadcastReceiver 运用在应用程序组件与组件之间进行通信,可以跨应用程序传递,发送广播内容是一个Intent,这个Intent中可以携带我们要发送的数据。
广播有两个角色,一个是广播发送者,另外一个是广播接收者.

2.使用场景

  • 同一app内有多个进程的不同组件之间的消息通信。
  • 不同app之间的组件之间消息的通信。

3.广播种类

  • 标准广播:context.sendBroadcast(Intent)方法发送的广播,无序,不可被拦截
  • 有序广播:context.sendOrderBroadcast(Intent)方法发送的广播,可被拦截
  • 本地广播:localBroadcastManager.sendBroadcast(Intent),只在app内传播

4.两个注册方式

图解

5.1 使用(标准广播)

  • 静态注册:在manifest.xml文件中注册
    先创建一个广播接接收类 继承 BroadcastReceiver类
    示例:
public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      Toast.makeText(context, "接收到广播", Toast.LENGTH_LONG).show();
    }
}

静态的广播接收器一定要在AndroidManifest.xml文件中注册才可以使用
注意: android7.0 以后android系统取消了一些系统广播,列:网络,拍照,录像
为了适配7.0以后最好使用动态广播

<!-- 申请权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 <!-- 静态广播  为了适配7.0以后最好使用动态广播 -->
        <receiver
            android:name=".broadcastReceiver.MyBroadcastReceiver"
          //属性表示接收者对外部应用程序不可用,即不接受来自外部的广播
            android:exported="false"
            >
            <intent-filter >
                <!-- 用于接收网络状态改变时发出的广播 由于7.0后取消了网络,拍照,录像...静态广播 -->
                <!-- 为了适配所以最好使用动态广播-->
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
                <!-- 自定义广播  -->
                <action android:name="customBroadcast"/>
            </intent-filter>
        </receiver>

  • 动态注册:

动态注册的广播接收器可以自由的控制注册与注销, 在灵活性方面有很大的优势, 但是它必须在程序启动之后才能接收到广播, 因为注册的逻辑写在onCreate()方法中. 那么有没有什么办法可以让程序在未启动的情况下就能接收到广播呢? 这就需要使用静态注册的方式了.

在activity中通过registerReceiver()注册广播,注册广播需要一个IntentFilter,一个广播接收者BroadcastReceiver ,所以创建一个类继承BroadcastReceiver类并重写父类的onReceive()方法就行了.

示例: 这里加了一个自定义广播和一个监听网络状态的系统广播

public class MyBroadcastReceiver extends BroadcastReceiver {
    // 自定义广播
    public static final String CUSTOMBROADCAST = "customBroadcast";
    public static final String CONNECTIVITY_CHANGE = "android.net.conn.CONNECTIVITY_CHANGE";
    // 网络状态码
    public static  int  NetStatus = -1;   // -1:网络异常   0:wifi   1:3G
    NetChangeStatusLinstener statusLinstener;
    public MyBroadcastReceiver() {
    }

    public MyBroadcastReceiver(NetChangeStatusLinstener statusLinstener) {
        this.statusLinstener= statusLinstener;
    }
    

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

        // 自定义广播
        if (CUSTOMBROADCAST.equals(intent.getAction())) {
            String msg = intent.getStringExtra("msg");
            ToastUitl.showShort(msg);
        } else if (CONNECTIVITY_CHANGE.equals(intent.getAction())) {  // 用于接收网络状态改变时发出的广播 系统广播
            if (NetWorkUtils.isNetConnected(context)) {
                if (NetWorkUtils.isWifiConnected(context)) {
//                    ToastUitl.showShort("当前是wifi网络状态!!!");
                    NetStatus = 0;
                    setNetStatus(NetStatus);
                    return;
                }
                if (NetWorkUtils.is3gConnected(context)) {
//                    ToastUitl.showShort("当前3G网络状态");
                    NetStatus = 1;
                    setNetStatus(NetStatus);
                }

            } else {
//                ToastUitl.showShort("网络异常,请检查网络是否异常");
                setNetStatus(NetStatus);
            }
        } else {
            LogUtils.logd("以上条件不满足的广播!!");
        }

    }

    public void setNetStatus(int status){
        if (statusLinstener!=null) {
            statusLinstener.onNetStatusLinstener(status);
        }
    }
  
// 监听网络状态的回调接口
    public interface NetChangeStatusLinstener {
        void onNetStatusLinstener(int status);
    }
}

activity 注册: 并发送广播

public class BroadcastActivity extends BaseActivity {

    private MyBroadcastReceiver myBroadcastReceiver;
    private XWUIRoundButton sendBroadcast;
    
//  实现网络监听接口
    MyBroadcastReceiver.NetChangeStatusLinstener netChangeStatusLinstener     
    = new MyBroadcastReceiver.NetChangeStatusLinstener() {
        @Override
        public void onNetStatusLinstener(int status) {
            switch (status) {
                case -1:
                    ToastUitl.showShort("网络异常,请检查网络是否异常");
                    break;
                case 0:
                    ToastUitl.showShort("当前是wifi网络状态!!!");
                    break;
                case 1:
                    ToastUitl.showShort("当前3G网络状态");
                    break;
            }
        }
    };


    @Override
    protected int initContentView() {
        return R.layout.activity_static_broadcast;
    }

    @Override
    protected void initData() {

    }

    @Override
    protected void initView(Bundle savedInstanceState) {
        sendBroadcast = findViewById(R.id.btn_sendBroadcast);
        setToolBarTitle("广播接收者");
    }


    @Override
    protected void onResume() {
        super.onResume();
        // 意图广播
        IntentFilter intentFilter = new IntentFilter(); //
        // 可以添加多个意图
        intentFilter.addAction(CONNECTIVITY_CHANGE);   // 用于接收网络状态改变时发出的广播 属于系统广播
       // intentFilter.addAction(CUSTOMBROADCAST);            // 自定义广播
        myBroadcastReceiver = new MyBroadcastReceiver(netChangeStatusLinstener);
        // 注册广播
        registerReceiver(myBroadcastReceiver, intentFilter);

    }


    @Override
    protected void onPause() {
        super.onPause();
        if (myBroadcastReceiver != null) {
            // 注销广播
            unregisterReceiver(myBroadcastReceiver);
        }
    }
    // 这是点击按钮发送自定义广播
    public void sendBroadcast(View view) {

        // sendBroadcast方法发送广播
        Intent intent = new Intent();
        // 设置发什么广播  这里填的参数必输是被注册过的广播
        intent.setAction(CUSTOMBROADCAST);
        intent.putExtra("msg", "自定义广播发送成功!!!");
//适配8.0系统静态注册的接收不到消息  参数1:是包名   参数2 : 广播接收者的类
        intent.setComponent(new ComponentName(this,MyBroadcastReceiver.class));
        sendBroadcast(intent);

    }
}

注意:

  • 静态注册和动态注册不能出现一样Action 不然onReceive()方法会执行两次
  • 注:动态广播最好在Activity 的 onResume()注册、onPause()注销。
  • 原因:对于动态广播,有注册就必然得有注销,否则会导致内存泄露

在onResume()注册、onPause()注销是因为onPause()在App死亡前一定会被执行,从而保证广播在App死亡前一定会被注销,从而防止内存泄露。

  • 不在onCreate() & onDestory() 或 onStart() & onStop()注册、注销是因为:
    当系统因为内存不足(优先级更高的应用需要内存)要回收Activity占用的资源时,Activity在执行完onPause()方法后就会被销毁,有些生命周期方法onStop(),onDestory()就不会执行。当再回到此Activity时,是从onCreate方法开始执行。
  • 假设我们将广播的注销放在onStop(),onDestory()方法里的话,有可能在Activity被销毁后还未执行onStop(),onDestory()方法,即广播仍还未注销,从而导致内存泄露。
  • 但是,onPause()一定会被执行,从而保证了广播在App死亡前一定会被注销,从而防止内存泄露。
  • Android系统广播action如下:
系统广播.png
5.2 有序广播

有序广播跟标准广播使用基本一样.
有序广播接受者接收广播的顺序规则是通过Priority(优先级)属性值从大-小排序(1000到-1000)执行,Priority属性相同者,动态注册的广播优先;
有序广播是通过sendOrderedBroadcast()发送广播.

示例:

 <receiver android:name=".broadcastReceiver.MyBroadcastReceiver">
            <intent-filter android:priority="500">
                <!-- 自定义广播 -->
                <action android:name="customBroadcast" />
            </intent-filter>
        </receiver>
        <receiver
            android:name=".broadcastReceiver.MyBroadcastReceiverTwo"
            >
            <intent-filter android:priority="900">
                <!-- 自定义广播 -->
                <action android:name="customBroadcast" />
            </intent-filter>
        </receiver>
//这里通过按钮点击发送广播
public void sendBroadcast(View view) {

        Intent intent = new Intent();
        // 设置发什么广播  这里填的参数必输是被注册过的广播 
        intent.setAction(CUSTOMBROADCAST);
        intent.putExtra("msg", "自定义广播发送成功!!!");

        sendOrderedBroadcast(intent,null);
    }
// 广播接收者1
public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String msg = intent.getStringExtra("msg");
        LogUtils.logd(msg+" : One");
    }
}

// 广播接收者2
public class MyBroadcastReceiverTwo extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String msg = intent.getStringExtra("msg");
        LogUtils.logd(msg+" : Two");
    }
}

最后输出的是

 自定义广播发送成功!!! : Two
 自定义广播发送成功!!! : One

5.3 本地广播

本地广播和全局广播的区别

  • 本地广播:发送的广播事件不被其他应用程序获取,也不能响应其他应用程序发送的广播事件。只在本app中有效,因此不必担心泄漏隐私的数据。本地广播只能被动态注册,不能静态注册。动态注册或方法时需要用到LocalBroadcastManager. 不接收系统广播,更高效.()
  • 全局广播:发送的广播事件可被其他应用程序获取,也能响应其他应用程序发送的广播事件(可以通过 exported–是否监听其他应用程序发送的广播 在清单文件中控制) 全局广播既可以动态注册,也可以静态注册。

内部实现机制:

  • LocalBroadcast高效的原因:因为它内部是通过Handler实现的,它的sendBroadcast()方法含义并非和系统的sendBroadcast()一样,它的sendBroadcast()方法其实就是通过Handler发送了一个Message而已。
  • LocalBroadcast安全的原因:既然它是通过Handler实现广播发送的,那么相比系统广播通过Binder机制实现那肯定更加高效,同时使用Handler来实现,别的app无法向我们应用发送该广播,而我们app内部发送的广播也不会离开我们的app。
  • LocalBroadcast内部协作主要是靠两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要存储待接收的广播对象。

基本使用

    @Override
    protected void onResume() {
        super.onResume();
   // 本地广播
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        // 意图广播
        IntentFilter intentFilter = new IntentFilter(); //
        // 可以添加多个意图 // 自定义广播
        intentFilter.addAction(CUSTOMBROADCAST);            
        myBroadcastReceiver = new MyBroadcastReceiver(netChangeStatusLinstener);
        // 注册本地广播
        localBroadcastManager.registerReceiver(myBroadcastReceiver, intentFilter);
    }
    // 点击按钮发送广播
  public void sendBroadcast(View view) {
        // sendBroadcast方法发送广播
        Intent intent = new Intent();
        // 设置发什么广播  这里填的参数必输是被注册过的广播 
        intent.setAction(CUSTOMBROADCAST);
        intent.putExtra("msg", "自定义广播发送成功!!!");
        sendBroadcast(intent);
        localBroadcastManager.sendBroadcast(intent);

    }

 @Override
    protected void onPause() {
        super.onPause();
        if (localBroadcastManager != null && myBroadcastReceiver!=null) {
            // 注销广播
            localBroadcastManager.unregisterReceiver(myBroadcastReceiver);
        }
    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容