[Android]【安卓】Broadcast Receiver详解

[Android]【安卓】Broadcast Receiver详解

本篇博客已收录到我的安卓开发小结中——点击【安卓开发小结】
参考资料:

《AndroidManifest.xml文件详解(receiver)》
《Android四大组件:BroadcastReceiver史上最全面解析》
《Android广播机制详解》
《第一行代码》

默认的priority是0


《AndroidManifest.xml文件详解(receiver)》

 语法(SYNTAX):

<receiverandroid: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>

被包含于(CONTAINED IN):

< application>

能够包含的元素(CAN CONTAIN):

< intent-filter>
< meta-data>

说明(DESCRIPTION):

这个元素用于声明一个广播接收器(一个BroadcastReceiver 子类),作为应用程序的组件之一。广播接收器能够让应用程序接收那些由系统或其他应用程序发出的广播Intent对象,即使是在该应用程序的其他组件没有运行的时候,也能够接收来自系统或其他应用程序的广播消息。
有两种方式让系统知道本应用程序用户一个广播接收器:
1. 在应用程序的清单文件中,使用本元素来声明注册一个广播接收器;
2. 在代码中动态的创建一个广播接收器,并使用Context.registerReceiver()方法来注册它。有关更多动态创建接收器的方法,请看BoradcastReceiver类说明。

属性(ATTRIBUTES):

android:enabled

这个属性用于定义系统是否能够实例化这个广播接收器,如果设置为true,则能够实例化,如果设置为false,则不能被实例化。默认值是true。
< application>元素有它自己的enabled属性,这个属性会应用给应用程序的所有组件,包括广播接收器。< a pplication>和< receiver>元素的这个属性都必须是true,这个广播接收器才能够被启用。如果有一个被设置为false,该广播接收器会被禁止实例化。

android:exported

这个属性用于指示该广播接收器是否能够接收来自应用程序外部的消息,如果设置true,则能够接收,如果设置为false,则不能够接收。如果设置为false,这该接收只能接收那些由相同应用程序组件或带有相同用户ID的应用程序所发出的消息。
它的默认值依赖它所包含的Intent过滤器。如果不包含过滤器,则接收器只能由指定了明确类名的Intent对象来调用,这就意味着该接收器只能在应用程序内部使用(因为通常在应用程序外部是不会知道这个类名的)。这种情况下默认值就是false。另一方面,如果接受器至少包含了一个过滤器,那么就意味着这个接收器能够接收来自系统或其他应用程序的Intent对象,因此默认值是true。
这个属性不是唯一的限制广播接收外部调用的方法,还能够通过全限来限制能够给它发送消息的外部实体。

android:icon

这个属性定义了一个代表广播接收器的图标,这个属性必须用包含图片定义的可绘制资源来设定。如果没有设置这个属性,会是应用< application>元素的icon属性值来代替。
无论是这个属性还是< application>元素的icon属性,它们设置的图标也是所有的接收器的Intent过滤器的默认图标。

android:label

这个属性给广播接收器设定一个用户可读的懂的文本标签。如果这个属性没有设置,那么就会使用< application>元素的label属性值来代替。
无论是这个属性还是< application>元素的label属性,它们设置的标签也是所有的接收器的Intent过滤器的默认标签。
应该使用一个字符串资源来设置这个属性,以便它能够像用户界面中的其他字符串一样能够被本地化。但是,为了应用开发的便利,也能够使用原生的字符串来设置。

android:name

这个属性值要用广播接收器的实现类的类名来设置,它是BroadcastReceiver类的一个子类。通常要使用类的全名来设置(如:com.example.project.ReportReceiver)。但是,也可以使用简写(如:.ReportReceiver)。系统会自动的把< manifest>元素中的package属性所设定的包名添加到这个简写的名称上。
一旦发布了应用程序,就不应该在改变这个名字了(除非android:exported=”false”)。
这个属性没有默认值,这个名字必须被指定。

android:permission

这个属性用于定义把消息发送给该广播接收器的广播器所必须要有的权限。如果没有设置这个属性,那么< application>元素的permission属性所设置的权限就适用于这个广播接收器。如果< application>元素也没有设置权限,那么该接收器就不受权限的保护。

android:process

这个属性用于设置该广播接收器应该运行在那个进程中的进程名。通常,应用程序的所有组件都在给应用程序创建的默认进程中运行,它有与应用程序包名相同的名称。< application>元素的process属性能够给它的所有组件设置一个不同的默认进程,但是它的每个组件自己的process属性能够覆盖这个默认设置,这样就允许把一个应用程序分离到多个进程中。
如果这个属性值用“:”开头,则在需要的时候系统会创建一个新的,应用程序私有的进程,并且该广播接收器也会运行在这个进程中。如果这个属性值用小写字母开头,那么接收器就会运行在以这个属性值命名的全局进程中,它提供使其工作的权限。这样就允许不同的应用程序组件来共享这个进程。

