Android基础总结三:BroadcastReceiver总结一

BroadCastReceiver 简介

1.定义

  • BroadCastReceiver(广播接收者) 是Android四大组件之一,与广播发送一起,利用intent机制,用于系统内的信息传递。
  • 源码位于:framework/base/core/java/android.content.BroadcastReceiver.java

2.作用

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

3.实现原理

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

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

  • 模型中有3个角色:

    1.消息订阅者(广播接收者)

    2.消息发布者(广播发布者)

    3.消息中心(AMS,即Activity Manager Service)

这里写图片描述

  • 原理描述

    1.广播接收者通过Binder机制在AMS注册

    2.广播发送者通过Binder机制向AMS发送广播

    3.AMS根据广播发送者的要求,在已注册列表中,找到合适的广播接收者

    寻找依据:IntentFilter / Permission

    4.AMS将广播发送到合适的广告接收者相应的消息循环排队中

    5.广播接收者通过消息循环拿到次广告,并回调onReceive()

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

BroadCastReceiver 使用

1 自定义广播接收者BroadcastReceiver

1.继承BroadcastReceiver类

2.重写抽象方法onReceive()方法

默认情况下,广播接收器运行在UI线程,因此,onReceive方法不能执行耗时操作(不能超过10秒钟),否则将导致ANR。

例子:

public class mBroadcastReceiver extends BroadcastReceiver 
{ 
    @Override 
    public void onReceive(Context context, Intent intent) 
    { 
        //写入接收广播后的操作 
    } 
}

2 注册BroadcastReceiver

注册有两种方式:静态注册、动态注册

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

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

2.2 动态注册####

在代码中通过调用Context的registerReceiver()方法进行动态注册BroadcastReceiver,具体代码如下:

    @Override 
    protected void onResume()
    { 
        super.onResume(); 
        //实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver 
        mBroadcastReceiver = new mBroadcastReceiver(); 
        IntentFilter intentFilter = new IntentFilter(); 
        //设置接收广播的类型 
        intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE); 
        //调用Context的registerReceiver()方法进行动态注册 
        registerReceiver(mBroadcastReceiver, intentFilter);
    } 
    //注册广播后,要在相应位置记得销毁广播 
    //即在onPause() 中unregisterReceiver(mBroadcastReceiver) 
    // 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中 
    // 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。 
    @Override 
    protected void onPause() 
    { 
        super.onPause(); 
        //销毁在onResume()方法中的广播 
        unregisterReceiver(mBroadcastReceiver); 
    }

特别注意

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

原因:

1. 对于动态广播,有注册就必然得有注销,否则会导致内存泄露

重复注册、重复注销也不允许



2. Activity生命周期如下:

Activity生命周期的方法是成对出现的:

  • onCreate() & onDestory()
  • onStart() & onStop()
  • 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死亡前一定会被注销,从而防止内存泄露。

2.3 两种注册方式的区别

这里写图片描述

3 广播发送者向AMS发送广播###

发送方法####

广播使用sendBroadcast(intent)或者sendOrderedBroadcast(intent)发送:

Intent intent= new Intent();
intent.setAction("android.intent.action.HIDE_STATUSBAR");
sendBroadcast(intent);
广播类型####

1.普通广播(Normal Broadcast)

2.系统广播(System Broadcast)

3.有序广播(Ordered Broadcast)

4.粘性广播(Sticky Broadcast)

5.App应用内广播(Local Broadcast)

具体说明如下:

1. 普通广播(Normal Broadcast)

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

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

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

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

若发送广播有相应权限,那么广播接收者也需要相应权限

2. 系统广播(System Broadcast)

  • Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播

  • 每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:
系统操作action
监听网络变化android.net.conn.CONNECTIVITY_CHANGE
关闭或打开飞行模式Intent.ACTION_AIRPLANE_MODE_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
成功安装APKIntent.ACTION_PACKAGE_ADDED
成功删除APKIntent.ACTION_PACKAGE_REMOVED
重启设备Intent.ACTION_REBOOT
屏幕被关闭Intent.ACTION_SCREEN_OFF
屏幕被打开Intent.ACTION_SCREEN_ON
关闭系统时Intent.ACTION_SHUTDOWN
重启设备Intent.ACTION_REBOOT



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

