Android中的BroadcastReceiver理解

广播(Broadcast)是用来在进程的不同组件之间通信的,起到通知和传递数据的作用。当然也可以使用广播在不同的进程之间进行通信,但是下面的内容没有涉及到。广播接受者(BroadcastReceiver)是android中的四大组件之一(Activity、Service、Broadcast、Contentprovider),一旦被启动之后就可以在后台监听广播事件,当然对于已经用不到的广播事件我们也可以调用unregisterReceiver(receiver)方法来注销广播,被注销的广播就不能在重新注册了,因为注销之后所占用的内存就会被回收了。下面的内容主要强调的是广播的使用方式和生命周期。没有涉及到序列广播的内容。

简单的广播使用:

一、声明一个广播接受者

有两种做法:一是继承BroadcastReceiver类,实现其中onReceive方法:

public class MainActivity extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		
	}
}
二是在类中声明一个匿名内部类:
public class BroadCast {
	
	private BroadcastReceiver receiver = new BroadcastReceiver(){

		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			
		}
	};
}

BroadcastReceiver中的onReceive方法在接收到广播事件之后就会触发。

二、在声明广播的同时我们要指定为广播接受者添加过滤器

为什么要家过滤器呢?顾名思义,是为了保证一个广播接受者只能接受指定的广播事件,为广播添加过滤器也有两种方法,

一是:在配置文件中添加(如果要监听多个广播事件的话,就可以为属性多添加几个属性


      
             
             
             
      
属性就是广播的过滤器,其中的属性就是该广播可以接受的广播事件,我在上面的代码中添加了三个过滤器,就是说这个广播接受者只能接受上面的三种事件(这三种事件都是系统自定义的事件,我们也可以定义自己的广播事件,这个我后面再说)

二是,直接在代码中加,需要借助一个IntentFilter对象

IntentFilter filter = new IntentFilter();
filter.addAction("android.app.action.ACTION_PASSWORD_CHANGED");
filter.addAction("android.app.action.ACTION_PASSWORD_FAILED");
filter.addAction("android.app.action.ACTION_PASSWORD_EXPIRING");

三、注册广播,上面的两步完成之后我们就要让这个广播开始监听(也就是注册广播),注册广播也有两种方法

一是在配置文件中进行注册,二中的属性就是用来指定广播接受者。

二是在代码中进行注册,调用registerReceiver(receiver, filter);方法,其中的receiver就是广播接受者对象,filter就是这个广播接受者的过滤器

在配置文件中注册广播成为静态注册,也就是说,在程序正式开始运行之前就注册了;而在代码中注册广播成为动态注册,就是说在程序运行的过程中进行注册。


上面的三部完成之后,就可以在其他的进程中调用sendBroadcast方法来发广播了,关于发送广播有两点需要注意:一是,只有在发送自定义的广播事件的时候才会使用sendBroadcast来发送广播,系统广播事件不用我们来发送的。二是,sendBroadcast方法是属于Context类的,我们知道,常见的组件中Activity、Serveice都是Context的子类,这也就是说,只有在Activity、Service对象中才能调用sendBroadcast方法发送广播,如果想要在其他的地方发送广播,需要接受一个Context参数,然后调用Context.sendBroadcast()方法来发送广播。

还有几点需要说明的:

1、自定义一个广播事件的时候,用一个字符串表示,虽然说这个字符串可以是任意的,但是字符串中间是不能含有空格的

2、如果在一个类中声明了一个广播接受者(就是说这个类不是继承BroadcastReceiver的),那么广播 的注册必须要在这个类中完成,如果在配置文件中进行注册会出现强转异常。(代码2)

3、如果继承Broadcast来实现一个广播接受者,一般情况下是在配置文件中进行注册,当然,也可以在另外的类中进行注册,那就必须在另外的类中实例化这个广播接受者的一个对象。(代码3)

4、在线程子类中不能发送广播,原因是在前面的第二点注意中说明了。需要强调一下的是,我这里所说的线程子类是指继直接承了Thread类或者实现了Runnable接口的类(外部类),除了用这两钟方式创建线程,剩下的应该就只有通过Thread thread = new Thread(new Runnable(){}和内部类这两种方式来创建线程吧,用后面的两种方式创建的线程是可以在其中发送广播的。(代码4)


下面看几段代码。

代码1,:监听系统的广播事件

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

public class MainActivity extends Activity {

	private BroadcastReceiver receiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			// 在这个方法中定义接受到广播事件之后做出的处理
			Log.v("adb", "外部电源已经接入");
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	
	@Override
	protected void onResume(){
		super.onResume();
		
		//为广播接受者添加过滤器,为过滤器添加了一个接入电源的系统广播事件,也就是说这个广播智能接收到接入电源这一事件
		IntentFilter filter = new IntentFilter();
		filter.addAction(Intent.ACTION_POWER_CONNECTED);

		registerReceiver(receiver, filter); //注册
	}
	
	protected void onPause() {
		super.onPause();
		
		//注销广播
		unregisterReceiver(receiver);
	}
}

系统的广播事件是不需要我们发送的,表示广播事件的字符串直接使用Intent类中的字符串常量。


下面演示一下前面说的需要注意的第二点,顺便看一下自定义的广播事件(自定义的广播事件就需要我们调用sendBroadcast方法来发送)

代码2:

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

public class MainActivity extends Activity {
	static final String str1 = "send_the_first_broadcast";//自定义的广播事件

	private BroadcastReceiver receiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			String action = intent.getAction();
			if (action == MainActivity.str1) {
				Log.v("adb", "接受到了广播事件");
			}
		}
	};

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

		Intent intent = new Intent();
		intent.setAction(str1);
		//或者使用下面的方式
		//Intent intent = new Intent(str1);
		sendBroadcast(intent);
	}
	
	@Override
	protected void onPause() {
		super.onPause();
		
		//注销广播
		unregisterReceiver(receiver);
	}
}