被引入版本(INTRODUCED IN):

API Level 1


《Android四大组件:BroadcastReceiver史上最全面解析》

1. 定义

即 广播,是一个全局的监听器,属于Android四大组件之一

Android 广播分为两个角色:广播发送者、广播接收者

2. 作用

监听 / 接收 应用 App 发出的广播消息,并 做出响应

3. 应用场景

  • Android不同组件间的通信(含 :应用内 / 不同应用之间)
  • 多线程通信
  • 与 Android 系统在特定情况下的通信

如:电话呼入时、网络可用时

4. 实现原理

4.1 采用的模型

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

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

4.2 模型讲解

模型中有3个角色:

  • 消息订阅者(广播接收者)
  • 消息发布者(广播发布者)
  • 消息中心(AMS,即Activity Manager Service)

示意图 & 原理如下
这里写图片描述

5.2.2 动态注册

在onResume()注册、onPause()注销是因为onPause()在App死亡前一定会被执行,从而保证广播在App死亡前一定会被注销,从而防止内存泄露。

1、不在onCreate() & onDestory() 或 onStart() & onStop()注册、注销是因为:
当系统因为内存不足(优先级更高的应用需要内存,请看上图红框)要回收Activity占用的资源时,Activity在执行完onPause()方法后就会被销毁,有些生命周期方法onStop(),onDestory()就不会执行。当再回到此Activity时,是从onCreate方法开始执行。
2、假设我们将广播的注销放在onStop(),onDestory()方法里的话,有可能在Activity被销毁后还未执行onStop(),onDestory()方法,即广播仍还未注销,从而导致内存泄露。
3、但是,onPause()一定会被执行,从而保证了广播在App死亡前一定会被注销,从而防止内存泄露。

5.2.3 两种注册方式的区别

这里写图片描述

5.3.1 广播的发送

  • 广播 是 用”意图(Intent)“标识
  • 定义广播的本质 = 定义广播所具备的“意图(Intent)”
  • 广播发送 = 广播发送者 将此广播的“意图(Intent)”通过sendBroadcast()方法发送出去

《Android广播机制详解》

一、概述

在 Android 里面有各种各样的广播,比如电池的使用状态,电话的接收和短信的接收都会产生一个广播,应用程序开发者也可以监听这些广播并做出程序逻辑的处理。下面是一张粗略的图来帮助大家理解广播的运行机制。
这里写图片描述

Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器)。广播作为Android组件间的通信方式,可以使用的场景如下:

  • 同一app内部的同一组件(Component)内的消息通信(单个或多个线程之间)
  • 同一app内部的不同组件之间的消息通信(单个进程)
  • 同一app具有多个进程的不同组件之间的消息通信
  • 不同app之间的组件之间消息通信
  • Android系统在特定情况下与App之间的消息通信

从实现原理上看,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。具体实现流程要点粗略概括如下:

  • 广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册
  • 广播发送者通过binder机制向AMS发送广播
  • AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver相应的消息循环队列中
  • 消息循环队列拿到此广播后,回调BroadcastReceiver中的onReceive()方法

由此看来,广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。

在上文中列举的广播机制具体可以使用的场景中,现分析实际应用中的适用性:

  • 第一种情形:同一app内部的同一组件内的消息通信(单个或多个线程之间),实际应用中肯定是不会用到广播机制的(虽然可以用),无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题,若适用广播机制,显然有些“杀鸡牛刀”的感觉,会显太“重”;
  • 第二种情形:同一app内部的不同组件之间的消息通信(单个进程),对于此类需求,在有些较复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等,相对而言,EventBus由于是针对统一进程,用于处理此类需求非常适合,且轻松解耦。关于EventBus更详细介绍请看这篇文章Android EventBus事件总线剖析。
  • 第三、四、五情形:由于涉及不同进程间的消息通信,此时根据实际业务使用广播机制会显得非常适宜。

二、广播的注册

BroadcastReceiver可以分为两种注册类型:静态注册和动态注册。

(1)静态注册

直接在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" >
          . . .
          <intent-filter>
              <action  android:name="string" />
          </intent-filter>
</receiver>

