详解Android广播机制

xilove0 2019-12-09

应用场景(常见的场景1)

(1)同一应用具有多个进程的不同组件之间的消息通信

   a)不同应用间的组件之间的消息通信

   b)与Android系统在特定情况下的通信,如:系统开机,网络变化等

(2)同一应用内同一组件的消息通信:显然扩展变量的作用域、接口回调、Handler-Message等方式都能更简单的实现。

(3)同一应用内的不同组件之间的消息通信(单个进程):对于简单的的情况,依靠接口的回调方式就可解决;而较为复杂的情况,更推荐直接使用EventBus等。

实现原理

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

从设计模式上讲,广播的发送者和接收者极大程度的解耦,使得系统方便集成,容易扩展
模型成员:

  • 消息发布者(广播发布者)
  • 消息订阅者(广播接收者)
  • 消息中心(AMS,Activity Manager Service,一个Android系统中极其重要!的成分,以后我们会详细讲解)

此处我们扩展一下,观察者模式和发布订阅模式的关系

  • 发布订阅模式属于广义上的观察者模式,前者时最常用的一种观察者模式的实现,且从解耦和重用角度上看更优于典型的观察者模式,在观察者模式中,观察者需要直接订阅目标事件,在目标发出内容改变的事件后,直接接收事件并作出响应。
  • 发布订阅模式加入消息中心,实现发布者和订阅者的解耦在发布订阅模式中,多了一个消息中心,一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要从消息中心订阅事件。以此避免发布者和订阅者之间产生依赖关系。

实现流程

  1. 广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
  2. 广播发送者通过binder机制向AMS发送广播;
  3. AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver
  4. AMS将广播发送到上述符合条件的BroadcastReceiver相应的消息循环队列中
  5. BroadcastReceiver通过消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。

BroadcastReceiver

自定义BroadcastReceiver,继承基类BroadcaseReceiver,实现抽象方法onReceive(context, intent)收到广播后,会自动回调onReceive(..)方法,通常,onReceive(..)方法会涉及到与其他组件的交互,如发送Notification,启动service等。默认情况,BroadcaseReceiver运行在UI线程,因此,onReceive(..)方法不能执行耗时操作,否则ANR

简单的自定义Demo:
MyBroadcastReceiver.java

//继承BroadcastReceiver基类
public class MyBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "MyBroadcastReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        StringBuilder sb = new StringBuilder();
        sb.append("Action: " + intent.getAction() + "\n");
        sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
        String log = sb.toString();
        Log.d(TAG, log);
        Toast.makeText(context, log, Toast.LENGTH_LONG).show();
    }
}

BroadcastReceiver注册类型

1. 静态注册

  • AndroidManifest.xml文件中通过<receiver>进行注册
  • 规则及实例说明:
<receiver
    //BroadcastReceiver子类的类名
    android:name="string"

    //是否使用该BroadcastReceiver
    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:permission="string"

    //broadcastReceiver运行所处的进程。
    //默认为app的进程,可以指定独立的进程
    //Android四大基本组件都可以通过此属性指定自己的独立进程
    android:process="string">

    //指定此广播接收器将用于接收特定的广播类型
    //本例中给出的时系统开机后自身发出的广播
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>

以上述静态方法注册的MyBroadcastReceiver,在app首次启动时,系统或自动实例化MyBroadcastReceiver,并注册到系统中。

2. 动态注册

  • 在代码中调用Context.registerReceiver()
  • 典型写法示例如下:
public class MainActivity extends AppCompatActivity {

    public static final String BROADCAST_ACTION = "com.example.whd_alive";
    private BroadcastReceiver mBroadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //实例化MyBroadcastReceiver 
        mBroadcastReceiver = new MyBroadcastReceiver();
        //实例化IntentFilter
        IntentFilter intentFilter = new IntentFilter();

        //设置接收广播的类型
        intentFilter.addAction(BROADCAST_ACTION);

        //动态注册
        registerReceiver(mBroadcastReceiver, intentFilter);
    }

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

注:Android中所有与观察者模式有关的设计中,一旦涉及到register,必定在相应的时机需要unregister。因此,上例在onDestroy()回调需要unregisterReceiver(mBroadcastReceiver)。

广播发送及广播类型

广播发送,广播 这一实体本身以 intent 表示,广播的定义 = 相应广播intent的定义,广播的发送:通过广播发送者将此intent发送出去,根据不同类型的广播调用相对应的send方法。

