Android系统四大组件之广播事件处理 Broadcast Receiver

Android广播事件处理 Broadcast Receiver


Android 广播机制简介

  • 为了便于进行系统级别的消息推送, Android引入了广播机制.Broadcast Receiver本质上是一种全局的监听器,用于监听系统全局的广播消息。可以非常方便地实现系统中不同组件之间的通信。

  • Android中的每个应用程序都可以对自己感兴趣的广播进行注册, 只接收自己关心的广播内容,广播可能是来自系统的,也可能是来自其他应用程序

  • Android系统提供了一套完整的API, 允许应用程序自由的发送广播接收广播(动态注册和静态注册). 发送广播用Intent,接收广播使用广播接收器BroadcastReceiver

  • Android中的广播主要分为两种类型: 标准广播有序广播.

  • 标准广播
    是一种完全异步执行的广播.在广播发出后,所有的广播接收器几乎都会在同一时刻接收到这条消息.这种广播效率会比较高,但这种广播无法被截断.
    标准广播工作示意图

  • 有序广播
    是一种同步执行的广播, 在广播发出后, 同一时刻只会有一个广播接收器能够接收到这条广播消息, 当这个广播接收器中的逻辑执行完毕后, 广播才会继续传递. 所以此时广播接收器有先后顺序, 优先级高的接收器先收到广播消息, 并且前面的广播接收器可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了.
    有序广播工作示意图


接收系统广播

注册广播接收器方式有两种,在代码中注册(动态注册)和在AndroidManifest.xml中注册(静态注册).

如何创建广播接收器:

  • 新建一个类, 继承BroadcastReceiver, 并重写父类的onReceive()方法.
  • 当有广播到来, onReceive()方法就会执行,具体的逻辑处理可以在这个方法中处理
动态注册监听网络变化

动态注册的广播接收器可以自由地控制注册与注销, 非常灵活, 但是必须要程序启动之后才能接收到广播.
通过动态注册的方式编写一个能够监听网络变化的程序, 借此学习一下广播接收器的基本用法。
新建一个 BroadcastTest项目,然后修改 MainActivity 中的代码,如下所示:

public class MainActivity extends AppCompatActivity {
	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(contest, "network changeds", Toast.LENGTH_SHORT).show();
		}
	}
}		

运行程序, 首先会在注册完成时收到一条广播, 然后按下Home键回到主页面(注意不能按Back, 会执行onDestroy()方法). 接着打开Settings–>Data usage进入到数据使用详情界面. 然后开关Cellular data按钮来启动和禁用网络.看到有Toast提醒网络发送变化

  1. 可以看到, 在MainActivity中定义了一个内部类NetworkChangeReceiver,这个类是继承自BroadcastReceiver,并重写了父类的onReceive()方法.这样每当网络状态发生变化时, onReceive()方法就会得到执行, 这里只是简单地使用Toast提示一段文版信息.
  2. 观察onCreate()方法, 首先创建了IntentFilter的实例,并添加一个值为android.net.conn.CONNECTIVITY_CHANGE的action. 当网络状态发生变化时, 系统会发出一条值为android.net.conn.CONNECTIVITY_CHANGE的广播.广播接收器想要监听什么广播,就要添加相应的action.
  3. 接着创建一个NetworkChangeReceive的实例, 然后调用registerReceiver()方法进行注册. 将NetworkChangeReceive的实例和IntentFilter的实例传入
  4. 最后在onDestroy()方法中通过调用unRegisterReceiver()方法来取消注册.
  • 一般是在Activity.onResume()方法中使用 Contex.registerReceiver() 方法来注册一个广播接收器,在Activity.onPause()中使用unregisterReceiver() 方法来注销一个广播接收器.
  • 不要在onReceive()内执行耗时操作,如果BroadcastReceiver的onReceive()方法不能在10秒内执行完成,Android会认为该程序无响应,会弹出ANR(Application No Response)的对话框,确实需要根据BroadcastReceiver来完成一项比较耗时的操作的,可以通过Intent启动一个Service来完成该操作. 因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条通知栏,或者启动一个服务.
静态注册实现开机启动

静态注册广播接收器,可以在程序未启动的情况下就能接收到广播

实例:让程序接收开机广播, 当收到这条广播就可以在onReceive()方法里执行相应逻辑,从而实现开机启动的功能.
MainActivity.java

/**
 * 接收系统启动完成(ACTION_BOOT_COMPLETED)广播来测试系统广播事件
 * 1:创建MyReceiver 类,该类继承BroadcastReceiver ,覆盖onReceive()方法显示系统启动完成广播信息
 * 2:在AndroidManifest.xml中配置文件中注册该接收器,<intent-filter>中的属性必须是
 * 		"android.intent.action.BOOT_COMPLETED",才能接收系统启动完成的广播事件
 */
publicclass MainActivity extends Activity {
	@Override
	protectedvoid onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Toast.makeText(contest, "network changeds", Toast.LENGTH_SHORT).show();
	}
}

MyReceiver.java

