什么是BroadCastReceiver
BroadcastReceiver 中文意思是“广播接收者”,它主要是用于接收应用程序或是系统发送过来的广播并根据广播内容进行相关响应的一类组件。Broadcast在Android系统中应用的非常广泛,比如电池状态的变化、电话的接收、短信的接收、键盘输入法切换和网络连接状态变化都会由系统发送一个广播,然后应用程序通过BroadCastReceiver可以监听这些广播并作出相应的处理。此外,BroadCastReceiver可以通过监听其它应用程序发送的广播接收传递过来的信息进而实现进程间的通信,我们再查看一下BroadcastReceiver的源码可以看到IBinder影子,就不难理解其中的原理了(Android进程通信是用IBinder实现的)。BroadCastReceiver的静态注册和动态注册
要实现一个BroadcastReceiver,就必须和使用Android其它组件一样(Activity、Service和ContentProvider),得在AndroidManifest.xml中进行注册。下面举一个示例,在AndroidManifest.xml中注册BroadcastReceiver,代码如下:
- <receiver android:name=".broadcastreceiver.NormalBroadReceiver" >
- <intent-filter >
- <action android:name="com.fendou.NORMAL_BROADCASTRECEIVER" />
- </intent-filter>
- </receiver>
以上代码中<intent-filter>中的<action>主要作用是充当广播过滤器的一个过滤标签,可以是自定义也可以由系统提供,并且一个<intent-filter>中可以有多个<action>。BroadcastReceiver除了可以在xml文件中注册外,还可以以代码的形式在类中注册,示例代码如下:
- //注册BroadcastReceiver方法
- public void filter()
- {
- IntentFilter mFilter = new IntentFilter();
- mFilter.addAction("com.fendou.NORMAL_BROADCASTRECEIVER" );
- registerReceiver( mBroadcastReceiver, mFilter);
- }
- //实例化BroadcastReceiver
- BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- if(intent.getAction().equals("com.fendou.NORMAL_BROADCASTRECEIVER" ))
- {
- //dosomething
- }
- }
- };
以代码方式注册,首先我们得实例化一个Intentfilter和一个BroadcastReceiver对象,然后使用registerReceiver()方法将BroadcastReceiver注册到系统中,很简单吧。对于这两种注册BroadcastReceiver的方式,我们一般将前者称为静态注册,后者则称为动态注册,两者的区别就是前者注册是属于常驻型,就是在应用程序一开启,以该种类型注册的BroadcastReceiver就可以一直接收到系统或其它应用发送过来的广播,即使它的宿主应用退出系统不再中运行,该类型的BroadcastReceiver也能照常工作。而后者由于在类中注册的,所以它的生命周期是跟随类对象的,若该类对象被停止销毁了,在该类中注册的BroadcastReceiver也会随之销毁。两种注册方式都有各自的特点,我们可以根据具体情况来选择需要使用哪种注册方式。
BroadCast的分类
前面讲解了BroadcastReceiver的注册,那么我们可以自定义广播来向注册的BroadcastReceiver发送广播,发送广播的方式一般分为以下四种:
<1>普通广播
发送普通广播是最常见也是使用最多的一种广播发送方式,它的特点就是当有多个BroadcastReceiver同时接收一个广播的时候,它们都是异步接收的的,换句话说就是各个BroadcastReceiver之间接收广播的时候互不干扰,互不影响,由此便可知该广播对于每个BroadcastReceiver来说,各自都无法阻止其它BroadcastReceiver的接收动作。
<2>有序广播
有序广播相对于普通广播的区别就是当有多个BroadcastReceiver同时接收同一个广播时,它们之间会根据注册时在<intent-filter>中设置的android:priority=""优先级属性来排列接收广播的顺序,该属性的数值越大,优先级越高,取值范围是在-1000到1000之间。当没有设置优先级时,接收顺序是随机的。
为了比较普通广播和有序广播的区别,我们接下来使用一个实例来实验一下,以便我们理解得更加深刻。我们打算分别使用(sendBroadcastReceiver())发送普通广播和发送有序广播(sendOrderBroadcastReceiver())的方式同时发送给三个BroadcastReceiver,然后检验两种广播发送方式的特点和不同点。首先定义三个BroadcastReceiver,分别是
OrderBroadcastReceiverFirst、OrderBroadcastReceiverSecond和OrderBroadcastReceiverThird,并为它们设置优先级,如下是它们的注册代码:
- <!-- android:priority属性是为每个BroadcastReceiver设置优先级,值越大优先级越大 -->
- <receiver android:name=".broadcastreceiver.OrderBroadCastReceiverFirst" >
- <intent-filter android:priority="500" >
- <action android:name="com.fendou.ORDER_NORMALBROADCASTRECEIVER" />
- </intent-filter>
- </receiver>
- <receiver android:name=".broadcastreceiver.OrderBroadCastReceiverSecond" >
- <intent-filter android:priority="400" >
- <action android:name="com.fendou.ORDER_NORMALBROADCASTRECEIVER" />
- </intent-filter>
- </receiver>
- <receiver android:name=".broadcastreceiver.OrderBroadCastReceiverThird" >
- <intent-filter android:priority="300" >
- <action android:name="com.fendou.ORDER_NORMALBROADCASTRECEIVER" />
- </intent-filter>
- </receiver>
然后再看三个BroadcastReceiver的接收代码,如下:
OrderBroadcastReceiverFirst.java:
- public class OrderBroadCastReceiverFirst extends BroadcastReceiver {
- private static final String TAG = "ygh";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- if(intent.getAction().equals(StaticContens.ORDER_NORMAL_BROADCASTRECEIVER))
- {
- String info = intent.getStringExtra("info" );
- Log. i(TAG, "OrderBroadCastReceiverFirst "+ info);
- info += " First;";
- Log. i(TAG, "OrderBroadCastReceiverFirst "+ info);
- //设置结果数据
- setResultData(info);
- }
- }
- }
OrderBroadcastReceiverSecond.java:
- public class OrderBroadCastReceiverSecond extends BroadcastReceiver {
- private static final String TAG = "ygh";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- if(intent.getAction().equals(StaticContens.ORDER_NORMAL_BROADCASTRECEIVER))
- {
- //得到从OrderBroadCastReceiverFirst设置的结果值
- String info = getResultData();
- info += "Second";
- Log. i(TAG, "OrderBroadCastReceiverSecond " + info);
- //设置结果数据
- setResultData(info);
- }
- }
- }
OrderBroadcastReceiverThird.java:
- public class OrderBroadCastReceiverThird extends BroadcastReceiver {
- private static final String TAG = "ygh";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- if(intent.getAction().equals(StaticContens.ORDER_NORMAL_BROADCASTRECEIVER))
- {
- //得到从OrderBroadCastReceiverFirst设置的结果值
- String info = getResultData();
- info += "; Third";
- Log. i(TAG, "OrderBroadCastReceiverThird "+ info);
- //show一个Notification
- StaticContens. showNotification(context,"新广播", "您接收了一条新广播" );
- }
- }
- }
然后再看两者发送广播的方法,代码如下:
- //发送一个普通广播
- public void sendBroadcastReceiverMethod()
- {
- //实例化一个Intent对象
- Intent mIntent = new Intent();
- //mIntent.setAction(StaticContens.NORMAL_BROADCASTRECEIVER);
- mIntent.setAction(StaticContens.ORDER_NORMAL_BROADCASTRECEIVER);
- mIntent.putExtra( "info", "Hello" );
- //发送普通广播
- this.sendBroadcast(mIntent);
- }
- //发送一个有序广播
- public void sendOrderBroadcastReceiverMethod()
- {
- //实例化一个Intent对象
- Intent mIntent = new Intent();
- mIntent.setAction(StaticContens.ORDER_NORMAL_BROADCASTRECEIVER);
- mIntent.putExtra( "info", "Hello" );
- //发送有序广播
- this.sendOrderedBroadcast(mIntent, "com.fendou.order_broadcastreceiver_permission" );
- }
界面有两个按钮,点击分别调用以上两个方法,界面截图如下:
好,我们下面开始做实验,首先点击“NormalBroadcastReceiver”按钮,观察控制台打印的Log信息如下:
然后我们再点击"OrderBroadcastReceiver"按钮,观察控制台打印的Log信息如下:
咱们仔细分析下,我们分别在三个BroadcastReceiver中设置了setResultData()目的就是为了将结果传给排在后面的BroadcastReceiver,排在后面的BroadcastReceiver使用getResultData()方法得到上一个BroadcastReceiver传递过来的结果。我们发现,发送普通广播接受到的值都是null,可知发送普通广播,三个BroadcastReceiver接受广播都是无序的,互不干涉。而发送有序广播都成功接收到了上一个BroadcastReceiver设置的结果,所以我们确定发送有序广播时,三个BroadcastReceiver都是有序进行接收的。
<3>普通粘性广播
发送普通粘性广播其实和发送普通广播效果是差不多的,唯一一个区别就是发送粘性广播后,其发送的intent一直在应用程序中保存着,甚至Broadcast已经停止,而其它的BroadcastReceiver可以通过registerReceiver()方法的返回值得到intent。可能这样解释比较难以理解,我们就通过一个实例来验证一下吧。我们实验的内容是分别发送一个普通广播和一个普通粘性广播给同一个BroadcastReceiver,但是发送的时候该BroadcastReceiver还没有进行注册,发送完毕后再跳转至一个新的Activity,在该Activity中动态注册了能够接收刚刚发送的普通广播和普通粘性广播的BroadcastReceiver,该BroadcastReceiver得到接收的广播的Action,观察是接收到了普通广播还是普通粘性广播。首先,创建一个新的Activity类(ReceiveActivity),然后该Activity类中动态注册一个BroadcastReceiver,具体代码如下:
- package com.fendou.activity;
- import com.fendou.R;
- import com.fendou.utils.StaticContens;
- 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;
- import android.widget.TextView;
- public class ReceiveActivity extends Activity {
- //声明控件对象
- private TextView mTextView;
- private static final String TAG = "ygh";
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- setContentView(R.layout. receive);
- //根据ID得到代表该控件的对象
- mTextView = (TextView)findViewById(R.id.show_action_textview );
- }
- @Override
- protected void onResume() {
- // TODO Auto-generated method stub
- super.onResume();
- //实例化过滤器对象
- IntentFilter mFilter = new IntentFilter();
- //添加Action
- mFilter.addAction(StaticContens. SEND_USE_NORMAL_RECEIVER);
- mFilter.addAction(StaticContens. SEND_USE_STICKY_RECEIVER);
- //注册广播
- registerReceiver( mBroadcastReceiver, mFilter);
- }
- @Override
- protected void onDestroy() {
- // TODO Auto-generated method stub
- super.onDestroy();
- //解除注册
- unregisterReceiver( mBroadcastReceiver);
- }
- //实例化BroadcastReceiver对象
- BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- //获取发送过来的Action
- String action = intent.getAction();
- Log. i(TAG, "The Receive Action is: "+ action);
- mTextView.setText("The Receive Action is: "+ action);
- }
- };
- }
然后,在MainActivity类中创建三个按钮,分别为“Send Use NormalReceiver”、“Send Use StickyNormalReceiver”和“Start ReceiveActivity”按钮,并分别为它们设置监听并分别调用以下方法,代码如下:
- //发送粘性广播
- public void sendUseStickyBroadcastReceiverMethod()
- {
- //实例化Intent对象
- Intent mIntent = new Intent();
- //设置Action
- mIntent.setAction(StaticContens.SEND_USE_STICKY_RECEIVER);
- //发送广播
- this.sendStickyBroadcast(mIntent);
- }
- //发送普通广播
- public void sendUseNormalBroadcastReceiverMethod()
- {
- //实例化Intent对象
- Intent mIntent = new Intent();
- //设置Action
- mIntent.setAction(StaticContens.SEND_USE_NORMAL_RECEIVER);
- //发送广播
- this.sendBroadcast(mIntent);
- }
- //开启ReceiveActivity
- public void startReceiveActivity()
- {
- //实例化Intent对象
- Intent mIntent = new Intent();
- mIntent.setClass(getApplicationContext(), ReceiveActivity.class );
- //开启Activity
- this.startActivity(mIntent);
- }
最后注意使用粘性广播的时候注意要在AndroidManifest.xml中加入使用权限,加上如下权限:
- <!-- 使用BroadcastReceiver Sticky权限 -->
- <uses-permission android:name="android.permission.BROADCAST_STICKY" />
界面有三个按钮,分别调用以上三个方法,界面截图如下:
接下来我们开始实验,依照上面的图按顺序依次点击这三个按钮,前两个是分别发送了一个普通广播和一个普通粘性广播,最后一个按钮是跳转至ReceiveActivity。然后,我们看一下效果图
我们看到ReceiveActivity显示的是“com.fendou.SEND_USE_STICKY_RECEIVE”,说明动态注册的BroadcastReceiver可以接收到的是普通粘性广播而普通广播无法接收到,那么由此我们就能很好的理解普通粘性广播的与普通广播的区别了:普通粘性广播发送后其持有的intent对象一直保存在程序中,直到后面某些时候动态注册了BroadcastReceiver用来接收,也可以成功接收到此之前发送的粘性广播,而发送的普通广播却不具有这样的特性。
<4>有序粘性广播
有序粘性广播其实也和有序广播的特点是差不多的,只不过添加了粘性的特点,该特点在上面已经讲了,这里就不再举实例来讲解了。
之前详细讲解了BroadcastReceiver的使用方法和原理,也使用实例来讲解了如何自定义发送广播和创建BroadcastReceiver用于接收广播。接下来我们开始学习如何使用我们创建的BroadcastReceiver来监听并接收系统发送的各种各样的广播,我们的应用通过接收各类型的系统广播在特定时刻执行相关操作。Android在很多情况下都会使用广播的方式向外界发送信息,比如电量过低、电话的接收、短信的接收和网络连接状态的变化等。以下列举了Android常见的系统广播Action常量(详细内容请参考Android API文档中关于Intent的详细用法):
1、ACTION_TIME_CHANGED:系统时间被改变。
2、ACTION_DATE_CHANGED:系统日期被改变。
3、ACTION_TIMEZONE_CHANGED:系统时区被改变。
4、ACTION_BOOT_COMPLETED:系统启动完成。
5、ACTION_PACKAGE_ADDED:系统中添加安装包。
6、ACTION_PACKAGE_CHANGED:系统的包被改变了。
7、ACTION_PACKAGE_REMOVED:系统的包被删除了。
8、ACTION_PACKAGE_RESTARTED:系统包被重启。
9、ACTION_PACKAGE_DATA_CLEARED:系统的包数据被清空。
10、ACTION_BATTERY_CHANGED:电池电量改变。
11、ACTION_SHUTDOWN:系统被关闭。
12、ACTION_BATTRY_LOW:电池电量低。
13、ACTION_AIRPLANE_MODE_CHANGE:飞行模式的状态改变。
14、CONNECTIVITY_SERVICE:网络连接状态改变。
系统广播常量非常的多,在这里就不一一详细介绍了,我们举两个例子来示范下就行:
<1>网络连接的状态改变
当我们开发一些网络应用的时候,我们的应用需要时刻监听手机网络连接的状态变化,当网络处于连接时,应用可以访问服务器获取所需要的信息资源,而当网络处于断开的状态时,则需要立即提示用户“网络连接中断”,访问服务器工作终止。那么要实现该项功能,我们可以在该应用中创建一个BroadcastReceiver用于时刻监听并接收系统网络状态变化的广播消息,然后向用户提示“网络连接中断”信息或者执行其它相关操作。下面我们来实现该项功能,首先在AndroidManifest.xml文件中注册一个BroadcastReceiver,具体代码如下:
- <receiver android:name=".broadcastreceiver.NetworkConnectBroadCastReceiver" >
- <intent-filter >
- <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
- </intent-filter>
- </receiver>
注意,<action>里面的值是系统提供的,其实就是Context.CONNECTIVITY_CHANGE所对应的常量值,具体可以参考Android API文档中关于Intent的详细用法。
而由于要使用网络连接的类库,因此需要声明相关的权限才行,下面是对应的权限声明:
- <!-- 读取网络状态 权限-->
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
接下来在应用程序中自定义一个BroadcastReceiver,代码如下:
- package com.fendou.broadcastreceiver;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.net.ConnectivityManager;
- import android.net.NetworkInfo;
- import android.net.NetworkInfo.State;
- import android.util.Log;
- import android.widget.Toast;
- public class NetworkConnectBroadCastReceiver extends BroadcastReceiver {
- private static final String TAG = "ygh";
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- //Log.i(TAG, "NetworkConnectBroadCastReceiver");
- doSomething(context);
- }
- public void doSomething(Context mContext)
- {
- if(isNetworkConnect(mContext))
- {
- Toast. makeText(mContext, "网络连接成功!!", Toast.LENGTH_LONG).show();
- Log. i(TAG, "网络连接成功!!" );
- }
- else
- {
- Toast. makeText(mContext, "网络已断开连接!!", Toast.LENGTH_LONG).show();
- Log. i(TAG, "网络已断开连接!!" );
- }
- }
- //判断网络是否连接
- public boolean isNetworkConnect(Context mContext)
- {
- boolean flag = false;
- //获取网络连接管理对象
- ConnectivityManager manager = (ConnectivityManager) mContext.getSystemService(mContext.CONNECTIVITY_SERVICE);
- //得到所有网络连接的信息
- NetworkInfo[] mInfo = manager.getAllNetworkInfo();
- if(mInfo != null){
- for(int i = 0 ; i < mInfo.length;i++)
- {
- //一一判断是否有已经连接的网络
- State mState = mInfo[i].getState();
- if(mState == NetworkInfo.State.CONNECTED )
- {
- flag = true;
- return flag;
- }
- }
- }
- return flag;
- }
- }
接下来我们进行实验,首先运行该程序,然后再模拟器中依次点击settings ->Wireless&networks-> Mobile netWorks后进入界面如下:
我们来模拟下网络断开和连接时的操作,首先点击选中Data enabled选项,连接网络,界面弹出一个“网络连接成功”的Toast信息,说明BroadcastReceiver接收到了系统发出的网络状态改变的广播,示例图如下:
然后再点击Data enabled选项,断开网络,弹出一个“网络已断开连接”的Toast信息,示例图如下:
<2>飞行模式状态改变
我们接着再做一个实验,就是创建一个BroadcastReceiver对象监听并接收当飞行模式状态改变后系统发送的Broadcast。首先列出注册代码:
- <receiver android:name=".broadcastreceiver.AirPlaneModeChangeBroadCastReceiver" >
- <intent-filter >
- <action android:name="android.intent.action.AIRPLANE_MODE" />
- </intent-filter>
- </receiver>
然后,创建一个BroadcastReceiver,具体代码如下:
- package com.fendou.broadcastreceiver;
- import com.fendou.utils.StaticContens;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- public class AirPlaneModeChangeBroadCastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- StaticContens. showNotification(context, "通知","飞行模式状态改变了!!" );
- }
- }
代码比较简单,就接收系统发送过来的广播,接收成功后show一个通知。那么下面我们进行实验,首先运行该程序,然后再模拟器中依次点击settings ->Wireless&networks后进入界面如下:
我们点击Airplane mode 选项,改变手机模拟器的飞行模式状态,随后弹出一个Notification通知,如下图示例:
由此我们可知Broadcast接收成功。
总结:BroadcastReceiver作为四大组件之一,应用的非常广泛,我们可以通过自定义发送Broadcast和使用BroadcastReceiver接收Broadcast来完成很多事情,例如接收系统Broadcast来执行相关逻辑操作(实现与系统的交互)和完成进程间的通信等等。