广播的类型主要分为一下四类:

  • Normal Broadcast(普通广播):通常调用sendBroadcast(Intent)(Intent, String)方法发送
  • System Broadcast(系统广播):发生各种事件时,系统自动发送
  • Ordered Broadcast(有序广播):调用sendOrderedBroadcast(Intent, String)方法发送
  • Local Broadcast(本地广播):调用LocalBroadcastManager.sendBroadcast(intent)方法发送
  • Sticky Broadcast(粘性广播):已弃用(API 21)

1. Normal Broadcast(普通广播)

开发者自定义的intent,以Context.sendBroadcast(),Context.sendBroadcastAsUser()等方法发送该intent。

Intent intent = new Intent();
        intent.setAction(BROADCAST_ACTION);
        //最普通的发送方式
        sendBroadcast(intent);
        //附带权限的发送方式,声明此权限的BroadcastReceiver才能接收此广播
        sendBroadcast(intent,RECEIVER_PREMISSION);

        //以下两种不常见,是因为只有预装在系统映像中的程序才能使用,否则无法使用
        //指明接收人的发送方式
        sendBroadcastAsUser(intent,USER_HANDLER);
        //指明接收人以及对应权限的发送方式
        sendBroadcastAsUser(intent,USER_HANDLER,RECEIVER_PREMISSION);
  • 若被注册了的BroadCastReceiver注册的intentFilteraction与上述匹配,则会接收此广播,且顺序是无序的。如果发送时有相应的权限要求,则BroadCastReceiver只有拥有相应的权限才能接受。
<receiver
    android:name=".MyBroadcastReceiver"
    android:permission="RECEIVER_PREMISSION">
    <intent-filter>
        <action android:name="BROADCAST_ACTION"/>
    </intent-filter>
</receiver>

2. System Broadcast(系统广播)

Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。

3. Ordered Broadcast(有序广播))

发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的有序广播中的“有序”是针对广播接收者而言的

发送方式:

定义过程与普通广播一样,调用sendOrderedBroadcast(),同样也有对应的sendOrderedBroadcastAsUser()方法,只不过同样针对于预装在系统映像的应用。
特点

按顺序接收
允许优先级高的BroadcastReceiver截断广播。
允许优先级高的BroadcastReceiver修改广播
接受顺序

priority值不同:由大到小排序
priority值相同:动态注册优于静态注册
4. Local Broadcast(本地广播)

可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App

方案2的具体实现:
使用封装好的LocalBroadcastManager类。
使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。

对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)
代码示例如下:

//实例化MyBroadcastReceiver
mBroadcastReceiver = new MyBroadcastReceiver();
//实例化IntentFilter
IntentFilter intentFilter = new IntentFilter();

//得到LocalBroadcastManager实例
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);

//设置接收广播的类型
intentFilter.addAction(BROADCAST_ACTION);

//动态注册
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);


//取消注册
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);

不同注册方式的广播接收器回调onReceive(context, intent)中的context具体类型

  • 静态注册(全局+本地): 回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext
  • 全局动态注册: 回调onReceive(context, intent)中的context具体指的是Activity Context;
  • LocalBroadcastManager动态注册,回调onReceive(context, intent)中的context具体指的是Application Context。

出于安全考虑的广播使用最佳实践

如不需要向应用程序之外的组件发送广播,则可以使用支持库Support Library中LocalBroadcastManager发送和接收本地广播。如果许多应用程序清单中注册接收相同的广播,它会导致系统启动大量的应用程序,从而对设备性能和用户体验产生重大影响。为了避免这种情况,请使用动态注册而不是Manifest声明。有时,Android系统本身会强制使用上下文注册的接收器。例如,CONNECTIVITY_ACTION广播只允许动态注册。

onReceive(Context, Intent)运行在UI线程,不要进行耗时操作

如耗时操作必不可少,生成子线程。
不要使用隐含的意图传播敏感信息。这些信息可以被任何注册的应用程序读取。

解决方案 : permission / setPackage(String) / LocalBroadcastManager.
当注册一个BroadcastReceiver,任何应用程序都可以发送潜在的恶意广播到你的应用的BroadcastReceiver。

解决方案 : permission / android:exported = "false" / LocalBroadcastManager.
广播操作的命名空间是全局的。确保操作名称和其他字符串都是在您自己的名称空间中编写的,否则您可能会无意中与其他应用程序发生冲突。

不要从BroadcastReceiver开始活动,这么做会导致用户体验很差,特别是如果有不止一个BroadcastReceiver。相反,考虑使用Notification。
————————————————
版权声明:本文为CSDN博主「whd_Alive」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/whdAlive/article/details/80250261

相关推荐