Android中广播的使用(动态、静态注册的区别,有序无序广播的使用)

一:Android广播机制概述

Android广播分为两个方面:广播发送者广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器)。

广播作为Android组件间的通信方式,可以使用的场景如下:

1.同一app内部的同一组件内的消息通信(单个或多个线程之间);

2.同一app内部的不同组件之间的消息通信(单个进程);

3.同一app具有多个进程的不同组件之间的消息通信;

4.不同app之间的组件之间消息通信;

5.Android系统在特定情况下与App之间的消息通信。

二、广播的分类

1).Normal Broadcast:普通广播

此处将普通广播界定为:开发者自己定义的intent,以context.sendBroadcast_"AsUser"(intent, ...)形式。具体可以使用的方法有:
sendBroadcast(intent)
sendBroadcast(intent,receiverPermission)
sendBroadcastAsUser(intent,userHandler)
sendBroadcastAsUser(intent,userHandler,receiverPermission)。
普通广播会被注册了的相应的感兴趣(intent-filter匹配)接收,且顺序是无序的。如果发送广播时有相应的权限要求,BroadCastReceiver如果想要接收此广播,也需要有相应的权限。

2).System Broadcast: 系统广播

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

3)Ordered broadcast:有序广播

有序广播的有序广播中的“有序”是 针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的定义过程与普通广播无异,只是其的主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, ...)。

对于有序广播,其主要特点总结如下:
1>多个具当前已经注册且有效的BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准遵循为:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。

2>先接收的BroadcastReceiver可以对此有序广播进行截断,使后面的BroadcastReceiver不再接收到此广播,也可以对广播进行修改,使后面的BroadcastReceiver接收到广播后解析得到错误的参数值。当然,一般情况下,不建议对有序广播进行此类操作,尤其是针对系统中的有序广播。

4)Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)。

既然已经deprecated,此处不再多做总结。

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

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

1.其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;

2.其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。

无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:
1.对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;
2.在广播发送和接收时,都增加上相应的permission,用于权限验证;
3.发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。
相比于全局广播,App应用内广播优势体现在:
1.安全性更高;
2.更加高效。
为此,Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题,使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。


三、广播接收者(BroadCastReceiver)的两种注册方式

 1.常驻型广播(静态注册)

 常驻型广播,这个广播接收者会在程序运行的整个过程中一直存在,不会被注销掉,当程序被杀掉后不会再接收到广播了。它的注册方式就是在你应用程序的AndroidManifast.xml 中进行注册,这种注册方式通常又被称作静态注册。这种方式可以理解为通过清单文件注册的广播是交给操作系统去处理的。在Manifest.xml中注册广播,是一种比较推荐的方法,因为它不需要手动注销广播(如果广播未注销,程序退出时可能会出错)。 

例如:下边是注册的一些系统广播接收者

首先写一个类继承自BroadCastReceiver。

package com.example.mybroadcastreceiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

/**
 * 这是一个获取系统的广播。是一个接收拨打电话的广播
 * 在配置文件中配置了广播接收的过滤意图来实现注册
 * 这个广播是只有程序在运行就一直处于接收状态,当程序被杀死后就无法再接收到广播了
 * 
 *   
       
        
            
                
            
        
 * @author Administrator
 *
 */
public class OutCallReceiver extends BroadcastReceiver{

	@Override
	public void onReceive(Context context, Intent intent) {
		/**
		 * 这里是接收到广播后需要进行的操作都放在这里
		 */
		System.out.println("接收到打电话的广播了,可以在此做相应的操作");
		Toast.makeText(context, "接收到打电话的广播了,可以在此做相应的操作", 1).show();
	}

}
然后在清单文件中进行静态的注册配置。这样就完成了对接收拨打电话广播的注册,在整个程序运行的过程中,只要手机拨打电话了都能接收到这个广播,并会执行onReceive中的代码。




    
    
    
    
	
    
        
      
     
        
            
                

                
            
        

        
       
        
            
                
            
        
        
        
     
     
        
            
                
                
            
        
        
        
     
        
            
                
            
        
        
        
   
        
            
    
                
                
                
                
            
        
    



注意BroadcastReceiver作为内部类时,如要通过静态注册的方式在清单文件中注册,必须要声明为public static,否则会报错:

没有public报错:java.lang.RuntimeException: Unable to instantiate receiver com.example.multibroadcastreceiver.MainActivity$ThirdBroadCast: java.lang.IllegalAccessException: access to class not allowed

没有static报错:java.lang.InstantiationException: can't instantiate class com.taodian.evacuation.fragment.AlarmFragment$MessageReceiver; no empty constructor

2.非常驻型广播(动态注册)
非常驻型广播,是通过代码注册广播接收者的一种形式。代码注册时可以将广播接收者作为一个内部类写在Activity中,也可以重新写一个类继承自BroadCastReceiver,需要用代码注册(注册广播可以写在任意的地方,只有注册的广播接收者才能收到对应的广播),比如在 Activity 中的 onCreate 或者 onResume 中注册广播接收者,在 onDestory 中注销广播接收者。这样你的广播接收者就一个非常驻型的了,这种注册方式也叫动态注册。这种方式可以理解为通过代码注册的广播是和注册者关联在一起的。

下面是在Activity中注册和注销广播接收者:

package com.example.mybroadcastreceiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

	
	private static final String MY_BROADCAST_ACTION = "com.example.mybroadcastreceiver.my_broadcast_action";
	private FirstBroadCast firstBroadCast;
	private TwiceBroadCast twiceBroadCast;
	private ThirdBroadCast thirdBroadCast;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		firstBroadCast = new FirstBroadCast();
		IntentFilter filter1 = new IntentFilter();
		filter1.addAction(MY_BROADCAST_ACTION);
		filter1.setPriority(1000);
		registerReceiver(firstBroadCast, filter1);
		
		
		twiceBroadCast = new TwiceBroadCast();
		IntentFilter filter2 = new IntentFilter();
		filter2.addAction(MY_BROADCAST_ACTION);
		filter1.setPriority(5);
		registerReceiver(twiceBroadCast, filter2);
		
		
		thirdBroadCast = new ThirdBroadCast();
		IntentFilter filter3 = new IntentFilter();
		filter3.addAction(MY_BROADCAST_ACTION);
		filter1.setPriority(6);
		registerReceiver(thirdBroadCast, filter3);
		
		
		
//		finish();
	}

	
	public void sendBroadCast(View v){
		//这里创建电台来发送广播,需要一个intent就行了
		Intent intent = new Intent();
		//给广播设置一个action,方便广播接收者接收相应的广播
		intent.setAction(MY_BROADCAST_ACTION);
		intent.putExtra("data", "这里是来自自定义电台发送的广播");
//		sendBroadcast(intent);//这里发送的是无序广播
		
		/*
		 * 有序广播
		 * intent:你要发送的数据,包含数据和动作
		 * receiverPermission:权限,一般不用为null
		 * resultReceiver:最终接收者
		 * Handler:消息处理
		 * initialCode:初始化编码1 - 1000
		 * initialData:初始化的数据
		 * initialExtras:额外的数据,一般不用
		 */
		sendOrderedBroadcast(intent, null, null, null, 1, "10000", null);
		
	}
	
	
	//这个广播接收者可用写在任意的类中,甚至可用写在别的app中。也能接收到对应电台发送的广播,只要action对应
	//这些广播可以通过在清单文件中注册,也可以手动的通过registerReceiver方法注册
	class FirstBroadCast extends BroadcastReceiver{

		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if (MY_BROADCAST_ACTION.equals(action)) {
				String data = intent.getStringExtra("data");
				Toast.makeText(context,
						"FirstBroadCast接收到广播了,接收的广播的数据是:" + data, 1).show();
				System.out.println("FirstBroadCast接收到广播了,接收的广播的数据是:" + data);
			}
		}
		
	}
	class TwiceBroadCast extends BroadcastReceiver{
		
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if (MY_BROADCAST_ACTION.equals(action)) {
				String data = intent.getStringExtra("data");
				Toast.makeText(context,
						"TwiceBroadCast接收到广播了,接收的广播的数据是:" + data, 1).show();
				System.out.println("TwiceBroadCast接收到广播了,接收的广播的数据是:" + data);
			}
		}
		
	}
	class ThirdBroadCast extends BroadcastReceiver{
		
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if (MY_BROADCAST_ACTION.equals(action)) {
				String data = intent.getStringExtra("data");
				Toast.makeText(context,
						"ThirdBroadCast接收到广播了,接收的广播的数据是:" + data, 1).show();
				System.out.println("ThirdBroadCast接收到广播了,接收的广播的数据是:" + data);
			}
		}
		
	}
	
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		if(firstBroadCast != null){
			unregisterReceiver(firstBroadCast);
		}
		if(twiceBroadCast != null){
			unregisterReceiver(twiceBroadCast);
		}
		if(thirdBroadCast != null){
			unregisterReceiver(thirdBroadCast);
		}
		
	}
	
	
}

