【Android学习笔记】Broadcast receiver

原创 2015年11月18日 17:17:31

一、What is a Broadcast receiver?

广播是一种广泛运用的在应用程序之间传输信息的机制 。而 BroadcastReceiver 是对发送出来的广播进行过滤接收并响应的一类组件;
来自普通应用程序,如一个应用程序通知其他应用程序某些数据已经下载完毕。
BroadcastReceiver 自身并不实现图形用户界面,但是当它收到某个通知后, BroadcastReceiver 可以启动 Activity 作为响应,或者通过 NotificationMananger 提醒用户,或者启动 Service 等等。

广播一般有两种分类方式

第一种 普通广播和有序广播

标准广播(Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。

有序广播(Ordered broadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断(abortBroadcast())正在传递的广播,这样后面的广播接收器就无法收到广播消息了。

第二种 全局广播和本地广播

全局广播 发出的广播可以被其他任何的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播,当携带数据的时候会存在安全性问题。

本地广播 只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,比较安全,主要通过LocalBroadcastManager来管理和发送。

二、How to create and use it ?

与其他四大组件类似,广播接收者也是通过继承系统的BroadcastReceiver类并重写某些生命周期方法(主要是onReceive)来创建,并且需要在manifest配置文件中进行配置。

但是这里有广播接收者的特性,那就是它可以通过代码的方式进行动态的注册而非一定在manifest中进行注册。动态注册的方式往往是利用内部类的方式来实现。但是像开机启动只能通过静态的方式进行注册。

就像在上面介绍的一样,广播的发送也有两种方式即发送普通广播sendBroadcast和发送有序广播sendOrderedBroadCast。

三、Typical use cases

1.发送有序广播

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

public class FirstReceiver extends BroadcastReceiver {
	
	private static final String TAG = "OrderedBroadcast";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = intent.getStringExtra("msg");
		Log.i(TAG, "FirstReceiver: " + msg);
		
		Bundle bundle = new Bundle();
		bundle.putString("msg", msg + "@FirstReceiver");
		setResultExtras(bundle);
	}

}
public class SecondReceiver extends BroadcastReceiver {
	
	private static final String TAG = "OrderedBroadcast";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = getResultExtras(true).getString("msg");
		Log.i(TAG, "SecondReceiver: " + msg);
		
		Bundle bundle = new Bundle();
		bundle.putString("msg", msg + "@SecondReceiver");
		setResultExtras(bundle);
	}

}

public class ThirdReceiver extends BroadcastReceiver {
	
	private static final String TAG = "OrderedBroadcast";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = getResultExtras(true).getString("msg");
		Log.i(TAG, "ThirdReceiver: " + msg);
	}

}

我们注意到,在FirstReceiver和SecondReceiver中最后都使用了setResultExtras方法将一个Bundle对象设置为结果集对象,传递到下一个接收者那里,这样以来,优先级低的接收者可以用getResultExtras获取到最新的经过处理的信息集合。

代码改完之后,我们需要为三个接收者注册广播地址,我们修改一下AndroidMainfest.xml文件:
        <receiver android:name=".FirstReceiver">
        	<intent-filter android:priority="1000">
        		<action android:name="android.intent.action.MY_BROADCAST"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>
        <receiver android:name=".SecondReceiver">
        	<intent-filter android:priority="999">
        		<action android:name="android.intent.action.MY_BROADCAST"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>
        <receiver android:name=".ThirdReceiver">
        	<intent-filter android:priority="998">
        		<action android:name="android.intent.action.MY_BROADCAST"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>

我们看到,现在这三个接收者的<intent-filter>多了一个android:priority属性,并且依次减小。这个属性的范围在-1000到1000,数值越大,优先级越高。

现在,我们需要修改一下发送广播的代码,如下:
    public void send(View view) {
    	Intent intent = new Intent("android.intent.action.MY_BROADCAST");
    	intent.putExtra("msg", "hello receiver.");
    	sendOrderedBroadcast(intent, "scott.permission.MY_BROADCAST_PERMISSION");
    }