其中,需要注意的属性:
android:exported ——此BroadcastReceiver能否接收其他App发出的广播。这个属性默认值有点意思,其默认值是由receiver中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。(同样的,activity/service中的此属性默认值一样遵循此规则)。同时,需要注意的是,这个值的设定是以application或者application user id为界的,而非进程为界(一个应用中可能含有多个进程);
android:name —— 此BoadcastReceiver类名(不能是内部类,内部类只能动态注册);
android:permission ——如果设置,具有相应权限的广播发送方发送的广播才能被此broadcastReceiver所接收;
android:process ——BroadcastReceiver运行所处的进程。默认为App的进程。可以指定独立的进程(Android四大基本组件都可以通过此属性指定自己的独立进程)
intent-filter ——指定此广播接收器将用于接收特定Action的广播。
当此App首次启动时,系统会自动实例化此BroadcastReceiver,并注册到系统中。
注:之前常说静态注册的广播接收器即使App已经退出,只要有相应的广播发出,依然可以接收到。但此种描述自Android 3.1开始有可能不再成立,具体分析详见本文后面部分。

(2)动态注册

动态注册时,无须在AndroidManifest中注册<\receiver>组件。直接在代码中通过调用Context的registerReceiver函数,可以在程序中动态注册BroadcastReceiver。registerReceiver的定义形式如下:

registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)
public class MainActivity extends AppCompatActivity {
    public static final String BROADCAST_ACTION = "com.hx.bc";
    private BroadcastReceiver mBroadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //动态注册广播
        mBroadcastReceiver = new MyBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BROADCAST_ACTION);
        registerReceiver(mBroadcastReceiver, intentFilter);
    }

    public class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            ...
        }
    }

    @Override
    protected void onDestroy() {
       super.onDestroy();
       unregisterReceiver(mBroadcastReceiver);
    }
}

当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中。当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
注:Android中所有与观察者模式有关的设计中,一旦涉及到register,必定在相应的时机需要unregister。因此,上例在onDestroy()回到中需要unregisterReceiver(mBroadcastReceiver)。

三、广播的接收

自定义广播接收器需要继承基类BroadcastReceivre,并实现抽象方法onReceive(context, intent)方法。广播接收器接收到相应广播后,会自动回调onReceive(..)方法。
默认情况下,广播接收器是运行在UI线程中的,因此,onReceive方法中不能执行太耗时的操作,否则将引起ANR。在BroadCast中尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理。一般情况下,根据实际业务需求,onReceive方法中都会涉及到与其他组件之间的交互,如发送Notification、启动service等。
下面代码片段是一个简单的自定义广播接收器:

public class MyBroadcastReceiver extends BroadcastReceiver {
      @Override
      public void onReceive(Context context, Intent intent) {
          showlog("MyBroadcastReceiver-onReceive");
          String strSMSBody = intent.getStringExtra("SMSBody");
          String strSMSAdress= intent.getStringExtra("SMSAdress");
          Intent myIntent = new Intent();    
          //通过Intent对象把信息的内容和发送人的号码发给新的Activity    
          myIntent.putExtra("SMSBody", strSMSBody);    
          myIntent.putExtra("SMSAdress", strSMSAdress);    
          //从Service或BroadcastReciver往Activity跳转时,要将Intent的Flag设置为FLAG_ACTIVITY_NEW_TASK才可以    
          myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);      
          myIntent.setClass(context, BroadcastActivity.class);    
          context.startActivity(myIntent); 
      }
}

四、广播的发送及广播类型

通常说”发送广播“和”接收广播“,表面上看广播作为Android广播机制的实体,实际上这一实体本身是并不是以所谓的”广播“对象存在的,而是以”意图“(Intent)去表示。广播的定义过程,实际就是相应广播”意图“的定义过程,然后通过广播发送者将此”意图“发送出去。被相应的BroadcastReceiver接收后将会回调onReceive()函数。
根据广播的发送方式,可以将其分为以下几种类型:

  • Normal Broadcast:普通广播
  • System Broadcast: 系统广播
  • Ordered broadcast:有序广播
  • Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)
  • Local Broadcast:App应用内广播

下面分别总结下各种类型的发送方式及其特点。

(1)Normal Broadcast:普通广播

此处将普通广播界定为:开发者自己定义的Intent,以context.sendBroadcast[AsUser](intent, …)形式。具体可以使用的方法有:

sendBroadcast(intent)
sendBroadcast(intent, receiverPermission)
sendBroadcastAsUser(intent, userHandler)
sendBroadcastAsUser(intent, userHandler, receiverPermission)

普通广播会被注册了的相应的感兴趣(intent-filter匹配)接收,且顺序是无序的。如果发送广播时有相应的权限要求,BroadCastReceiver如果想要接收此广播,也需要有相应的权限。普通广播是完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播。

//注册广播
mBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION_NORMAL);
registerReceiver(mBroadcastReceiver, intentFilter);

//取消注册广播
unregisterReceiver(mBroadcastReceiver);

//发送广播
button1.setOnClickListener(new View.OnClickListener(){
       @Override
       public void onClick(View v) {
            Intent it = new Intent();
            it.setAction(BROADCAST_ACTION_NORMAL);
            it.putExtra("name","normal broadcast");
            sendBroadcast(it);
       }
});

