望远山,知近路,而后自得其乐!

BroadcastReceiver使用介绍

BroadcastReceiver 介绍

BroadcastReceiver是Android开发4大组件之一。正如其名BroadcastReceiver作为广播接收者,用来监听系统全局的广播消息。广播(Broadcast)在Android系统中是在各组件之间传播数据的一种机制,这些组件可以位于不同的进程中。作为一个全局的广播监听组件,BroadcastReceiver可以很容易的实现系统中不同组件、不同应用之间的通信。

BroadcastReceiver 是对发送出来的 Broadcast 进行过滤、接受和响应的组件。而广播则被装入一个 Intent 对象,然后通过调用系统提供的 Context.sendBroadcast()Context.sendOrderBroadcast() 将该 Intent 对象已广播的形式发送出去。发出的Intent对象需要包括一个action信息,Category信息以及一下其他extra信息。广播发出去之后,所有注册了该action的 BroadcastReceiver 则会收到发出的广播。BroadcastReceiver 通过检查接收的广播的action,Category等信息进行相应的处理。

BroadcastReceiver 接收广播的处理十分简单,在应用开发中,我们只用要重写BroadcastReceiver的onReceive(Context context, Intent intent)方法就可以。

class MyBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "MyBroadcastReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "Receiver Broadcast: " + intent.getAction());
    }
}

BroadcastReceiver 的注册

  1. 静态注册
    静态注册即在 AndroidManifest.xml 清单文件中为 BroadcastReceiver 进行注册,使用 < receiver > 标签声明,并在标签内用 < intent-filter > 标签设置过滤器。这种形式的 BroadcastReceiver 的生命周期伴随着整个应用,如果这种方式处理的是系统广播,那么不管应用是否在运行,该广播接收器都能接收到该广播(非系统app需要运行过一次,且有权限接收该广播)。
<receiver android:name=".MyBroadcastReceiver">  
    <intent-filter>  
        <action android:name="android.intent.action.test"/>  
    </intent-filter>  
</receiver>
  1. 动态注册
    动态注册 BroadcastReceiver 是在代码中定义并设置好一个 IntentFilter 对象,然后在需要注册的地方调用 Context.registerReceiver() 方法,调用Context.unregisterReceiver() 方法取消注册
MyBroadcastReceiver mReceiver = new MyBroadcastReceiver();          
IntentFilter filter = new IntentFilter();  
filter.addAction("android.intent.action.test"); 

registerReceiver(mReceiver, filter); 
unregisterReceiver(mReceiver);

广播发送

发送广播有两种方式:

  • sendBroadcast 普通广播
  • sendOrderedBroadcast 有序广播
  1. 普通广播
private static final String ACTION = "android.intent.action.test";
Intent intent = new Intent(ACTION);
sendBroadcast(intent);

发送普通广播,对于多个注册了该action的广播,都能一块接收到,并没有接收的先后顺序。也没有办法阻止另一个接收者接收这个广播。

  1. 有序广播
private static final String ACTION = "android.intent.action.test";
Intent intent = new Intent(ACTION);
sendOrderedBroadcast(intent, null);

发送有序广播, 有多个广播接收者,则按顺序接收广播,高优先级的先收到,然后是低优先级的。优先级一般在注册的时候通过 priority 属性来进行设置,优先级从-1000~1000,数越大,优先级越高。另外。优先级高的 Receiver 也能在处理完操作后向优先级低的 Receiver 传送处理结果。此外,高优先级的 BroadcastReceiver 也能调用 abortBroadcast() 方法截断广播,这样低优先级的广播接收器就无法接收到广播了。

<receiver android:name=".MyBroadcastReceiver1">  
    <intent-filter android:priority="100">  
        <action android:name="android.intent.action.test"/>  
    </intent-filter>  
</receiver>
<receiver android:name=".MyBroadcastReceiver2">  
    <intent-filter android:priority="99"> 
        <action android:name="android.intent.action.test"/>  
    </intent-filter>  