注意,使用sendOrderedBroadcast方法发送有序广播时,需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为null,则表示接收者若要接收此广播,需声明指定权限。这样做是从安全角度考虑的,例如系统的短信就是有序广播的形式,一个应用可能是具有拦截垃圾短信的功能,当短信到来时它可以先接受到短信广播,必要时终止广播传递,这样的软件就必须声明接收短信的权限。

所以我们在AndroidMainfest.xml中定义一个权限:
    <permission android:protectionLevel="normal"
    			android:name="scott.permission.MY_BROADCAST_PERMISSION" />
然后使用:
<uses-permission android:name="scott.permission.MY_BROADCAST_PERMISSION" />
更多关于权限的说明可以参考这里


2.开机启动服务

我们经常会有这样的应用场合,比如消息推送服务,需要实现开机启动的功能。要实现这个功能,我们就可以订阅系统“启动完成”这条广播,接收到这条广播后我们就可以启动自己的服务了。我们来看一下BootCompleteReceiver和MsgPushService的具体实现:

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

public class BootCompleteReceiver extends BroadcastReceiver {
	
	private static final String TAG = "BootCompleteReceiver";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		Intent service = new Intent(context, MsgPushService.class);
		context.startService(service);
		Log.i(TAG, "Boot Complete. Starting MsgPushService...");
	}

}

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MsgPushService extends Service {

	private static final String TAG = "MsgPushService";
	
	@Override
	public void onCreate() {
		super.onCreate();
		Log.i(TAG, "onCreate called.");
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(TAG, "onStartCommand called.");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}
}

        <!-- 开机广播接受者 -->
        <receiver android:name=".BootCompleteReceiver">
        	<intent-filter>
        		<!-- 注册开机广播地址-->
        		<action android:name="android.intent.action.BOOT_COMPLETED"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>
        <!-- 消息推送服务 -->
        <service android:name=".MsgPushService"/>

我们看到BootCompleteReceiver注册了“android.intent.action.BOOT_COMPLETED”这个开机广播地址,从安全角度考虑,系统要求必须声明接收开机启动广播的权限,于是我们再声明使用下面的权限:


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

3.网络状态变化
在某些场合,比如用户浏览网络信息时,网络突然断开,我们要及时地提醒用户网络已断开。要实现这个功能,我们可以接收网络状态改变这样一条广播,当由连接状态变为断开状态时,系统就会发送一条广播,我们接收到之后,再通过网络的状态做出相应的操作。下面就来实现一下这个功能:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import android.widget.Toast;

public class NetworkStateReceiver extends BroadcastReceiver {
	
	private static final String TAG = "NetworkStateReceiver";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		Log.i(TAG, "network state changed.");
		if (!isNetworkAvailable(context)) {
			Toast.makeText(context, "network disconnected!", 0).show();
		}
	}
	
	/**
	 * 网络是否可用
	 * 
	 * @param context
	 * @return
	 */
	public static boolean isNetworkAvailable(Context context) {
		ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo[] info = mgr.getAllNetworkInfo();
		if (info != null) {
			for (int i = 0; i < info.length; i++) {
				if (info[i].getState() == NetworkInfo.State.CONNECTED) {
					return true;
				}
			}
		}
		return false;
	}

}
        <receiver android:name=".NetworkStateReceiver">
        	<intent-filter>
        		<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>

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

4.电量变化
如果我们阅读软件,可能是全屏阅读,这个时候用户就看不到剩余的电量,我们就可以为他们提供电量的信息。要想做到这一点,我们需要接收一条电量变化的广播,然后获取百分比信息,这听上去挺简单的,我们就来实现以下:
package com.scott.receiver;

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

public class BatteryChangedReceiver extends BroadcastReceiver {