在配置文件中进行注册:(部分)


            
                
            
        
运行结果:


正如我前面所说的出现了强转异常,因为在配置文件中进行注册的广播接受者必须是Broadcast的子类,但是在上面的代码中Mainactivity并不是Broadcast的子类,所以出现强转异常。


再来演示一下上面说的需要注意的第三点

代码3:

import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;

public class MainActivity extends Activity {
	
	public static final String str = "我是广播事件";//自定义的广播事件

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	
	@Override
	protected void onResume() {
		super.onResume();
		Receiver1 receiver = new Receiver1();//实例化一个广播接受者对象
		IntentFilter filter = new IntentFilter();
		filter.addAction(this.str);
		registerReceiver(receiver, filter);
		
		Intent intent = new Intent(str);
		sendBroadcast(intent);
	}
	
	@Override
	protected void onPause(){
		super.onPause();
		unregisterReceiver(receiver);
	}
}

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

public class Receiver1 extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		String action = intent.getAction();
		if(action.equals(MainActivity.str)){
			Log.v("adb", "接收到了广播事件");
		}
	}
}
在上面的代码中,我是在代码中进行的注册,当然也可以在配置文件中进行注册。

看完代码2和代码3(里面都用到了自定义的广播事件),再来说一下Intent中的action,Intent中的action都是public static final String 型的,例如代码1中的电源接入事件在Intent中的定义是:

public static final String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED"

在上面的代码中,我在onResume方法中注册了广播,我把上面的表示自定义事件的字符串声明为public static final型的,是模仿系统事件的定义方式,当然,你也不一定非要这么定义,但是出于安全考虑还是这样定义比较好。

需要注意的是,我们在为广播接受者添加过滤器的时候,在配置文件中只能写字符串的值,在代码中可以写字符串的名字,也可以写字符串的值。

再来看一下前面说的需要注意的第四点

代码4:

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;

public class MainActivity extends Activity {

	public static final String str = "Broadcast_in_Thread";

	BroadcastReceiver receiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			Log.v("adb", "接收到了广播事件");
		}
	};

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

		IntentFilter filter = new IntentFilter();
		filter.addAction(this.str);
		registerReceiver(receiver, filter);
	}

	@Override
	protected void onStart() {
		super.onStart();

		
		/*Thread thread = new Thread(new Runnable() {

			@Override
			public void run() { // TODO Auto-generated method stub
				Intent intent = new Intent();
				intent.setAction(MainActivity.str);
				sendBroadcast(intent);
			}
		});

		thread.start();*/
		
		BroadcastThread thread = new BroadcastThread();
		thread.start();
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		unregisterReceiver(receiver);
	}
	
	class BroadcastThread extends Thread {
	  
		@Override
	    public void run() { 
	    	Intent intent = new Intent();
	        intent.setAction(MainActivity.str); 
	        sendBroadcast(intent);
	    } 
	}
}