</receiver>
class MyBroadcastReceiver1 extends BroadcastReceiver {
    private static final String TAG = "MyBroadcastReceiver1";
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.eq("android.intent.action.test")) {
            String id = intent.getStringExtra("id");
            Log.d(TAG, "Receiver Broadcast: " + action + ", id = " + id);
            Bundle bundle = new Bundle();
            bundle.putString("data", "hello, world!");
            setResultExtras(bundle);
        }
    }
}
class MyBroadcastReceiver2 extends BroadcastReceiver {
    private static final String TAG = "MyBroadcastReceiver2";
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.eq("android.intent.action.test")) {
            String id = intent.getStringExtra("id");
            Log.d(TAG, "Receiver Broadcast: " + action + ", id = " + id);
            String data = getResultExtras(true).getString("data");
            Log.d(TAG, "getResultExtras data: " + data);
        }
    }
}

上面的例子中,MyBroadcastReceiver2即可接收的MyBroadcastReceiver1处理结果。

sendOrderedBroadcast(Intent intent, String receiverPermission)方法中的第二个参数用来设置接收者是否需要申请权限。如果参数为null,则接收者不需要申请权限就可以接收广播。如果不为空则需要申请权限,否则接收不到广播。

Intent intent = new Intent("android.intent.action.test");
intent.putExtra("id", 100);
sendOrderedBroadcast(intent, "test.permission.broadcast");

接收端需要申请权限才可以接收到该广播。

<permission android:protectionLevel="normal" android:name="test.permission.broadcast"/>
<uses-permission android:name="test.permission.broadcast" />

本地广播

前面说到的广播全都是属于系统全局广播,即发出的广播后可以被其他应用接收到,而且也可以接收到其他应用发送出的广播,这样可能会有不安全因素。因此,在某些情况下可以采用本地广播机制,使用这个机制发出的广播只能在应用内部进行传递,而且广播接收器也只能接收本应用内自身发出的广播。

本地广播拥有以下特点:

  1. 发送的广播只会在当前APP中传播,不会泄露给其它APP,确保了数据传输的安全。
  2. 其它APP的广播无法发送到本APP中,不用担心安全漏洞被其它APP所利用。
  3. 比系统全局广播更加高效。

本地广播是使用 LocalBroadcastManager 来对广播进行管理,LocalBroadcastManager是support V4包中的一个组件。

函数作用
LocalBroadcastManager.getInstance(this).registerReceiver(BroadcastReceiver, IntentFilter)注册Receiver
LocalBroadcastManager.getInstance(this).unregisterReceiver(BroadcastReceiver);注销Receiver
LocalBroadcastManager.getInstance(this).sendBroadcast(Intent)发送异步广播
LocalBroadcastManager.getInstance(this).sendBroadcastSync(Intent)发送同步广播

本地广播的接收者与全局广播是一样的。只是注册和发送的方式改变了而已。另外,本地广播是无法通过静态注册的方式来接收的,因为静态注册广播主要是为了在程序未启动的情况下也能接收广播,而本地广播是应用自己发送的,此时应用肯定是启动的了。

public class TestActivity extends Activity {
    private LocalBroadcastManager mLocalBroadcastManager;

    private LocalReceiver mLocalReceiver;

    private final String LOCAL_ACTION = "android.intent.action.local_test";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
        mLocalReceiver = new LocalReceiver();
        IntentFilter filter = new IntentFilter(LOCAL_ACTION);
        mLocalBroadcastManager.registerReceiver(mLocalReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mLocalBroadcastManager.unregisterReceiver(mLocalReceiver);
    }
    
    public void sendLocalBroadcast() {
        Intent intent = new Intent(LOCAL_ACTION);
        mLocalBroadcastManager.sendBroadcast(intent);
    }

    public static class LocalReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("LocalReceiver", "onReceive : " + intent.getAction());
        }
    }
}

生命周期