//接收广播
public class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            showlog("MyBroadcastReceiver-onReceive");
            String name = intent.getStringExtra("name");
            text1.setText(name);
            showlog("name="+name);
        }
    }

点击按钮,发送广播,Log信息如下:
这里写图片描述

(2)System Broadcast:系统广播

Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开机启动,网络状态改变,拍照,屏幕关闭与开启,电量不足等等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,由系统自动发出。

(3)Ordered broadcast:有序广播

有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后顺序接收。有序广播的定义过程与普通广播无异,只是其主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, …)。
对于有序广播,其主要特点总结如下:
1>当前多个已经注册且有效的BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准遵循:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值(声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000,也可以调用IntentFilter对象的setPriority()进行设置)从大到小排序,对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。
2>先接收的BroadcastReceiver可以对此有序广播进行截断(BroadcastReceiver.abortBroadcast()),使后面的BroadcastReceiver不再接收到此广播。也可以将处理结果通过setResultExtras(Bundle)方法存放进结果对象,然后传给下一个接收者,通过代码:Bundle bundle =getResultExtras(true))可以获取上一个接收者存入在结果对象中的数据。一个经典的应用案例是电话黑名单,首先通过将黑名单号码保存在数据库里面,当来电时,我们接收到来电广播并将黑名单号码与数据库中的某个数据做匹配,如果匹配的话则做出相应的处理,比如挂掉电话或静音等。

        //广播注册
        mBroadcastReceiver1 = new MyBroadcastReceiver1();
        IntentFilter intentFilter1 = new IntentFilter();
        intentFilter1.addAction(BROADCAST_ACTION_ORDERED);
        intentFilter1.setPriority(800);
        registerReceiver(mBroadcastReceiver1, intentFilter1);

        mBroadcastReceiver2 = new MyBroadcastReceiver2();
        IntentFilter intentFilter2 = new IntentFilter();
        intentFilter2.addAction(BROADCAST_ACTION_ORDERED);
        intentFilter2.setPriority(500);
        registerReceiver(mBroadcastReceiver2, intentFilter2);

        mBroadcastReceiver3 = new MyBroadcastReceiver3();
        IntentFilter intentFilter3 = new IntentFilter();
        intentFilter3.addAction(BROADCAST_ACTION_ORDERED);
        intentFilter3.setPriority(300);
        registerReceiver(mBroadcastReceiver3, intentFilter3);

        //取消注册广播
        unregisterReceiver(mBroadcastReceiver1);
        unregisterReceiver(mBroadcastReceiver2);
        unregisterReceiver(mBroadcastReceiver3);

        //广播发送
        button2.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                Intent it = new Intent();
                it.setAction(BROADCAST_ACTION_ORDERED);
                it.putExtra("name","ordered broadcast");
                sendOrderedBroadcast(it, null);
            }
        });

     //广播接收
     public class MyBroadcastReceiver1 extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            showlog("MyBroadcastReceiver1-onReceive");
            String name = intent.getStringExtra("name");
            text2_1.setText(name);
            showlog("name="+name);

            Bundle bundle = new Bundle();
            bundle.putString("name", "ordered broadcast modified");
            setResultExtras(bundle);
        }
    }

    public class MyBroadcastReceiver2 extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            showlog("MyBroadcastReceiver2-onReceive");
            //获取原生广播的数据
//          String name = intent.getStringExtra("name");
            Bundle bundle =getResultExtras(true);
            String name = bundle.getString("name");
            text2_2.setText(name);
            showlog("name="+name);

            bundle.putString("name", "ordered broadcast modified again");
            setResultExtras(bundle);
     //     abortBroadcast();  //终止广播
        }
    }

    public class MyBroadcastReceiver3 extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            showlog("MyBroadcastReceiver3-onReceive");
            //获取原生广播的数据
//          String name = intent.getStringExtra("name");
            Bundle bundle =getResultExtras(true);
            String name = bundle.getString("name");
            text2_3.setText(name);
            showlog("name="+name);
        }
    }

点击按钮,发送广播,看Log输出信息:
这里写图片描述
可以看到Receiver1和Receiver2对接收到的数据分别进行了修改,继续传递给后续的广播接收器。
注:通过setResultExtras(bundle); 传递的数据是不会更改原生广播的数据的,只是在原来广播数据中额外添加新的数据。

(4)Sticky Broadcast:粘性广播

在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated。在此不再多做总结。

(5)Local Broadcast:App应用内广播(此处的App应用以App应用进程为界)

Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,由此将可能出现安全隐患如下:

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

无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:

  • 对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;
  • 在广播发送和接收时,都增加上相应的permission,用于权限验证;
  • 发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。{ String packageName = intent.getPackage(); }