//显示系统启动完成广播接收器
publicclass MyReceiver extends BroadcastReceiver {
	@Override
	publicvoid onReceive(Context context, Intent intent) {
		//显示广播信息
		Log.i("my_tag", "BOOT_COMPLETED!");
		//也可以唤起MainActivity
		Intent i = new Intent(Intent.ACTION_MAIN);
		i.setClass(context, MainActivity.class);
		i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		context.startActivity(i);
	}
}

AndroidManifest.xml

<manifest
...
	<user-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED">
	<application
...
		<receiver
			android:name="com.icesea.systemreceiver.MyReceiver"
			android:enabled="true"
			android:exported="true">
			<intent-filter>
				<action android:name="android.intent.action.BOOT_COMPLETED"/>
			</intent-filter>
		</receiver>
	</application>
</manifest>

运行程序后, 将模拟器关闭并重新启动,在启动完成之后就会接收到开机广播, 打印Log并唤起MainActivity 弹出Toast.


标准广播Action常量

标准广播Action常量

发送自定义广播

广播主要分为两种类型: 标准广播和有序广播.

发送标准广播

Normal Broadcast:普通(标准)广播,是异步的,可以在同一时刻被所有接收者接收到,消息传递的效率高,但接收者不能将处理结果传递给下一个接收者,并且无法终止Broadcast的传播.

步骤:

  1. 在程序组件里构建要广播的Intent,并把要发送的广播的值传入, 然后调用Context的sendBroadcast 方法发送出去.(由于广播是使用Intent进行传递的, 可以通过Intent携带一些数据传递给广播接收器)
  2. 定义一个广播接收器,该广播接收器继承BroadcastReceiver.并且覆盖onReceive()方法来响应事件,接收广播
  3. 注册该广播接收器(可以再代码中注册,也可以在AndroidManifest.xml配置文件中注册)

实例:
发送标准广播
接收标准广播
MyReceiver.java

//在工程中的包中定义一个MyReceiver 类.该类继承BroadcastReceiver 类,覆盖 onReceive()方法来接收广播并显示收到的信息
publicclass MyReceiver extends BroadcastReceiver {
	@Override
	publicvoid onReceive(Context context, Intent intent) {
		//从Intent中获得信息
		String msg = intent.getStringExtra("msg");
		//通过Toast显示
		Toast.makeText(context, "接收到的Intent的Action为: 				
				"+intent.getAction()+"\n消息内容是: "+msg,
				Toast.LENGTH_LONG).show();	
	}
}

然后在AndroidManifest.xml中注册(关键代码):

<receiver
	android:name="com.icesea.customerreceiver.MyReceiver">
	<intent-filter>
		<actionandroid:name="com.icesea.MY_RECEIVER"/>
	</intent-filter>
</receiver>

可以看到,这里让MyReceiver接收一条值为com.icesea.MY_RECEIVER的广播,因此待会在发送广播的时候,我们就需要发出一条这样的广播.
MainActivity.java

/**
 * 1.创建一个MainActivity,声明一个Button对象响应单击事件发出广播,在onCreate()方法中设置当前布局,实例化Button对象,为Button添加单击事件监听器,在onClick()方法中创建一个Intent对象,为其设置Action属性和Extra属性,使用该Intent发出广播
 * 2.在布局文件中添加一个按钮,作为发送广播的触发点
 * 3.在工程中的包中定义一个MyReceiver 类.该类继承BroadcastReceiver 类,覆盖 onReceive()方法来接收广播并显示收到的信息
 * 4:在工程的AndroidManifest.xml配置文件中声明广播接收器组件
 */
publicclass MainActivity extends Activity {
	private Button btn;
	@Override
	protectedvoid onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//获取组件
		btn = (Button)findViewById(R.id.Button01);
		//为Button添加单机事件监听器
		btn.setOnClickListener(new View.OnClickListener() {
			@Override
			publicvoid onClick(View v) {
				//创建Intent
//注:可以直接new Intent("com.icesea.MY_RECEIVER");
				Intent intent = new Intent();
				//设置Action属性
				intent.setAction("com.icesea.MY_RECEIVER");
				//为Intent添加附加信息
				intent.putExtra("msg", "001,001,我是小鱼包,收到请回复1");
				//发出广播
				sendBroadcast(intent);
			}
		});
	}
}

可以看到,在按钮的点击事件里面加入了发送自定义广播的逻辑,首先构建一个Intent对象,并把要发送的广播的值传入(Action值),同时也可以选择在Intent中携带一些数据,然后调用Context的sendBroadcast()方法将这个广播发送出去,这样所有监听com.icesea.MY_RECEIVER这条广播的接收器就会收到消息.此时发出去的广播就是一条标准广播.

广播是一种可以跨进程的通信方式,因此在应用程序内发出的广播,其他的应用程序也可以接收到,接收系统发出的广播就是一个实例.
为了验证这点,再新建一个BroadcastTest2项目,将项目创建好之后,在项目下定义一个广播接收器,用于接收另一个程序发出的
AnotherReceiver.java

