Broadcast 知识梳理(1) - BroadcastReceiver 基本概念

一、基本概念

1.1 实现广播接收者

首先,我们需要创建一个广播接收者,继承于BroadcastReceiver并重写它的onReceive方法。

public class MyBroadcastReceiver extends BroadcastReceiver {

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

在创建完接收者之后,还需要进行注册,告诉系统有这个监听者。广播注册的方式分为:静态注册和动态注册。

静态注册

静态注册在AndroidManifest.xml中进行指定。

<receiver android:enabled=["true" | "false"]
    android:exported=["true" | "false"]
    android:icon="drawable resource"
    android:label="string resource"
    android:name="string"
    android:permission="string"
    android:process="string" >
. . .
</receiver>
  • exportedexported决定了 广播接收者所能收到广播的范围,假如为false,那么只有同一个App,或者userId相同的App发出的广播它才能够收到,并不是指同一个进程。exported一般情况下默认为false,唯一例外的是假如设置了intent-filter,那么默认值为true。对于静态注册的广播,在Android 3.1之后,应用如果没有启动并且Intent中包含了FLAG_INCLUDE_STOPPED_PACKAGES属性,那么会先调起应用,否则在应用没有启动的情况下将无法收到广播。

  • permission:如果设置了permission,那么只有 具有相应权限的广播发送方 发送的广播才能被此BroadcastReceiver接收。Android广播的权限机制是双向的,即我们既可以 要求发送者具有权限,也可以 要求接收者具有权限,才能完成端到端的通信过程,这里就是 要求发送者具有权限

  • process:运行所处的进程,默认为App的进程。

动态注册

动态注册的广播无需在AndroidManifest.xml进行声明,在代码中进行注册和注销即可。

//注册广播。
registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
//注销广播。
unregisterReceiver(BroadcastReceiver receiver);

1.2 发送广播

广播的发送者通过Intent将其意图发送出去,系统找到匹配的接收者,发送广播的一般方式如下:

Intent intent = new Intent();
intent.setAction(INTENT_ACTION);
sendBroadcast(intent);

1.3 广播类型

广播可以分为以下几类:无序广播、有序广播和粘性广播。

1.3.1 无序广播

无序广播指的是所有广播接收者收到广播的顺序是没有规律的。

1.3.2 有序广播

有序广播指的是发送出的广播被BroadcastReceiver按照priority从大到小的顺序接收,当priority相同时,动态广播优先静态广播,发送有序广播的方式为:

sendOrderedBroadcast(intent, receiverPermission, ...)

对于有序广播有一个特点,先接收的BroadcastReceiver具有拦截广播的权利,拦截的方法为onReceive方法中调用abortBroadcast()方法。

1.3.3 粘性广播

已经废弃,它是用来处理先收到广播然后才注册的情况。

1.4 应用内广播

假如exported属性为true,那么是允许两个不同应用通过广播进行通信的。就可能出现 安全隐患

  • 其他App可能会针对性地发出与当前Appintent-filter相匹配的广播,导致当前App不断接收到广播并处理。
  • 其他App可以注册与当前App相匹配的intent-filter,从而获取广播具体信息。

为了避免出现以上的安全问题,有以下的解决方法:

  • 设置exported属性为false
  • 设置权限。
  • 发送广播时,指定具体的包名。

假如我们的广播只需要在应用内部通信,那么可以采用封装好的LocalBroadcastManager类,用于解决安全问题。

传统的广播是通过Binder来实现的,而LocalBroadcastManager则是采用Handler的方式。当注册广播的时候,其实将Receiver添加到单例对象LocalBroadcastManager维护的列表当中,发送消息的时候,通过Receiver所关联的action找到它,最后回调它的onReceive方法。

因此,只有通过LocalBroadcastManager注册的BroadcastReceiver才能收到通过LocalBroadcastManager发出的广播。

具体的代码实现可以参考这篇文章 LocalBroadcastManager 的实现原理,还是 Binder?

二、一些需要注意的点

2.1 权限问题

通过权限可以也可以解决我们之前谈到的安全问题,Broadcast的权限是双向的。

2.1.1 要求接收者具有权限

这种方式 用于防止广播信息泄露

发送者AndroidManifest.xml中定义权限。

<permission android:name = "com.android.permission.RECV_XXX"/>

发送者在发送广播的时候,采用带有权限的接口进行发送。

sendBroadcast("com.android.XXX_ACTION", "com.android.permission.RECV_XXX");

接收者 如果希望能收到广播,那么需要在它的AndroidManifest.xml进行声明使用该权限。

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

2.1.2 要求发送者具有权限

这种方式 用于防止外部应用恶意地发送广播,导致接收者一直在处理

接收者AndroidManifest.xml中定义权限。

<permission android:name="com.android.SEND_XXX"/>

在接收者的AndroidManifest.xml声明BroadcastReceiver的时候,通过permission字段指定权限。

<receiver android:name=".XXXReceiver" 
          android:permission="com.android.permission.SEND_XXX"> 
    <intent-filter>
         <action android:name="com.android.XXX_ACTION" /> 
    </intent-filter>
</receiver>

发送者 如果希望广播能被该接收者收到,那么需要在AndroidManifest.xml中声明使用该权限。

<uses-permission android:name="com.android.permission.SEND_XXX“></users-permission>

2.2 ANR

BroadcastReceiver方法中,不要进行耗时的操作,超过10s会发生BroadcastQueue TimeoutANR异常。

2.3 onReceive 传入的 Context

  • 静态注册的BroadcastReceiver,其Contextandroid.app.ReceiverRestrictedContext

  • 动态注册的普通BroadcastReceiver,与调用registerReceiver方法的Context有关,如果是通过Application Context注册的,那么ContextApplication Context,如果是Activity Context,那么其ContextActivity Context

  • LocalBroadcastManager动态注册的BroadcastReceiver,其ContextApplication Context

三、参考文章

Android Broadcast 和 BroadcastReceiver 的权限限制
Android 总结篇系列:Android 广播机制
LocalBroadcastManager 的实现原理,还是 Binder?

    原文作者:泽毛
    原文地址: https://www.jianshu.com/p/516c110cf2a5
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