首页 > 代码库 > Android之旅十七 android中的广播使用

Android之旅十七 android中的广播使用

广播是一种广泛运用在应用程序之间传输信息的机制,android中的广播用于监听系统事件或应用程序事件!android中的广播包括普通广播、有序广播以及异步广播(粘性广播)!

广播又有常驻型广播和非常驻型广播,常驻型广播是在xml中进行注册的,当应用程序关闭后,如果有对应的广播发送过来,广播接收器还是能够被激活;非常驻型广播是在代码中进行注册的,当应用程序关闭,广播也就取消了,我们可以在Activity中的onCreate或者onResume方法中注册广播,然后在onDestory或者onPause方法中取消注册广播;

注意:如果是非常驻型广播,应用程序关闭后,必须取消注册广播,否则会抛出异常!!

普通广播的发送

普通广播的发送使用方式:

sendBroadcast(Intent intent):intent表示意图,所有匹配该广播的意图都能收到该广播信息

sendBroadcast(intent, String receiverPermission);intent与上面一样,receiverPermission表示权限,与之匹配权限的广播才能接收到相应的广播,如果为null,表示不经许可的要求!

一、使用sendBroadcast(Intent intent)发送广播

1)通过代码注册非常驻型广播:

	
	//定义两个广播接收者
	BroadcastReceiver receiver1=new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			System.out.println("receiver1 started!");
		}
	};
	BroadcastReceiver receiver2=new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			System.out.println("receiver2 started!");
		}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//注册广播
		IntentFilter filter=new IntentFilter();
		filter.addAction("com.xin.action.broadcast");
		registerReceiver(receiver1, filter);
		registerReceiver(receiver2, filter);
	}
	
	@Override
	protected void onPause() {
		super.onPause();
		//取消注册广播
		unregisterReceiver(receiver1);
		unregisterReceiver(receiver2);
	}
代码注册属于非常驻型广播,我们需在Activity相应的生命周期中取消注册广播:unregisterReceiver

2)通过xml文件注册常驻型广播,此时的MyBroadcast1、MyBroadcast2为两个广播类:

public class MyBroadcast1 extends BroadcastReceiver{

	@Override
	public void onReceive(Context context, Intent intent) {
		System.out.println("MyBroadcast1 started!");
	}
}
public class MyBroadcast2 extends BroadcastReceiver{

	@Override
	public void onReceive(Context context, Intent intent) {
		System.out.println("MyBroadcast2 started!");
	}
}

在AndroidManifest.xml中的application中添加:

        <receiver android:name=".MyBroadcast1">
            <intent-filter>
                <action android:name="com.xin.action.broadcast"/>
            </intent-filter>
        </receiver>
        <receiver android:name=".MyBroadcast2">
            <intent-filter>
                <action android:name="com.xin.action.broadcast"/>
            </intent-filter>
        </receiver>
代码中的IntentFilter和xml文件中的intent-filter是一样的,都是Intent意图,表示Intent(String actionName)发送出去的广播能被哪些广播接收者所接收!

3)广播的发送:与之匹配的intent意图的广播将被激活

			Intent intent=new Intent("com.xin.action.broadcast");
			sendBroadcast(intent);
输出结果为:MyBroadcast1 started,MyBroadcast2 started!!

二、使用sendBroadcast(intent, String receiverPermission)发送广播

第二个参数的介绍如上面所示,指的是一个权限,我们需在AndroidManifest.xml中声明一个权限:

<permission android:name="com.xin.permission" android:protectionLevel="normal"/>
里面还有很多的属性可供我们选择,大家可以去自己去了解一下;

然后我们的发送广播方和接收广播方都需要该权限定义:

发送广播方使用该权限:

<uses-permission android:name="com.xin.permission"/>
通过方法发送广播:sendBroadcast(intent,"com.xin.permission");其中第二个参数表示我们定义的权限name

接收方声明广播权限:

        <receiver android:name=".MyBroadcast1" android:permission="com.xin.permission">
            <intent-filter>
                <action android:name="com.xin.action.broadcast"/>
            </intent-filter>
        </receiver>
这样,使用带权限的广播就定义好了,我们在发送方和接收方都需要给权限进行定义,否则消息发送不过去!

注意,通过sendBroadcast(intent,"com.xin.permission");发送的广播,并不一定需要在receiver中添加android:permission才能接收到,测试发现,没有添加这个也能接收到:

        <receiver android:name=".MyBroadcast2">
            <intent-filter>
                <action android:name="com.xin.action.broadcast"/>
            </intent-filter>
        </receiver>

有序广播的发送

顾名思义,有序广播就是广播的发送是按照顺序进行的,它根据优先级别的定义android:priority的高低来进行有序发送,一个接受完发给下一个接收,优先级越高,表示它接收到的广播级别高,android:priority的范围一般是在-1000到1000之间;
有序广播和普通广播之间的区别:

有序广播和无序广播的区别:我们发送完无序广播之后,我们不知道谁先接收谁后接收,更不要说当这个接收了之后不要再发给另外的了。而有序广播就可以做到这一点,它通过设置优先级可以决定广播接受者的顺序。