	private static final String TAG = "BatteryChangedReceiver";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		int currLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);	//当前电量
		int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);		//总电量
		int percent = currLevel * 100 / total;
		Log.i(TAG, "battery: " + percent + "%");
	}

}

        <receiver android:name=".BatteryChangedReceiver">
        	<intent-filter>
        		<action android:name="android.intent.action.BATTERY_CHANGED"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>

当然,有些时候我们是要立即获取电量的,而不是等电量变化的广播,比如当阅读软件打开时立即显示出电池电量。我们可以按以下方式获取:

Intent batteryIntent = getApplicationContext().registerReceiver(null,
        new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
int currLevel = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
int total = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
int percent = currLevel * 100 / total;
Log.i("battery", "battery: " + percent + "%");

四、Others

参考文献:

http://blog.csdn.net/liuhe688/article/details/6955668

官方文档

《第一行代码Android》

Android中Broadcast Receiver的两种注册方法和区别

broadcast receiver:用以监听系统或用户程序broadcast的Intent,它本质上是系统的一种全局监听器(与onXxxxListener相似但不同),只要存在与之匹配的Intent...
  • u012293194
  • u012293194
  • 2016年03月10日 10:20
  • 498

Android四大组件--Broadcast Receiver详解

本文主要讲述了: 一、BroadcastReceiver概述: 二、BroadcastReceiver事件分类 三、BroadcastReceiver事件的编程流程 四、两类Bro...
  • u011067360
  • u011067360
  • 2014年04月26日 17:41
  • 2785

安卓四大控件之BroadcastReceiver详解

BroadcastReceiver详解广播的概念Android:系统在产生某个事件时发送广播,应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件。 Android系统在运行的过程中,会产生很...
  • qq_27280457
  • qq_27280457
  • 2016年07月06日 17:00
  • 18802

android broadcastReceiver启用禁用

有时候我们需要在我们需要启用receiver的时候启用,不需要的时候就不想让代码去执行receiver里面的函数方法。 两种receiver,现在只看静态注册的receiver。因为动态注册的rec...
  • sheofir
  • sheofir
  • 2013年01月26日 15:17
  • 518

关于BroadCastReceiver安全性的思考

介绍了避免自己app中的广播响应其他应用中的广播,增加广播安全性的四种解决方案...
  • yuanzeyao2008
  • yuanzeyao2008
  • 2014年08月30日 23:17
  • 4040

Android静态安全检测 -> Broadcast Receiver组件暴露

Broadcast Receiver组件暴露 - exported属性 1. android:exported 该属性用来标示,当前Broadcast Receiver是否可以从当前应用外部获取Re...
  • u013107656
  • u013107656
  • 2016年07月12日 17:17
  • 2343

Android中的BroadcastReceiver设置permission

 To enforce a permission when sending, you supply a non-null permission argument to sendBroadcas...
  • qq_17041257
  • qq_17041257
  • 2015年08月29日 21:41
  • 1278

Broadcast Receiver的静态注册&动态注册

Broadcast Receiver有两种注册方式,一是静态注册,一是动态注册;二者在实现方法上不尽相同,使用场景也随之不同;静态注册的Broadcat会从application启动开始就一直常驻监听...
  • yuanzihui
  • yuanzihui
  • 2016年02月26日 17:41
  • 1748

Android Broadcast Receiver 基础详解

Android中的广播机制十分灵活,因为Android中的每个应用程序都可以对自己感兴趣的广播进行注册。它提供了一套完整的API允许应用程序自由的发送和接受广播。 广播分为两类:1.标准广播,是一种完...
  • edcSam
  • edcSam
  • 2016年08月29日 10:15
  • 600

Android BroadcastReceiver生命周期分析

经常用到BroastReceiver,
  • mahaiming1990
  • mahaiming1990
  • 2016年04月29日 15:39
  • 394
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【Android学习笔记】Broadcast receiver
举报原因:
原因补充:

(最多只允许输入30个字)