四.不同注册方式的广播接收器回调onReceive(context, intent)中的context具体类型

1).对于静态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext;

2).对于全局广播的动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Activity Context;

3).对于通过LocalBroadcastManager动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Application Context。

注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。


五、不同Android API版本中广播机制相关API重要变迁

1).Android5.0/API level 21开始粘滞广播和有序粘滞广播过期,以后不再建议使用;

2).”静态注册的广播接收器即使app已经退出,主要有相应的广播发出,依然可以接收到,但此种描述自Android 3.1开始有可能不再成立

Android 3.1开始系统在Intent与广播相关的flag增加了参数,分别是FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES。

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

FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包

主要原因如下:

自Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。

详情参加Android官方文档:http://developer.android.com/about/versions/android-3.1.html#launchcontrols

由此,对于系统广播,由于是系统内部直接发出,无法更改此intent flag值,因此,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,如果App进程已经退出,将不能接收到广播。

但是对于自定义的广播,可以通过复写此flag为FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,即使所在App进程已经退出,也能能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是重新新建的。

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

六、Android 广播的生命周期    

    一个广播接收者有一个回调方法:void onReceive(Context curContext, Intent broadcastMsg)。当一个广播消息到达接收者时,Android调用它的onReceive()方法并传递给它包含消息的Intent对象。广播接收者被认为仅当它执行这个方法时是活跃的。当onReceive()返回后,它是不活跃的。

有一个活跃的广播接收者的进程是受保护的,不会被杀死。但是系统可以在任何时候杀死仅有不活跃组件的进程,当占用的内存别的进程需要时。

这带来一个问题,当一个广播消息的响应时费时的,因此应该在独立的线程中做这些事,远离用户界面其它组件运行的主线程。如果onReceive()衍生线程然后返回,整个进程,包括新的线程,被判定为不活跃的(除非进程中的其它应用程序组件是活跃的),将使它处于被杀的危机。解决这个问题的方法是onReceive()启动一个服务,及时服务做这个工作,因此系统知道进程中有活跃的工作在做。

    Broadcast Receive为广播接收器,它和事件处理机制类似,只不过事件的处理机制是程序组件级别的,广播处理机制是系统级别的。

Broadcast Receiver用于接收并处理广播通知(broadcast announcements)。多数的广播是系统发起的,如地域变换、电量不足、来电来信等。程序也可以播放一个广播。程序可以有任意数量的 broadcast receivers来响应它觉得重要的通知。broadcast receiver可以通过多种方式通知用户:启动activity、使用NotificationManager、开启背景灯、振动设备、播放声音等,最典型的是在状态栏显示一个图标,这样用户就可以点它打开看通知内容。

通常我们的某个应用或系统本身在某些事件(电池电量不足、来电来短信)来临时会广播一个Intent出去,我们可以利用注册一个Broadcast Receiver来监听到这些Intent并获取Intent中的数据。

七、开发BroadcastReceiver的一些注意事项:

BroadcastReceiver的生命周期比较短,一些比较费时的操作不应该放在onReceiver里完成。如果在onReceiver()的方法不能在10秒内执行完成,将会产生程序无响应也就是我们熟悉的ANR(Application not Response)。但是如果非得要在这里面执行一些费时的操作我们可以在这个onReceiver去启动一个Service来完成这样的一个费时操作。

广播接收者的对象只有在回调onReceive()这个函数时有效,一旦从这个函数返回,这个对象就被结束,不再激活。在onReceive()中,任何异步的操作都是不可行的。因为需要从onRecive()这个函数回来去处理异步的操作,但是这个广播接收者不再被激活,系统将会在异步操作完成前结束进程。特别是在这里面显示对话框或者绑定service不行,但是可以用其他替代的方式,前者可以notificationManger,后者用startService()发送请求。


下边是一个如何使用广播的代码案例地址,需要的可以下载:

http://download.csdn.net/detail/oonullpointeralex/9064049


你可能感兴趣的:(Android基础知识)