App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时,之所以使用应用内广播,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。
相比于全局广播,App应用内广播优势体现在:安全性更高、更加高效。
为此,Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题,使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。
代码片段如下:

/**注册应用内广播接收器*/
mBroadcastReceiver4 = new MyBroadcastReceiver4();
IntentFilter intentFilter4 = new IntentFilter();
intentFilter4.addAction(BROADCAST_ACTION_LOCAL);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(mBroadcastReceiver4, intentFilter4);

//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver4);

//发送应用内广播
button3.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View v) {
        Intent it = new Intent();
        it.setAction(BROADCAST_ACTION_LOCAL);
        it.putExtra("name","local broadcast");
        localBroadcastManager.sendBroadcast(it);
    }
});

//接收应用内广播
public class MyBroadcastReceiver4 extends BroadcastReceiver {
     @Override
     public void onReceive(Context context, Intent intent) {
         showlog("MyBroadcastReceiver4-onReceive");
         String name = intent.getStringExtra("name");
         text3.setText(name);
         showlog("name="+name);
     }
 }

点击按钮,发送广播,Log输出信息:
这里写图片描述

五、onReceive(context, intent)中的context具体类型

(1)对于静态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext;
注:这个context是一个ReceiverRestrictedContext实例,它有两个主要函数被禁掉:registerReceiver()和bindService()。这两个函数在BroadcastReceiver.onReceive()不允许调用。每次Receiver处理一个广播,传递进来的context都是一个新的实例。
(2)对于全局广播的动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Activity Context;
(3)对于通过LocalBroadcastManager动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Application Context。
注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。

六、API重要变迁

还记得本篇开始时说的“静态注册的广播接收器即使app已经退出,只要有相应的广播发出,依然可以接收到,但此种描述自Android 3.1开始有可能不再成立”吗?这里我们探究一下原因:
Android 3.1开始系统在Intent与广播相关的flag增加了参数:

  • FLAG_INCLUDE_STOPPED_PACKAGES:包含已经停止的包(停止:即包所在的进程已经退出)
  • FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包

自Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。
对于系统广播,由于是系统内部直接发出,无法更改此intent flag值,因此,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,如果App进程已经退出,将不能接收到广播。
但是对于自定义的广播,可以通过复写此flag为FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,即使所在App进程已经退出,也能能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是重新新建的。

Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
intent.putExtra("name", "test broadcast");
sendBroadcast(intent);

注:
1. 对于动态注册类型的BroadcastReceiver,由于注册和取消注册是在其他组件(如Activity)中进行,因此,不受此改变影响。
2. 在3.1以前,相信不少app可能通过静态注册方式监听各种系统广播,以此进行一些业务上的处理(如即时app已经退出,仍然能接收到,可以启动service等..),3.1后,静态注册接受广播方式的改变,将直接导致此类方案不再可行。于是,通过将Service与App本身设置成不同的进程已经成为实现此类需求的可行替代方案。


《第一行代码》

一、广播机制简介

  为什么说 Android 中的广播机制更加灵活呢?这是因为 Android 中的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容,这些广播可能是来自于系统的,也可能是来自于其他应用程序的。 Android 提供了一套完整的API,允许应用程序自由地发送和接收广播。发送广播的方法其实之前稍微有提到过一下,如果你记性好的话可能还会有印象,就是借助我们第 2 章学过的 Intent。而接收广播的方法则需要引入一个新的概念,广播接收器(Broadcast Receiver)。
  广播接收器的具体用法将会在下一节中做介绍,这里我们先来了解一下广播的类型。Android 中的广播主要可以分为两种类型,标准广播和有序广播。
  标准广播(Normal broadcasts) 是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。标准广播的工作流程如图 5.1 所示。
  这里写图片描述
  有序广播(Ordered broadcasts) 则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。有序广播的工作流程如图 5.2 所示。
  这里写图片描述

二、接收系统广播

  Android 内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。比如手机开机完成后会发出一条广播,电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条广播等等。如果想要接收到这些广播,就需要使用广播接收器,下面我们就来看一下它的具体用法。

1、动态注册监听网络变化

  广播接收器可以自由地对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广播接收器就能够收到该广播,并在内部处理相应的逻辑。注册广播的方式一般有两种,在代码中注册和在 AndroidManifest.xml 中注册,其中前者也被称为动态注册,后者也被称为静态注册。
  那 么 该 如 何 创 建 一 个 广 播 接 收 器 呢 ? 其 实 只 需 要 新 建 一 个 类 , 让 它 继 承 自BroadcastReceiver, 并重写父类的 onReceive()方法就行了。这样当有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。
  那我们就先通过动态注册的方式编写一个能够监听网络变化的程序,借此学习一下广播接收器的基本用法吧。新建一个 BroadcastTest 项目,然后修改 MainActivity 中的代码,如下所示:

public class MainActivity extends Activity {
    private IntentFilter intentFilter;
    private NetworkChangeReceiver networkChangeReceiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        networkChangeReceiver = new NetworkChangeReceiver();
        registerReceiver(networkChangeReceiver, intentFilter);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(networkChangeReceiver);
    }
    class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();
        }
    }
}

  可以看到,我们在MainActivity中定义了一个内部类NetworkChangeReceiver,这个类是继承自 BroadcastReceiver 的,并重写了父类的 onReceive()方法。这样每当网络状态发生变化时, onReceive()方法就会得到执行,这里只是简单地使用 Toast 提示了一段文本信息。
  然后观察onCreate()方法,首先我们创建了一个IntentFilter的实例,并给它添加了一个值为 android.net.conn.CONNECTIVITY_CHANGE 的 action,为什么要添加这个值呢 ? 因 为 当 网 络 状 态 发 生 变 化 时 , 系 统 发 出 的 正 是 一 条 值 为android.net.conn.CONNECTIVITY_ CHANGE 的广播,也就是说我们的广播接收器想要监 听 什 么 广 播 , 就 在 这 里 添 加 相 应 的 action 就 行 了 。 接 下 来 创 建 了 一 个NetworkChangeReceiver 的实例,然后调用 registerReceiver()方法进行注册,将NetworkChangeReceiver 的 实 例 和 IntentFilter 的 实 例 都 传 了 进 去 , 这 样NetworkChangeReceiver 就 会 收 到 所 有 值 为
  android.net.conn.CONNECTIVITY_CHANGE 的广播,也就实现了监听网络变化的功能。最后要记得,动态注册的广播接收器一定都要取消注册才行,这里我们是在 onDestroy()方法中通过调用 unregisterReceiver()方法来实现的。整体来说,代码还是非常简单的,现在运行一下程序。首先你会在注册完成的时候收到一条广播,然后按下 Home键回到主界面(注意不能按Back键,否则onDestroy()方法会执行),接着按下Menu键→System settings→Data usage 进入到数据使用详情界面,然后尝试着开关 Mobile Data 来启动和禁用网络,你就会看到有 Toast 提醒你网络发生了变化。
  不过只是提醒网络发生了变化还不够人性化,最好是能准确地告诉用户当前是有网络还是没有网络,因此我们还需要对上面的代码进行进一步的优化。修改 MainActivity 中的代码,如下所示:

public class MainActivity extends Activity {
    ……
    class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            ConnectivityManager connectionManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
            if (networkInfo != null && networkInfo.isAvailable()) {
                Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

  在onReceive() 方 法 中 , 首 先 通 过 getSystemService() 方 法 得 到 了ConnectivityManager 的实例,这是一个系统服务类,专门用于管理网络连接的。然后调用 它 的 getActiveNetworkInfo() 方 法 可 以 得 到 NetworkInfo 的 实 例 , 接 着 调 用NetworkInfo 的 isAvailable()方法,就可以判断出当前是否有网络了,最后我们还是通过
Toast 的方式对用户进行提示。
  另外,这里有非常重要的一点需要说明, Android 系统为了保证应用程序的安全性做了规定,如果程序需要访问一些系统的关键性信息,必须在配置文件中声明权限才可以,否则程序将会直接崩溃,比如这里查询系统的网络状态就是需要声明权限的。打开AndroidManifest.xml 文件,在里面加入如下权限就可以查询系统网络状态了:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.broadcasttest"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    ……
</manifest>

2、静态注册实现开机启动

  动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()方法中的。那么有没有什么办法可以让程序在未启动的情况下就能接收到广播呢?这就需要使用静态注册的方式了。
  这里我们准备让程序接收一条开机广播,当收到这条广播时就可以在onReceive()方法里执行相应的逻辑,从而实现开机启动的功能。新建一个 BootCompleteReceiver 继承自BroadcastReceiver,代码如下所示:

public class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
    }
}

  可以看到,这里不再使用内部类的方式来定义广播接收器,因为稍后我们需要在AndroidManifest.xml 中将这个广播接收器的类名注册进去。在 onReceive()方法中,还是简单地使用 Toast 弹出一段提示信息。
  然后修改 AndroidManifest.xml 文件,代码如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.broadcasttest"
    android:versionCode="1"
    android:versionName="1.0" >
    ……
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        ……
        <receiver android:name=".BootCompleteReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
           </intent-filter>
       </receiver>
    </application>
