详解Android广播机制

应用场景(常见的场景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

原文地址:https://www.cnblogs.com/yz123/p/12008548.html

时间: 2024-08-30 13:28:29

详解Android广播机制的相关文章

Android随笔之——Android广播机制Broadcast详解

在Android中,有一些操作完成以后,会发送广播,比如说发出一条短信,或打出一个电话,如果某个程序接收了这个广播,就会做相应的处理.这个广播跟我们传统意义中的电台广播有些相似之处.之所以叫做广播,就是因为它只负责“说”而不管你“听不听”,也就是不管你接收方如何处理.另外,广播可以被不只一个应用程序所接收,当然也可能不被任何应用程序所接收. 一.Android广播机制三要素: 1.广播(Broadcast):用于发送广播.是一种广泛应用的在应用间传输信息的机制 2.广播接收器(Broadcast

详解Android Handler的使用

我们进行Android开发时,Handler可以说是使用非常频繁的一个概念,它的用处不言而喻.本文就详细介绍Handler的基本概念和用法. Handler的基本概念         Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分逐个的在消息队列中将消息取出,然后对消息进行出来,就是发送消息和接收消息不是同步的处理. 这种机制通常用来处理相对耗时比较长的操作. Handler工具类在多线程中有两方面的应用: 1.发送消息,在

详解Android Handler的使用-别说你不懂handler(转)

我们进行Android开发时,Handler可以说是使用非常频繁的一个概念,它的用处不言而喻.本文就详细介绍Handler的基本概念和用法. Handler的基本概念         Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分逐个的在消息队列中将消息取出,然后对消息进行出来,就是发送消息和接收消息不是同步的处理. 这种机制通常用来处理相对耗时比较长的操作. Handler工具类在多线程中有两方面的应用: 1.发送消息,在

详解Android Handler的使用-别说你不懂handler

我们进行Android开发时,Handler可以说是使用非常频繁的一个概念,它的用处不言而喻.本文就详细介绍Handler的基本概念和用法. Handler的基本概念         Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分逐个的在消息队列中将消息取出,然后对消息进行出来,就是发送消息和接收消息不是同步的处理. 这种机制通常用来处理相对耗时比较长的操作. Handler工具类在多线程中有两方面的应用: 1.发送消息,在

详解 Android 的 Activity 组件

本文详细介绍了 Android 应用编程中 Activity 的生命周期.通信方式和 Intent Filter 等内容,并提供了一些日常开发中经常用到的关于 Activity 的技巧和方法.通过本文,你可以进一步了接 Android 中 Activity 的运作方式. 详解 Android 的 Activity 组件 Activity 的生命周期 和 J2ME 的 MIDlet 一样,在 android 中,Activity 的生命周期交给系统统一管理.与 MIDlet 不同的是安装在 and

(转)详解Android中AsyncTask的使用

转载自:详解Android中AsyncTask的使用 在Android中实现异步任务机制有两种方式,Handler和AsyncTask. Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制.关于Handler的相关知识,前面也有所介绍,不清楚的朋友们可以参照一下. 为了简化操作,Android1.5提供了工具类a

Android广播机制(转)

1.Android广播机制概述 Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器).广播作为Android组件间的通信方式,可以使用的场景如下:1.同一app内部的同一组件内的消息通信(单个或多个线程之间): 2.同一app内部的不同组件之间的消息通信(单个进程): 3.同一app具有多个进程的不同组件之间的消息通信: 4.不同app之间的组件之间消息通信: 5.Android系统在特定情况下与App之间的消息通

详解Android中那些酷炫返回方式的实现

Android手机都会有返回键,不管是实体键,还是虚拟键.Android用户主要也都是通过这个返回键操控页面返回方式的,不比IOS逼格甚高的只保留一个操作键.这种方式是最普遍的返回方式,还有一种也是比较常见的,那就是页面内部自己响应.绝大多数APP每个页面的设计图顶部左侧都会有一个返回键图标,偶尔也有奇葩的设计放在底部左侧,点击这个图标即finish掉当前页面.简单的介绍完了最常见的两种方式,下面为大家介绍两种更友好的交互方式. 拿大家比较常用的三款社交软件的交互来说.腾讯微博的返回方式除去上述

详解Android ActionBar之二:ActionBar添加Tabs标签和下拉导航

本节主要讲解ActionBar如何添加Tabs标签和下拉导航. 一.添加标签 Tabs 在ActionBar中实现标签页可以实现android.app.ActionBar.TabListener ,重写onTabSelected.onTabUnselected和onTabReselected方法来关联Fragment.代码如下: Java代码 private class MyTabListener implements ActionBar.TabListener { private TabCon