有序广播的发送方式:

sendOrderedBroadcast(intent, receiverPermission);

sendOrderedBroadcast(intent, receiverPermission, resultReceiver,

 scheduler, initialCode, initialData, initialExtras)

意图,广播,所有匹配的这一意图将接收机接收广播。

receiverPermission 这是权限,一个接收器必须持以接收您的广播。如果为 null ,不经许可的要求。 
resultReceiver 您自己 BroadcastReceiver 来当作最后的广播接收器。 
调度自定义处理程序,用以安排 resultReceiver 回调 ; 如果为 null 将语境中的主线程举行。 
initialCode 一种结果代码的初始值。通常为 Activity.RESULT_OK 。这个值是 -1 ;为其他 int 型 也可以,如 0,1,2; 
initialData 一种结果数据的初始值。通常情况下为空 , 是 String 类型 ;
initialExtras 一种结果额外的初始值。通常情况下为空 , 是 Bundle;

有序广播需注意方面:

1,  该广播的级别有级别之分,级别数值是在 -1000 到 1000 之间 , 值越大 , 优先级越高;
2,  同级别接收是先后是随机的,再到级别低的收到广播;
3,  同级别接收是先后是随机的,如果先接收到的把广播截断了,同级别的例外的接收者是无法收到该广播的,截断广播的方式:abortBroadcast() ;
 4 ,能截断广播的继续传播,高级别的广播收到该广播后,可以决定把该钟广播是否截断掉。
5 ,实验现象,在这个方法发来的广播中,代码注册方式中,收到广播先后次序为:注明优先级的、代码
代码演示:

下面给大家演示一下发送有序广播,并且通过Intent在广播之间传递数据,因为关于权限那块上面已经说了,所以在有序广播这里就不再描述了,通过sendOrderedBroadcast(intent, receiverPermission);方法发送有序广播,第二个参数就设置为null了:这里我们通过xml注册广播

1)定义广播

public class MyBroadcast2 extends BroadcastReceiver{
	@Override
	public void onReceive(Context context, Intent intent) {
		System.out.println("MyBroadcast2 started!");
		//接收sendOrderedBroadcast传递过来的Intent中的参数
		System.out.println(intent.getStringExtra("test"));
		//添加另一个参数
		Bundle bundle=new Bundle();
		bundle.putString("test2", "我是从MyBroadcast2中存储的数据");
		//将其封装为Bundle对象,让下一个广播接收
		setResultExtras(bundle);
	}
}
public class MyBroadcast1 extends BroadcastReceiver{

	@Override
	public void onReceive(Context context, Intent intent) {
		System.out.println("MyBroadcast started!");
		//接收sendOrderedBroadcast传递过来的Intent中的参数
		System.out.println(intent.getStringExtra("test"));
		//得到从上一个广播中携带过来的另一个数据
		Bundle bundle=getResultExtras(true);
		System.out.println(bundle.getString("test2"));
	}
}
2)在AndroidManifest.xml中声明广播:

        <receiver android:name=".MyBroadcast1">
            <intent-filter android:priority="900">
                <action android:name="com.xin.action.broadcast"/>
            </intent-filter>
        </receiver>
        <receiver android:name=".MyBroadcast2">
            <intent-filter android:priority="1000">                <action android:name="com.xin.action.broadcast"/>
            </intent-filter>
上面声明的广播中,MyBroadcast2比Mybroadcast1的优先级高!

3)发送有序广播

			Intent intent=new Intent("com.xin.action.broadcast");
			intent.putExtra("test", "我是sendOrderedBroadcast发送过来的数据!");
			sendOrderedBroadcast(intent,null);
测试结果:



因为Mybroadcast2的优先级(1000)比Mybroadcast1(900)的高,所以广播通过sendOrderedBroadcast发送出去后,首先被Mybroadcast2接收,然后再Mybroadcast2中通过setResultExtras设置了另一些参数一起传到Mybroadcast1,然后Mybroadcast1接收到广播,也通过getResultExtras(true)接收从Mybroadcast2中携带过来的数据,所以出现上面的结果!

经测试发现,在使用sendBroadcast时候,如果在BroadcastReceiver中使用setResultExtras或者getResultExtras,程序会报错,因为它发送的不是有序广播!

注意:
1、
如果是在代码中注册的,我们可以通过filter.setPriority(1000);来设置其优先级,这里就不举例说明了!

2、如果我们想让有序广播在一个BroadcastReceiver中接收后,不再往下一个广播执行,可以调用其abortBroadcast();中断广播的发送,后面的广播将接收不到!


粘性广播(异步广播)的发送

在网上专业名称各不一样,有人讲它是粘性广播,也有人说它是异步广播,先不讨论它的专业名词的问题了,先来让我们了解它有什么用,它与普通广播的区别就是当广播取消注册后,然后发送一个粘性广播,广播重新注册后仍然能接收到粘性广播发送过来的消息!

普通广播与粘性广播最大的区别:

我们知道,我们先注册广播,然后发送广播,那么无论是什么广播都能被接收到,那么如果我们先发送广播,后注册广播呢:

普通广播:未注册广播-->发送广播-->注册广播-->接收不到广播

粘性广播:未注册广播-->发送广播-->注册广播-->能接收到广播,并且能接收到多次发送广播的最后一条广播信息

这就是两者之间的区别,下面我们通过代码的方式给大家举一个例子,我们的广播是在代码中注册的非常驻型广播:

页面只包含三个测试按钮:


我们在注册广播的Activity中注册广播:RegisterActivity,在其生命周期的onPause方法中取消注册广播unregisterBr

	 
	//定义广播
	BroadcastReceiver receiver=new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action=intent.getAction();
			int count=intent.getIntExtra("count", 0);
			System.out.println("action="+action+",count="+count);
		}
	};
	
	//注册广播
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.register);
		IntentFilter filter=new IntentFilter();
		//添加广播Action
		filter.addAction("com.xin.action.broadcast");
		filter.addAction("com.xin.action.sticky.broadcast");
		registerReceiver(receiver, filter);
	}

	//取消注册广播
	@Override
	protected void onPause() {
		super.onPause();
		System.out.println("RegisterBroadActivity onPause!");
		unregisterReceiver(receiver);
	}
在MainActivity中发送广播:

    //定义一个变量,来统计点击发送粘性广播的次数,然后测试粘性广播的接收是不是最后一条广播
    private int count;
	@Override
	public void onClick(View v) {
		Intent intent=null;
		switch(v.getId()){
		case R.id.btn_send1://发出广播sendBroadcast
			intent=new Intent("com.xin.action.broadcast");
			sendBroadcast(intent);
			break;
		case R.id.btn_send2://发出粘性广播sendStickyBroadcast
			count++;
			intent=new Intent("com.xin.action.sticky.broadcast");
			intent.putExtra("count", count);
			sendStickyBroadcast(intent);
			break;
		case R.id.register://启动注册广播页面
			intent=new Intent(MainActivity.this,RegisterBroadActivity.class);
			startActivity(intent);
			break;
		}
	}
	
	@Override
	protected void onResume() {
		super.onResume();
		count=0;
		System.out.println("MainActivity onResume!");
	}
注意,发送粘性广播,我们需要在AndroidManifest.xml中添加能够发送粘性广播的权限,否则会报错:

<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
测试及结论:

当我们点击发送sendBroadcast按钮3次--点击注册按钮,控制台无输出结果

当我们点击发送sendStickyBroadcast按钮4次--点击注册按钮,控制台输出结果:action=com.xin.action.sticky.broadcast,count=4

这就是普通广播和粘性广播的区别
sendBroadcast发送出去的广播,如果没有广播进行注册,那么该广告也就接收不到了,当重新注册广播后,也接收不到
sendStickyBroadcast发送出去的广播,如果没有广播进行注册,那么该广告此时也就接收不到了,当重新注册广播后,会接收到,并且会接受sendStickyBroadcast发出去的最后一条广播,所以上面的输出结果中点击发送stickybroadcast 4次,count变为4,那么当重新注册广播后,控制台会输出结果count=4;
sendStickyBroadcast发出的最后一个Intent会被保留,下次当Recevier处于活跃的 时候,又会接受到它

当我们需要移除掉粘性广播的时候,调用方法:removeStickyBroadcast(intent);即可清除掉粘性广播

还有一个发送广播的方式:sendStickyOrderedBroadcast (),测试在这个方法发来的广播,代码注册方式中,收到广播先后次序为:注明优先级的、代码注册的、没有优先级的;如果都没有优先级,代码注册收到为最先。

上面给大家介绍了广播操作中的几种方式:

发送广播:sendBroadcast(Intent intent)、sendBroadcast(Intent intent,String receiverPermission);

发送有序广播:sendOrderedBroadcast(Intent intent,String receiverPermission);

发送粘性广播:sendStickyBroadcast(Intent intent);

还有一种方式:sendStickyOrderedBroadcast();(未研究,不知道用的多不多)


在Android的广播操作中,我们还应该知道:

1、无论对于有序广播还是无序广播,广播接收器默认都是运行在主线程中的(main线程,即UI线程)。可以通过在程序中使用registerReceiver(receiver, filter, broadcastPermission, scheduler)方法中的最后一个参数指定要运行的广播接收器的线程。也可以在Manifest.xml文件中设置(Intent-filter标签中设置android:process)。

2、我们在代码中注册广播registerBroadcast十次,那么广播发送过来的时候会接收十次,注销广播只需一次!

3、每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应 . 所以在BroadcastReceiver 里不能做一些比较耗时的操作 , 否侧会弹出 ANR(Application NoResponse) 的对话框,如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由 Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的 .

4、耗时的操作应该通过广播启动service来执行操作

	BroadcastReceiver receiver=new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			Intent intent=new Intent(context,TestService.class);
			startService(intent);
		}
	};

Android之旅十七 android中的广播使用