3. 有序广播(Ordered Broadcast)

定义:

  • 发送出去的广播被广播接收者按照先后顺序接收
  • 有序是针对广播接收者而言的

特点:

  • 该广播的级别有级别之分,级别数值是在 -1000 到 1000 之间 , 值越大 , 优先级越高;
  • 同级别接收是先后是随机的,再到级别低的收到广播;
  • 实验现象,在这个方法发来的广播中,代码注册方式中,收到广播先后次序为:注明优先级的、代码注册的、没有优先级的;如果都没有优先级,代码注册收到为最先
  • 先接收的广播接收者可以对广播进行截断(abortBroadcast()),即后接收的广播接收者不再接收到此广播;
  • 先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播
    例子:
public class FirstRecever extends BroadcastReceiver {

    private static final String TAG = "MyReceiver";  
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub

        //先获得传过来的MSG
        String msg = intent.getStringExtra("msg");  
        Log.i(TAG, "FirstRecever:"+msg);  

        //更改广播数据
         Bundle bundle = new Bundle();  
         bundle.putString("msg", msg + "@FirstReceiver");  
         setResultExtras(bundle);  
    }
}

具体使用:

AndroidManifest 注册:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testbroadcast_order_perssion"
    android:versionCode="1"
    android:versionName="1.0" >

    ……
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        ……
        <receiver android:name=".FirstRecever" >
            <intent-filter android:priority="10">
                <action android:name="android.intent.action.MY_BROADCAST"/>
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>

         <receiver android:name=".SecondRecever" >
            <intent-filter android:priority="9">
                <action android:name="android.intent.action.MY_BROADCAST"/>
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
……
    </application>
</manifest>

发送广播:

Intent intent = new Intent("android.intent.action.MY_BROADCAST");  
intent.putExtra("msg", "hello receiver.");  
sendOrderedBroadcast(intent,null); 

有序广播的发送主要是
使用:

sendOrderedBroadcast(intent)

sendOrderedBroadcast(intent,permission)

添加访问权限:

sendOrderedBroadcast(intent, permission)中的第二个参数可以设定有序广播的访问权限:

Intent intent = new Intent("android.intent.action.MY_BROADCAST");  
intent.putExtra("msg", "hello receiver.");  
sendOrderedBroadcast(intent, "harvic.broadcast.permission"); 

这段代码中,我们利用 sendOrderedBroadcast(intent, “harvic.broadcast.perssion”); 发送一个必须拥有”harvic.broadcast.perssion”权限的接收器才能接收到我们的广播;
然后我们要在接收器中加入声明使用权限的代码:

<permission android:name="harvic.broadcast.permission" android:protectionLevel="normal"></permission> 

然后是底部声明,我们要使用这个权限:

<uses-permission  android:name="harvic.broadcast.permission"/>

所以总体的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testbroadcast_order_perssion"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="14" />
    <permission android:name="harvic.broadcast.perssion" android:protectionLevel="normal"></permission> 

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        ……
        <receiver android:name=".FirstRecever" >
            <intent-filter android:priority="10">
                <action android:name="android.intent.action.MY_BROADCAST"/>
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
        ……
    </application>

    <!-- 如果不添加使用权限声明,那么接收器会拒绝接受消息的,所以在Log中不会有任何显示 -->
    <uses-permission  android:name="harvic.broadcast.perssion"/>

</manifest>

4. App应用内广播(Local Broadcast)

背景:

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

冲突:

可能出现的问题:

其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;

其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息;

即会出现安全性 & 效率性的问题

解决方案:

使用App应用内广播(Local Broadcast)

1.App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。

2.相比于全局广播(普通广播),App应用内广播优势体现在:安全性高 & 效率高

具体使用1 – 将全局广播设置成局部广播:

1.注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;

2.在广播发送和接收时,增设相应权限permission,用于权限验证;

3.发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。

通过intent.setPackage(packageName)指定报名

具体使用2 – 使用封装好的LocalBroadcastManager类:

使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了LocalBroadcastManager的单一实例

注:对于LocalBroadcastManager方式发送的应用内广播,只能通过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);

5. 粘性广播(Sticky Broadcast)

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

特别注意

  • 对于不同注册方式的广播接收器回调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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值