//在工程中的包中定义一个AnotherReceiver 类.该类继承BroadcastReceiver 类,覆盖 onReceive()方法来接收广播并显示收到的信息
publicclass AnotherReceiver extends BroadcastReceiver {
	@Override
	publicvoid onReceive(Context context, Intent intent) {
		//从Intent中获得信息
		String msg = intent.getStringExtra("msg");
		//通过Toast显示
		Toast.makeText(context, "接收到其他程序发出的广播,其Intent的Action为: "+intent.getAction()+"\n消息内容是: "+msg,
				Toast.LENGTH_LONG).show();	
	}
}

然后也需要在AndroidManefest.xml中对这个广播接收器进进行注册:

<receiver
	android:name=".AnotherReceiver">
	<intent-filter>
		<action android:name="com.icesea.MY_RECEIVER"/>
	</intent-filter>
</receiver>

然后回到customerreceiver项目的主界面(发出广播的应用程序),点击按钮发出广播,就会发现分别弹出了两次提示信息
接收广播
接收其他程序发出的广播

发送有序广播

Ordered Broadcast:有序广播,接收者将按预先声明的优先级依次接收Broadcast(优先级声明在元素的android:priority属性中,数越大优先级别越高,取值范围为-1000~1000;优先级别也可以调用setPriority()进行设置).优先到的接收者可以终止Broadcast(调用BroadcastReceiver的abortBroadcast()方法)如果Broadcast被前面的接收者终止,后面的接收者就再也无法获取到.优先接收者还可以通过setResultExtras(Bundle)方法将处理结果存入Broadcast中,然后传给下一个接收者,下一个接收者通过代码:Bundle bundle = getResultExtras(true)可以获取上一个接收者存入的数据.

示例:
SortedBroadcast.java

/**
 * 在该程序中只有一个按钮,用于发送广播,它会按照优先级依次触发每个BroadcastReceiver的onReceive()方法
 */
publicclass SortedBroadcast extends Activity {
	//声明组件
	private Button send;
	@Override
	protectedvoid onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		//获取组件实例
		send = (Button)findViewById(R.id.send);
		//绑定按钮的点击事件监听器
		send.setOnClickListener(new View.OnClickListener() {
			@Override
			publicvoid onClick(View v) {
				//创建一个Intent
				Intent intent = new Intent();
				//设置Action属性
				intent.setAction(
"com.icesea.orderedbroadcast.ORDERED_BROADCAST");
				//添加信息
				intent.putExtra("msg", "这是一则广播消息!");
				//启动BroadCastReceiver,第二个参数是与权限相关的字符串
				sendOrderedBroadcast(intent, null);
			}
		});
	}
}

第一个BroadcastReceiver
FirstReceiver.java

publicclass FirstReceiver extends BroadcastReceiver {
	@Override
	publicvoid onReceive(Context context, Intent intent) {
		// 获取信息
		String msg = intent.getStringExtra("msg");
		// 显示信息
		Toast.makeText(context,"接收到的Intent 的Action 为: "
+ intent.getAction() + "\n消息内容是: "
				+ msg, Toast.LENGTH_LONG).show();
		Log.i("mylog", "one");

		// 创建Bundle
		Bundle bundle = new Bundle();
		// 添加结果信息
		bundle.putString("first", "这是第一个BroadcastReceiver存入的消息");
		// 将Bundle存入Broadcast
		setResultExtras(bundle);
		// 取消Broadcast 的继续传播
		// abortBroadcast();
	}
}

AndroidManifest.xml中添加:

<receiver
	android:name="com.icesea.orderedbroadcast.FirstReceiver">
	<intent-filter
		android:priority="20">
			<action android:name="com.icesea.orderedbroadcast.ORDERED_BROADCAST"/>
	</intent-filter>
</receiver>

第二个BroadcastReceiver
SecondReceiver.java

publicclass SecondReceiver extends BroadcastReceiver {
	@Override
	publicvoid onReceive(Context context, Intent intent) {
		Log.i("mylog","two");
		//获取上一个接收者存入的信息
		Bundle bundle = getResultExtras(true);
		String first = bundle.getString("first");
		//显示该信息
		Toast.makeText(context, "第一个Broadcast接收者存入的消息为: \n"
				+first, Toast.LENGTH_LONG).show();
	}
}

AndroidManifest.xml中添加:

<receiver
	android:name="com.icesea.orderedbroadcast.SecondReceiver">
	<intent-filter
		android:priority="0">
			<action android:name="com.icesea.orderedbroadcast.ORDERED_BROADCAST"/>
	</intent-filter>
</receiver>

截图显示:
发送广播
第一个广播接收器
第二个广播接收器
前面我们发送和接收的广播全部属于系统全局广播, 发出的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播.这样就容易引起安全性问题.比如我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获.或者其他的应用程序不停地向我们广播接收器发送各种垃圾广播.
为了解决广播的安全性问题, Android还引入了一套本地广播机制,使用这个机制发出的广播只能在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播.

本地广播将在下篇博文进行阐述.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值