运行没有问题,代码中被注释掉的部分可以替换掉内部类。这是因为用上面的两种方式创建的线程,仍然在Activity中,所以可以直接调用sendBroadcast方法。如果不采用上面的创建线程的方法,而是直接另外写一个实现Runnable接口或是继承自Thread类的类,这个时候如果想要在新建的线程中发送广播的话,就必须为线程类(暂且这么称呼吧)创建一个接收Context对象的构造方法,并且在MainActivity中实例化这个线程类的时候,把MainActivity.this(主类的引用)作为参数传递进去。


如果你想为一个广播接受者添加多个广播事件(就是说想让一个广播接受者接受多个广播事件),那就多调用几次IntentFilter.addAction就可以了。如果是在配置文件中进行注册的话,那就像二中那样为属性多添加几个属性。既然一个广播接受者可以接受多个广播事件,那么一个广播事件能不能被多个广播接受者接受呢?

代码5:

import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;

public class MainActivity extends Activity {
	
	public static final String str = "我是广播事件";
	Receiver1 receiver1;
	Receiver2 receiver2;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		receiver1 = new Receiver1();
		receiver2 = new Receiver2();
		IntentFilter filter = new IntentFilter();
		filter.addAction(this.str);
		registerReceiver(receiver1, filter);
		registerReceiver(receiver2, filter);
	}
	
	@Override
	protected void onStart(){
		super.onStart();
		
		//也可以通过构造函数的方式来为Intent对象添加事件
		Intent intent = new Intent(this.str);
		sendBroadcast(intent);//发送广播
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		unregisterReceiver(receiver1);
		unregisterReceiver(receiver2);
	}
}

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

public class Receiver1 extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		String action = intent.getAction();
		if(action.equals(MainActivity.str)){
			Log.v("adb", "Receiver1接收到了广播事件");
		}
	}
}

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

public class Receiver2 extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		String action = intent.getAction();
		if (action.equals(MainActivity.str)) {
			Log.v("adb", "Receiver2接收到了广播事件");
		}
	}
}


运行之后,发现一个广播事件是可以被多个广播接受者所接受的。

当然在配置文件中进行注册也是可以实现的(配置文件中貌似是不能出现汉字的,所以上面的表示自定义的广播事件的字符串就要换一下了)


上面只是简单的介绍了广播事件起到的通知的作用,很多时候广播不仅仅起到通知的作用,在通知的同时,广播还能传递数据。先要先了解一下Intent的用法,Intent的作用是在android组件之间扮演一个信使的作用(在我的另一篇博客中有专门介绍Intent的用法的),下面再看一个例子

代码6:

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

public class MainActivity extends Activity {
	
	public static final String str = "message_transporation";
	private static String message_transported_by_Broadcast;
	
	BroadcastReceiver receiver = new BroadcastReceiver(){

		@Override
		public void onReceive(Context context, Intent intent) {
			// TODO Auto-generated method stub
			
			String action = intent.getAction();
			if(action.equals(MainActivity.str)){
				//Bundle bundle = intent.getExtras();
				//message_transported_by_Broadcast = bundle.getString("message");
				message_transported_by_Broadcast = intent.getStringExtra("message");
				Log.v("adb", message_transported_by_Broadcast);
			}
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		IntentFilter filter = new IntentFilter();
		filter.addAction(str);
		registerReceiver(receiver, filter);
	}
	
	@Override
	protected void onStart(){
		super.onStart();
		
		Intent intent = new Intent();
		intent.setAction(str);
		
		//Bundle bundle = new Bundle();
		//bundle.putString("message", "将要传输的数据");
		//intent.putExtras(bundle);
		intent.putExtra("message", "将要传输的数据");
		sendBroadcast(intent);
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		unregisterReceiver(receiver);
	}
}

上面的只是很简单的一个通过广播来传递数据的例子,Intent中还有很多方法可以用来向其中保存或是取出不同类型的数据,也可以跟Bundle一起使用来传递数据,注释中的内容就是的。传递数据时,Intent相当于一个货车,发送广播之前存入数据,接受到广播之后取出数据,如果使用了Bundle,Bundle就相当于货车上装货物的箱子。

两种注册广播的方式的区别,以及广播的生存周期。

1、动态注册的广播接受者生命周期

为了测试MainActivity在不同的状态下,广播接受者是否能够接收到广播消息,以此来判断广播接受者的生命周期,需要系统不断发送广播消息,在下面的测试用例中,我在开启了一个服务,并且在服务中开启一个线程,在这个线程中每隔一秒就发送一个广播事件(见下面的代码),这个服务一旦启动就会一直在后台运行,也就是说会不停的发送广播消息,此时需要关心的就是广播接受者是否收到了广播消息。

大家很可能对Activity的生命周期十分了解了,但是我觉得还是有必要看看下面的Activity的状态的转换图:

Android中的BroadcastReceiver理解_第1张图片

从上面的图中,我们应该可以体会到在不同的方法中注册或者注销广播接收者应该会有不同的效果。我们看一下下面的代码:

import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;

public class MainActivity extends Activity {
	
