Android开发系列5——BroadcastReceiver详解

前言

BroadcastReceiver(广播接收器)是Android四大组价之一,是一种广泛运用的在应用之间消息传输机制。顾名思义,是一个接收广播(broadcast intent)的一个类。所以想理解好BroadcastReceiver工作原理,必须带着一些问题去看:

  • 1.广播的机制是什么?
  • 2.广播是怎么发送出来的?
  • 3.广播内容是什么,怎么传递给接收器?
  • 4.BroadcastReceiver怎么接收广播的内容?
  • 5.广播的种类?

Android的广播机制是一个典型的:发布-订阅模式,就是观察者模式。(很多博客会把广播机制说成:观察者模式来说)。它最大的特点就是发送方不关心接收方是否接收到数据,也不关心接收方是如何处理数据。通过这样的形式来达到接收双方的完全解耦。一个广播可以有一个或多个接收者。

监听器模式: 事件源经过事件的封装传给监听器,当事件源触发事件后,监听器接收到事件对象可以回调事件的方法


观察者模式: 观察者(Observer)相当于事件监听者,被观察者(Observable)相当于事件源和事件,执行逻辑时通知observer即可触发oberver的update, 同时可传参数给被观察者

广播的类型
  • 普通广播(Normal Broadcast)
  • 有序广播(Ordered Broadcast)
  • 本地广播(Local Broadcast)
  • 粘性广播(Sticky Broadcast)
  • 系统广播(System Broadcast)
BroadcastReceiver 注册类型
  • 静态注册(常驻)
  • 动态注册(非常驻)

一、普通广播(Normal Broadcast)

普通广播(Normal Broadcast)是一种异步、无序。

  • 优点: 传播效率高
  • 缺点: BroadcastReceiver 接收无序,每个接收器之间不能把处理的结果传给下一个接收者。不能终止已经发出的广播。

Activity 页面:

public class MainActivity extends AppCompatActivity {
    private final String NORMAL_ACTION = "com.ftimage.NormalBroadcast";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.sendBroad).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(NORMAL_ACTION);
                intent.putExtra("Broad_Msg", "Hello World!Broad :");
				
				// 广播中携带数据
                Bundle bundle = new Bundle();
                bundle.putString("Broad_Local","Send Broad before!");

                sendBroadcast(intent);
            }
        });
    }
}



BroadcastReceiver A

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class NormalOneReceiver extends BroadcastReceiver {

    private static final String tag = "NormalOneReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        
        String msg = intent.getStringExtra("Broad_Msg");
        Log.e(tag, msg + tag);

    }
}

AndroidManifest.XML 文件配置:

<receiver android:name=".NormalOneReceiver">
        <intent-filter>
            <action android:name="com.ftimage.NormalBroadcast"></action>
         </intent-filter>
</receiver>

二、有序广播(Ordered Broadcast)

有序广播(Ordered Broadcast)是同步执行的广播。广播发出去同一时刻只有一个广播接收器能收到这条消息,当本条接收器执行完毕,广播才继续传播。

  • 优点: 广播可以根据优先级先后执行(AndroidManifest.xml中receiver标签下intent-filter中的android:priority属性来设置,数值越大优先级越高,数值越小优先级越低);可以中断广播(abortBroadcast()方法);接收者之间可以使用上一个接收者处理后的结果。
  • 缺点: 效率比较低。

Activity 页面:

public class MainActivity extends AppCompatActivity {

   private final String NORMAL_ACTION = "com.ftimage.OrderBroadcast";
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       findViewById(R.id.sendBroad).setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Intent intent = new Intent(NORMAL_ACTION);
               intent.putExtra("Broad_Msg", "Hello World!Broad :");

               Bundle bundle = new Bundle();
               bundle.putString("Broad_Local","Send Broad before!");
   			   sendOrderedBroadcast(intent,null);

           }
       });

   }
}

BroadcastReceiver的类:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;


// BroadcastReceiver 第一个
public class OrderOneReceiver extends BroadcastReceiver {
    private static final String tag = "OrderOneReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {

        String msg = intent.getStringExtra("Broad_Msg");
        Log.e(tag, msg + tag);

        Bundle bundle = new Bundle();
        bundle.putString("msg", "OrderOneReceiver的结果传递给下一个Receiver");
        setResultExtras(bundle);
    }
}


// BroadcastReceiver 第二个
public class OrderTwoReceiver extends BroadcastReceiver {
    private static final String tag = "OrderTwoReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {

        String msg = intent.getStringExtra("Broad_Msg");
        Log.e(tag, msg + tag);

        String resultMsg = getResultExtras(true).getString("msg");
        Log.e(tag, "从上一条Receiver获取的结果:" + resultMsg);


        Bundle bundle = new Bundle();
        bundle.putString("msg", "OrderTwoReceiver的结果传递给下一个Receiver");
        setResultExtras(bundle);

    }
}


// BroadcastReceiver 第三个
public class OrderThirdReceiver extends BroadcastReceiver {

    private static final String tag = "OrderThirdReceiver";
    private Intent receverIntent;
    @Override
    public void onReceive(Context context, Intent intent) {

        String msg = intent.getStringExtra("Broad_Msg");
        Log.e(tag, msg + tag);


        String resultMsg = getResultExtras(true).getString("msg");
        Log.e(tag, "从上一条Receiver获取的结果:" + resultMsg);

    }
}

其中,setResultExtras() 和 getResultExtras()是BroadcastReceiver消息传递的类方法。

AndroidManifest.XML 文件配置:

<receiver android:name=".OrderOneReceiver">
            <intent-filter android:priority="110">
                <action android:name="com.ftimage.OrderBroadcast"></action>
            </intent-filter>
        </receiver>

        <receiver android:name=".OrderTwoReceiver">
            <intent-filter android:priority="100">
                <action android:name="com.ftimage.OrderBroadcast"></action>
            </intent-filter>
        </receiver>

        <receiver android:name=".OrderThirdReceiver">
            <intent-filter android:priority="90">
                <action android:name="com.ftimage.OrderBroadcast"></action>
            </intent-filter>
        </receiver>

打印出来的log:

03-18 17:02:52.976 4520-4520/com.ftimage.firstandroid E/OrderOneReceiver: Hello World!Broad :OrderOneReceiver
03-18 17:02:52.980 4520-4520/com.ftimage.firstandroid E/OrderTwoReceiver: Hello World!Broad :OrderTwoReceiver
03-18 17:02:52.980 4520-4520/com.ftimage.firstandroid E/OrderTwoReceiver: 从上一条Receiver获取的结果:OrderOneReceiver的结果传递给下一个Receiver
03-18 17:02:52.984 4520-4520/com.ftimage.firstandroid E/OrderThirdReceiver: Hello World!Broad :OrderThirdReceiver
03-18 17:02:52.984 4520-4520/com.ftimage.firstandroid E/OrderThirdReceiver: 从上一条Receiver获取的结果:OrderTwoReceiver的结果传递给下一个Receiver

可以看出 Receiver 接收广播时,“priority”越大优先级越高,且 Receiver之间也能通过接收器的类方法传递数据

此外,BroadcastReceiver 也能调用 abortBroadcast() 方法截断广播,会使低优先级的广播接收器就无法接收到广播

三、本地广播(Local Broadcast)

本地广播只能在应用内部进行传递,而且广播接收器也只能接收本应用内自身发出的广播

本地广播是使用 LocalBroadcastManager 来对广播进行管理

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

使用本地监听管理器,进行本地广播的例子如下:

本地BroadcastReceiver

public class LocalReceiver extends BroadcastReceiver {

    private final String TAG = "LocalReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "----本地广播-----");
    } 
}

Activity上的

public class MainActivity extends AppCompatActivity  {

    private LocalBroadcastManager localBroadcastManager;
    private LocalReceiver localReceiver;

    private final String LOCAL_ACTION = "com.ftimage.local";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

		// 本地监听管理器
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localReceiver = new LocalReceiver();
        IntentFilter filter = new IntentFilter(LOCAL_ACTION);
        localBroadcastManager.registerReceiver(localReceiver, filter);

        findViewById(R.id.sendBroad).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent(LOCAL_ACTION);
                localBroadcastManager.sendBroadcast(intent);
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 销毁本地监听
        localBroadcastManager.unregisterReceiver(localReceiver);
    }
}

四、粘性广播(Sticky Broadcast)

粘性广播通过Context.sendStickBroadcast()方法来发送,用此方法发送的广播会一直滞留,当有匹配此广播的接收器被注册后,该广播接收器就会收到此广播。使用此广播时,需要获得BROADCAST_STICKY权限。(在 android 5.0/api 21后不再推荐使用)

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

五、系统广播(System Broadcast)

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

六、静态注册和动态注册

静态注册:

就是将广播的注册行为在清单文件AndroidManifest.xml中进行。静态注册的广播接收者只要App在系统中运行则一直可以接收到广播消息,上边的例子都是静态注册。

application标签内出现了一个新的标签receiver,所有静态注册的广播接收器都是在这里进行注册的。通过android:name来指定具体注册哪一个广播接收器。然后在intent-filter标签里加入想要接收的广播。 要销毁静态注册的广播接收器可以通过PackageManager的Receiver禁用。

动态注册:

直接使用IntentFilter来注册,例子如下:


public class MainActivity extends AppCompatActivity {

   private final String NORMAL_ACTION = "com.ftimage.DynamicReceiver";
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
		
		IntentFilter filter = new IntentFilter();
        filter.addAction(NORMAL_ACTION);
        dynamicReceiver = new DynamicReceiver();	          			
        registerReceiver(dynamicReceiver,filter);

       findViewById(R.id.sendBroad).setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Intent intent = new Intent();
         //设置与动态相同的Action,方便同时触发静态与动态
         intent.setAction(NORMAL_ACTION);
         intent.putExtra("info","您现在使用的是动态注册");
         sendBroadcast(intent);//默认广播
           }
       });
   }

	class DynamicReceiver extends BroadcastReceiver{
         @Override
         public void onReceive(Context context, Intent intent) {
            Toast t = Toast.makeText(context,"动态广播:"+ intent.getStringExtra("info"), Toast.LENGTH_SHORT);
             t.setGravity(Gravity.TOP,0,0);
             t.show();
         }
     }
}

七、BroadcastCeceiver使用的细节

发送和接收到的广播全都是属于系统全局广播,即发出的广播可以被其他应用接收到,而且也可以接收到其他应用发送出的广播,这样可能会有不安全因素。使用动态注册广播接收器存在一个问题,即系统内的任何应用均可监听并触发我们的 Receiver 。可以通过在AndroidManifest.XML文件中的< receiver > 标签添加一个android:exported=“false” 属性,标明该 Receiver 仅限应用内部使用。这样,系统中的其他应用就无法接触到该 Receiver 了

此外,也可以选择创建自己的使用权限,即在清单文件中添加一个 < permission > 标签来声明自定义权限

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

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

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

总结

广播(Broadcast)是在组件之间传播数据的一种机制,这些组件可以位于不同的进程中,起到进程间通信的作用。但是广播存在一个非常大的效率问题,所以在使用过程中需要注意使用。

持续更新中……

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值