BroadcastReceiver 的生命周期与其他系统组件还是有些不一样的。在 AndroidManifest.xml 中注册的BroadcastReceiver , 每次收到一个 Intent , 也就是 onReceive 被回调的时候, 这个 BroadcastReceiver都是新创建出来的。也就是说, 出了 onReceive , 这个 BroadcastReceiver对象的生命周期就已经到头了, 这也是为什么我们不能在 onReceive 中进行一些异步操作的原因, 有可能异步操作还没完成, BroadcastReceiver所在的进程就被kill了。 所以,我们在接受到广播后,需要进程执行一些异步任务是,需要配合 Service 来实现。

而动态注册的 BroadcastReceiver 对象的生命其实是受我们控制的。既然是动态注册,那么我们的应用一定是处于运行状态的。此时,只要该进程不被kill掉,被注册的 BroadcastReceiver 都是在注册指定的实例中处理接收的广播数据的。

使用自定义权限

使用全局广播接收器存在一个问题,即系统内的任何应用均可触发我们的 Receiver 。通常情况下我们是不希望如此的。为了避免这种问题,可以有以下方式:

  1. 使用本地广播,但本地广播只允许本应用自己发出;
  2. AndroidManifest.xml 清单文件中为 < receiver > 标签添加一个 android:exported="false" 属性,标明该 Receiver 仅限应用内部使用,这样的结果一样是无法接收到外部发来的广播。
  3. 使用自定义权限来对接收者进行过滤,只有声明了指定权限的 Receiver 才可以接收到该广播。

使用私有权限,首先需要在AndroidManifest.xml 清单文件中声明自定义权限。

 <permission
        android:name="com.test.permission.receiver"
        android:protectionLevel="signature" />

声明自定义权限时必须同时指定 protectionLevel 属性值,系统根据该属性值确定自定义权限的使用方式

属性值限定方式
normal默认值。较低风险的权限,对其他应用,系统和用户来说风险最小。系统在安装应用时会自动批准授予应用该类型的权限,不要求用户明确批准(虽然用户在安装之前总是可以选择查看这些权限)
dangerous较高风险的权限,请求该类型权限的应用程序会访问用户私有数据或对设备进行控制,从而可能对用户造成负面影响。因为这种类型的许可引入了潜在风险,所以系统可能不会自动将其授予请求的应用。例如,系统可以向用户显示由应用请求的任何危险许可,并且在继续之前需要确认,或者可以采取一些其他方法来避免用户自动允许
signature只有在请求该权限的应用与声明权限的应用使用相同的证书签名时,系统才会授予权限。如果证书匹配,系统会自动授予权限而不通知用户或要求用户的明确批准
signatureOrSystem系统仅授予Android系统映像中与声明权限的应用使用相同的证书签名的应用。请避免使用此选项,“signature”级别足以满足大多数需求,“signatureOrSystem”权限用于某些特殊情况

新建一个工程作为发送端,在它的 AndroidManifest.xml 文件中创建一个自定义权限,并声明该权限。

<permission android:name="com.test.permission.receiver" android:protectionLevel="signature" /> 
<uses-permission android:name="com.test.permission.receiver" />

发送含有该权限声明的 Broadcast,这样,只有使用相同证书签名且声明该权限的应用才能接收到该 Broadcast。

private final String PRIVATE_PERMISSION = "com.test.permission.receiver";
public void sendPermissionBroadcast(View view) {
    Intent intent = new Intent("android.intent.action.test");
    sendBroadcast(intent, PRIVATE_PERMISSION);
}

而接收端,则只需要在AndroidManifest.xml 文件中也声明该权限,就可以接收到该广播了。

<uses-permission android:name="com.test.permission.receiver" />

接收端注册广播:

private final String PRIVATE_PERMISSION = "com.test.permission.receiver";
private final String ACTION = "android.intent.action.test";
IntentFilter intentFilter1 = new IntentFilter(ACTION); 
MyBroadcastReceiver receiver = new MyBroadcastReceiver(); 
registerReceiver(receiver, intentFilter1, PRIVATE_PERMISSION, null);

Android常用系统广播