	Receiver receiver;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		Intent intent = new Intent();
		intent.setClass(MainActivity.this, BackService.class);
		startService(intent);

		IntentFilter filter = new IntentFilter();
		filter.addAction("EVENT");
		receiver = new Receiver();
		registerReceiver(receiver, filter);
	}
	
	/*@Override
	protected void onPause(){
		super.onPause();
		unregisterReceiver(receiver);
	}*/
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		unregisterReceiver(receiver);
	}
}

发送广播事件的服务:

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

public class BackService extends Service {
	
	@Override
	public void onCreate() {
		
		Thread thread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true) {
					try {
						Thread.sleep(1000);
						Intent intent = new Intent("EVENT");
						sendBroadcast(intent);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		});
		
		thread.start();
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
}
下面是用另一种方式定义的广播接收者:

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

public class Receiver extends BroadcastReceiver {
	private static int count = 0;
	
	public Receiver() {
		count++;
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		Log.v("abc", "receiver_out收到了广播, count = " + count);
	}
}

测试结果:

Android中的BroadcastReceiver理解_第2张图片

说明:在上面的代码中,为了显示广播接收者在接受广播事件的时候,是否每次接受都创建了一个接受者对象,我在广播接受者的构造函数中更新一个static变量的值,从运行结果来看,static变量只更新了一次,也就是说只创建了一个广播接受者对象,但却还是收到了多次广播事件。

关于广播的注销:在上面的代码中,如果不注销广播就直接退出程序(按下返回键,调用了onDestroy方法),就会抛出异常。根据前面的那张图,在不同的方法中注销广播,效果也是不一样的。上面的代码中我分别在onPause和onDestroy方法中注销广播,当在onDestroy方法中注销广播的时候,按下home键,仍然可以收到广播事件。同样的,从上面的图中也可以看出,如果在onStart或者onResume方法中注册广播的话,每次回到Activity的时候都会注册一次广播。重复注册同一个广播,并没有什么副作用,但是只能注销一次广播,不论你注册了多少次。

关于生命周期:从上面的分析中,应该可以看出动态注册的广播的生命周期,跟注册它的组件的生命周期是息息相关的,取决于你在什么方法中注册和注销。


2、静态注册的广播接收者的生命周期

代码比较简单:

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		Intent intent = new Intent();
		intent.setClass(MainActivity.this, BackService.class);
		startService(intent);
	}
}
Receiver类和发送广播事件的服务类跟前面一样。在配置文件中对Receiver进行了注册

运行结果:

Android中的BroadcastReceiver理解_第3张图片
从运行结果中,我们可以看出,跟前面的例子不同的是,每次接受广播事件的时候都实例化了一个BroadcastReceiver对象,此时就会有一个疑问:我要怎么注销这个广播呢?借一位大神的说法:采用静态注册时候,当onReceive方法执行完毕之后,广播接受者对象会在任意时刻被销毁。也就是说,采用静态注册时,我们并不需要担心BroadcastReceiver对象的注销问题。

关于生命周期:当上面的程序与运行起来之后,不论按下home键还是返回键(销毁了Activity),还是可以收到广播事件。说明这种方式注册的广播接收者会一直在后台运行,我试了一下,貌似只有把程序卸载,才会停止。

你可能感兴趣的:(Android笔记)