</manifest>

  终于,< application>标签内出现了一个新的标签< receiver>,所有静态注册的广播接收器都是在这里进行注册的。它的用法其实和< activity>标签非常相似,首先通过android:name 来指定具体注册哪一个广播接收器,然后在< intent-filter>标签里加入想要接 收 的 广 播 就 行 了 , 由 于 Android 系 统 启 动 完 成 后 会 发 出 一 条 值 为android.intent.action.BOOT_COMPLETED 的广播,因此我们在这里添加了相应的action。
  另外,监听系统开机广播也是需要声明权限的,可以看到,我们使用< uses-permission>标签又加入了一条 android.permission.RECEIVE_BOOT_COMPLETED 权限。现在重新运行程序后,我们的程序就已经可以接收开机广播了,首先打开到应用程序管理界面来查看一下当前程序所拥有的权限。在桌面按下Menu键→System settings→
Apps,然后点击 BroadcastTest,如图 5.5 所示。
这里写图片描述
  可以看到,我们的程序目前拥有访问网络状态和开机自动启动的权限。然后将模拟器关闭并重新启动,在启动完成之后就会收到开机广播了,如图 5.6 所示。
这里写图片描述
  到目前为止,我们在广播接收器的 onReceive()方法中都只是简单地使用 Toast 提示了一段文本信息,当你真正在项目中使用到它的时候,就可以在里面编写自己的逻辑。需要注意的是,不要在 onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当 onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等,这几个概念我们会在后面的章节中学到。

3、发送自定义广播

  现在你已经学会了通过广播接收器来接收系统广播,接下来我们就要学习一下如何在应用程序中发送自定义的广播。前面已经介绍过了,广播主要分为两种类型,标准广播和有序广播,在本节中我们就将通过实践的方式来看下这两种广播具体的区别。

1)发送标准广播

  在发送广播之前,我们还是需要先定义一个广播接收器来准备接收此广播才行, 不然发出去也是白发。因此新建一个 MyBroadcastReceiver 继承自 BroadcastReceiver,代码如下所示:

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
    }
}

  这里 当 MyBroadcastReceiver 收 到 自 定 义 的 广 播 时 , 就 会 弹 出 received in MyBroadcastReceiver 的提示。然后在 AndroidManifest.xml 中对这个广播接收器进行注册:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.broadcasttest"
    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=".MyBroadcastReceiver">
           <intent-filter>
               <action android:name="com.example.broadcasttest. MY_BROADCAST"/>
            </intent-filter>
        </receiver>
    </application>
</manifest>

  可以看到,这里让MyBroadcastReceiver接收一条值为com.example.broadcasttest.MY_BROADCAST的广播,因此待会儿在发送广播的时候,我们就需要发出这样的一条广播。

public class MainActivity extends Activity {
    ……
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
                sendBroadcast(intent);
            }
        });
        ……
    }
    ……
}

  可以看到,我们在按钮的点击事件里面加入了发送自定义广播的逻辑。首先构建出了一个Intent对象,并把要发送的广播的值传入,然后调用了Context的sendBroadcast()方法将广播发送出去,这样所有监听com.example.broadcasttest.MY_BROADCAST这条广播的广播接收器就会收到消息。此时发出去的广播就是一条标准广播。

2)发送有序广播

  广播是一种可以跨进程的通信方式,这一点从前面接收系统广播的时候就可以看出来了。因此在我们应用程序内发出的广播,其他的应用程序应该也是可以收到的。为了验证这一点,我们需要再新建一个 BroadcastTest2 项目。
  将项目创建好之后,还需要在这个项目下定义一个广播接收器,用于接收上一小节中的自定义广播。新建 AnotherBroadcastReceiver 继承自 BroadcastReceiver,代码如下所示:

public class AnotherBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show();
    }
}

  这里仍然是在广播接收器的 onReceive()方法中弹出了一段文本信息。然后在AndroidManifest.xml 中对这个广播接收器进行注册,代码如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.broadcasttest2"
    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=".AnotherBroadcastReceiver" >
            <intent-filter>
                <action android:name="com.example.broadcasttest.MY_BROADCAST" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

  可以看到 , AnotherBroadcastReceiver 同 样 接 收 的 是com.example.broadcasttest. MY_BROADCAST 这条广播。现在运行 BroadcastTest2项目将这个程序安装到模拟器上,然后重新回到 BroadcastTest 项目的主界面,并点击一下 Send Broadcast 按钮,就会分别弹出两次提示信息,如图 5.8 所示。
这里写图片描述
  这样就强有力地证明了,我们的应用程序发出的广播是可以被其他的应用程序接收到的。
  不过到目前为止,程序里发出的都还是标准广播,现在我们来尝试一下发送有序广播。关闭 BroadcastTest2 项目,然后修改 MainActivity 中的代码,如下所示:

public class MainActivity extends Activity {
    ……
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.broadcasttest. MY_BROADCAST");
                sendOrderedBroadcast(intent, null);
            }
        });
        ……
    }
    ……
}

  可以看到,发送有序广播只需要改动一行代码,即将 sendBroadcast()方法改成sendOrderedBroadcast()方法。 sendOrderedBroadcast()方法接收两个参数,第一个参数仍然是 Intent,第二个参数是一个与权限相关的字符串,这里传入 null 就行了。现在重新运行程序,并点击 Send Broadcast 按钮,你会发现,两个应用程序仍然都可以接收
到这条广播。
  看上去好像和标准广播没什么区别嘛,不过别忘了,这个时候的广播接收器是有先后顺序的,而且前面的广播接收器还可以将广播截断,以阻止其继续传播。那么该如何设定广播接收器的先后顺序呢?当然是在注册的时候进行设定的了,修改AndroidManifest.xml 中的代码,如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.broadcasttest"
    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=".MyBroadcastReceiver">
      <intent-filter android:priority="100" >
           <action android:name="com.example.broadcasttest.MY_BROADCAST"/>
       </intent-filter>
    </receiver>
    </application>
</manifest>

  可以看到,我们通过 android:priority 属性给广播接收器设置了优先级,优先级比较高的广播接收器就可以先收到广播。这里将 MyBroadcastReceiver 的优先级设成了 100,以保证它一定会在 AnotherBroadcastReceiver 之前收到广播。
  既然已经获得了接收广播的优先权,那么 MyBroadcastReceiver 就可以选择是否允许广播继续传递了。修改 MyBroadcastReceiver 中的代码,如下所示:

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received in MyBroadcastReceive", Toast.LENGTH_SHORT).show();
        abortBroadcast();
    }
}

  如果在 onReceive()方法中调用了 abortBroadcast()方法,就表示将这条广播截断,后面的广播接收器将无法再接收到这条广播。现在重新运行程序,并点击一下 SendBroadcast 按钮,你会发现,只有 MyBroadcastReceiver 中的 Toast 信息能够弹出,说明这条广播经过 MyBroadcastReceiver 之后确实是终止传递了。
  

4、使用本地广播

  前面我们发送和接收的广播全部都是属于系统全局广播,即发出的广播可以被其他任何的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样就很容易会引起安全性的问题,比如说我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。
  为了能够简单地解决广播的安全性问题, Android 引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。
  本地广播的用法并不复杂,主要就是使用了一个 LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。下面我们就通过具体的实例来尝试一下它的用法,修改 MainActivity 中的代码,如下所示:

public class MainActivity extends Activity {
    private IntentFilter intentFilter;
    private LocalReceiver localReceiver;
    private LocalBroadcastManager localBroadcastManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        localBroadcastManager = LocalBroadcastManager.getInstance(this); //获取实例
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.broadcasttest.LOCAL_BROADCAST");
                localBroadcastManager.sendBroadcast(intent); //发送本地广播
            }
        });
        intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
        localReceiver = new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver, intentFilter); // 注册本地广播监听器
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(localReceiver);
    }
    class LocalReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show();
        }
    }
}

  有没有感觉这些代码很熟悉?没错,其实这基本上就和我们前面所学的动态注册广播接收器以及发送广播的代码是一样。只不过现在首先是通过 LocalBroadcastManager 的getInstance()方 法 得 到 了 它 的 一 个 实 例 , 然 后 在 注 册 广 播 接 收 器 的 时 候 调 用 的 是LocalBroadcastManager 的 registerReceiver()方法,在发送广播的时候调用的是LocalBroadcastManager 的 sendBroadcast()方法,仅此而已。这里我们在按钮的点击事件里面发出了一条 com.example.broadcasttest. LOCAL_BROADCAST 广播,然后在LocalReceiver 里去接收这条广播。重新运行程序,并点击 Send Broadcast 按钮,效果如图 5.9 所示。
这里写图片描述
  可以看到, LocalReceiver 成功接收到了这条本地广播,并通过 Toast 提示了出来。如 果 你 还 有 兴 趣 进 行 实 验 , 可 以 尝 试 在 BroadcastTest2 中 也 去 接 收com.example.broadcasttest. LOCAL_BROADCAST 这条广播,答案是显而易见的,肯定无法收到,因为这条广播只会在 BroadcastTest 程序内传播。另外还有一点需要说明,本地广播是无法通过静态注册的方式来接收的。其实这也完全可以理解,因为静态注册主要就是为了让程序在未启动的情况下也能收到广播,而发送本地广播时,我们的程序肯定是已经启动了,因此也完全不需要使用静态注册的功能。
  最后我们再来盘点一下使用本地广播的几点优势吧。
  1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄漏的问题。
  2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患。
  3. 发送本地广播比起发送系统全局广播将会更加高效。
  

猜你喜欢

转载自blog.csdn.net/Hystudio_lzu/article/details/80656071