广播名说明备注
Intent.ACTION_BATTERY_LO电池电量低
Intent.ACTION_BATTERY_OK电池电量充足
Intent.ACTION_AIRPLANE_MODE_CHANGED关闭或打开飞行模式
Intent.ACTION_BATTERY_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设备当前设置被改变时发出的广播界面语言,设备方向,等 请参考Configuration.java
Intent.ACTION_DATE_CHANGED设备日期发生改变时
Intent.ACTION_HEADSET_PLUG在耳机口上插入耳机时发出的广播
Intent.ACTION_INPUT_METHOD_CHANGED改变输入法时发出的广播
Intent.ACTION_LOCALE_CHANGED设备当前区域设置已更改时发出的广播
Intent.ACTION_MANAGE_PACKAGE_STORAGE表示用户和包管理所承认的低内存状态通知应该开始
Intent.ACTION_MEDIA_BAD_REMOVAL未正确移除SD卡扩展卡已经从SD卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)
Intent.ACTION_MEDIA_CHECKING插入外部储存装置比如SD卡时,系统会检验SD卡,此时发出的广播
Intent.ACTION_MEDIA_EJECT已拔掉外部大容量储存设备发出的广播不管有没有正确卸载
Intent.ACTION_MEDIA_MOUNTED插入SD卡并且已正确安装扩展介质被插入而且已经被挂载
Intent.ACTION_MEDIA_NOFS拓展介质存在,但使用不兼容FS(或为空)的路径安装点检查介质包含在Intent.mData领域
Intent.ACTION_MEDIA_REMOVED外部储存设备已被移除,扩展介质被移除不管有没正确卸载,都会发出此广播
Intent.ACTION_MEDIA_SCANNER_FINISHED已经扫描完介质的一个目录
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE请求媒体扫描仪扫描文件并将其添加到媒体数据库
Intent.ACTION_MEDIA_SCANNER_STARTED开始扫描介质的一个目录
Intent.ACTION_MEDIA_SHARED扩展介质的挂载被解除 (unmount)它已经作为 USB 大容量存储被共享
Intent.ACTION_PACKAGE_ADDED成功的安装APK数据包括包名(最新安装的包程序不能接收到这个广播)
Intent.ACTION_PACKAGE_CHANGED一个已存在的应用程序包已经改变包括包名
Intent.ACTION_PACKAGE_DATA_CLEARED清除一个应用程序的数据时发出的广播清除包程序不能接收到这个广播
Intent.ACTION_PACKAGE_INSTALL触发一个下载并且完成安装时发出的广播比如在电子市场里下载应用
Intent.ACTION_PACKAGE_REMOVED成功的删除某个APK之后发出的广播正在被安装的包程序不能接收到这个广播
Intent.ACTION_PACKAGE_REPLACED替换一个现有的安装包时发出的广播(不管现在安装的APP比之前的新还是旧
Intent.ACTION_PACKAGE_RESTARTED用户重新开始一个包重新开始包程序不能接收到这个广播
Intent.ACTION_POWER_CONNECTED插上外部电源时发出的广播
Intent.ACTION_POWER_DISCONNECTED已断开外部电源连接时发出的广播
Intent.ACTION_REBOOT重启设备时的广播
Intent.ACTION_SCREEN_OFF屏幕被关闭之后的广播
Intent.ACTION_SCREEN_ON屏幕被打开之后的广播
Intent.ACTION_SHUTDOWN关闭系统时发出的广播
Intent.ACTION_TIMEZONE_CHANGED时区发生改变时发出的广播
Intent.ACTION_TIME_CHANGED时间被设置时发出的广播
Intent.ACTION_TIME_TICK当前时间已经变化(正常的时间流逝)每分钟都发送,在代码注册
Intent.ACTION_UID_REMOVED一个用户ID已经从系统中移除发出的广播
Intent.ACTION_UMS_CONNECTED设备已进入USB大容量储存状态时发出的广播
Intent.ACTION_UMS_DISCONNECTED设备已从USB大容量储存状态转为正常状态时发出的广播
Intent.ACTION_WALLPAPER_CHANGED设备墙纸已改变时发出的广播
Intent.ACTION_USER_PRESENT用户唤醒设备
Intent.ACTION_NEW_OUTGOING_CALL拨打电话

文章评论已关闭!