简介
Broadcast Receiver用于接收并处理广播通知(broadcastannouncements)。
多数的广播是系统发起的,如地域变换、电量不足、来电来信等。
程序可以有任意数量的Broadcast Receivers来响应它觉得重要的通知。
Broadcast Receiver可以通过多种方式通知用户:启动activity、使用NotificationManager、开启背景灯、振动设备、播放声音等,最典型的是在状态栏显示一个图标,这样用户就可以点它打开看通知内容。
通常我们的某个应用或系统本身在某些事件(电池电量不足、来电来短信)来临时会广播一个Intent出去,我们可以利用注册一个Broadcast Receiver来监听到这些Intent并获取Intent中的数据。
在程序中接受系统发的广播
我们举一个例子说明,一个接受系统Date改变的广播的例子。
先新建一个HelloBroadcastReceiver.java类,继承自BroadcastReceiver并复写它的onReceive方法。这样就创建了一个专门用来接收广播的类。
- package com.tianjf;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.util.Log;
- public class HelloBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d("debug", "Date has been changed!");
- Log.d("debug", ""+intent.getAction());
- }
- }
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.tianjf"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk android:minSdkVersion="4" />
- <application
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name" >
- <activity
- android:name=".BroadcastDemoActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <!-- 定义Broadcast Receiver指定监听的Action(系统Date改变action) -->
- <receiver android:name="HelloBroadcastReceiver" >
- <intent-filter>
- <action android:name="android.intent.action.DATE_CHANGED" />
- </intent-filter>
- </receiver>
- </application>
- </manifest>
OK,运行之后,更改系统Date看看,果然打印了两行Log。(着两行Log有时候打得出来,有时候打不出来,不知道神马原因)
接收自己发的广播
直接上代码
BroadcastDemoActivity.java
- package com.tianjf;
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- public class BroadcastDemoActivity extends Activity implements OnClickListener {
- private Button button;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- button = (Button) findViewById(R.id.button);
- button.setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- // 定义一个intent
- Intent intent = new Intent();
- intent.setAction("MY_BROADCAST_ACTION");
- intent.putExtra("yaoyao",
- "yaoyao is 189 days old ,27 weeks -- 2010-08-10");
- // 广播出去
- sendBroadcast(intent);
- }
- }
- package com.tianjf;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.util.Log;
- public class HelloBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d("debug", "" + intent.getAction());
- if (intent.getAction().equals("android.intent.action.DATE_CHANGED")) {
- Log.d("debug", "Date has been changed!");
- Log.d("debug", "" + intent.getAction());
- } else if (intent.getAction().equals("MY_BROADCAST_ACTION")) {
- Log.d("debug", "Say Hello to Yaoyao!");
- Log.d("debug", intent.getStringExtra("yaoyao"));
- }
- }
- }
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.tianjf"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk android:minSdkVersion="4" />
- <application
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name" >
- <activity
- android:name=".BroadcastDemoActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <!-- 定义Broadcast Receiver指定监听的Action(系统Date改变action和自动义的广播) -->
- <receiver android:name="HelloBroadcastReceiver" >
- <intent-filter>
- <action android:name="MY_BROADCAST_ACTION" />
- <action android:name="android.intent.action.DATE_CHANGED" />
- </intent-filter>
- </receiver>
- </application>
- </manifest>
注册广播的2种方式
BroadcastReceiver在没有注册广播地址之前是使用不了的。没有注册广播地址的BroadcastReceiver就像一个缺少选台按钮的收音机,虽然功能俱备,但也无法收到电台的信号。有两种注册方法:
- 静态注册。在AndroidManifest.xml文件中配置(上文两个例子已经介绍了)
- 动态注册。要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service注册一个广播
静态注册的方法参照以上两个例子。
至于动态注册的方法,我们看以下例子:
将以上的BroadcastDemoActivity做如下修改
- package com.tianjf;
- import android.app.Activity;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- public class BroadcastDemoActivity extends Activity implements OnClickListener {
- private Button button;
- HelloBroadcastReceiver receiver;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- button = (Button) findViewById(R.id.button);
- button.setOnClickListener(this);
- receiver = new HelloBroadcastReceiver();
- IntentFilter filter = new IntentFilter();
- filter.addAction("android.intent.action.DATE_CHANGED");
- filter.addAction("MY_BROADCAST_ACTION");
- registerReceiver(receiver, filter);
- }
- @Override
- public void onClick(View v) {
- // 定义一个intent
- Intent intent = new Intent();
- intent.setAction("MY_BROADCAST_ACTION");
- intent.putExtra("yaoyao",
- "yaoyao is 189 days old ,27 weeks -- 2010-08-10");
- // 广播出去
- sendBroadcast(intent);
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- unregisterReceiver(receiver);
- }
- }
- 12-19 07:16:04.186: E/ActivityThread(23244): Activity com.tianjf.BroadcastDemoActivity has leaked IntentReceiver com.tianjf.HelloBroadcastReceiver@40cef150 that was originally registered here. Are you missing a call to unregisterReceiver()?
两种广播注册方式的区别:
- 静态注册的方法,不管应用处于什么状态,甚至于退出应用,都能接收广播并作相应的处理。当然,相对来说是比较耗电的
- 动态注册的方法,因为动态注册必须要注销广播,所以,应用退出也就不接收广播了。所以是省电的
有序广播(Ordered Broadcast)
有序广播比较特殊,它每次只发送到优先级较高的接收者那里,然后由优先级高的接受者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播
为了演示有序广播的流程,我们创建三个接收者的代码,如下:
- package com.tianjf;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- public class FirstBroadcastReceiver extends BroadcastReceiver {
- private static final String TAG = "TestOrderdBroadcastReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- String msg = intent.getStringExtra("msg");
- Log.i(TAG, "FirstBroadcastReceiver: " + msg);
- Bundle bundle = new Bundle();
- bundle.putString("msg", msg + "@FirstBroadcastReceiver");
- setResultExtras(bundle);
- }
- }
- package com.tianjf;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- public class SecondBroadcastReceiver extends BroadcastReceiver {
- private static final String TAG = "TestOrderdBroadcastReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- String msg = getResultExtras(true).getString("msg");
- Log.i(TAG, "SecondBroadcastReceiver: " + msg);
- Bundle bundle = new Bundle();
- bundle.putString("msg", msg + "@SecondBroadcastReceiver");
- setResultExtras(bundle);
- }
- }
- package com.tianjf;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.util.Log;
- public class ThirdBroadcastReceiver extends BroadcastReceiver {
- private static final String TAG = "TestOrderdBroadcastReceiver";
- @Override
- public void onReceive(Context context, Intent intent) {
- String msg = getResultExtras(true).getString("msg");
- Log.i(TAG, "ThirdBroadcastReceiver: " + msg);
- }
- }
之后,我们需要为三个接收者注册广播地址,我们修改一下AndroidMainfest.xml文件:
- <receiver android:name=".FirstBroadcastReceiver" >
- <intent-filter android:priority="1000" >
- <action android:name="android.intent.action.MY_BROADCAST" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- <receiver android:name=".SecondBroadcastReceiver" >
- <intent-filter android:priority="999" >
- <action android:name="android.intent.action.MY_BROADCAST" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
- <receiver android:name=".ThirdBroadcastReceiver" >
- <intent-filter android:priority="998" >
- <action android:name="android.intent.action.MY_BROADCAST" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
现在,我们需要写一下发送广播的代码,如下:
- Intent intent = new Intent("android.intent.action.MY_BROADCAST");
- intent.putExtra("msg", "hello receiver.");
- sendOrderedBroadcast(intent, "myAndroid.permission.MY_BROADCAST_PERMISSION");
所以我们在AndroidMainfest.xml中定义一个权限:
- <permission
- android:name="myAndroid.permission.MY_BROADCAST_PERMISSION"
- android:protectionLevel="normal" />
- <uses-permission android:name="myAndroid.permission.MY_BROADCAST_PERMISSION" />
然后我们点击发送按钮发送一条广播,控制台打印如下:
- 12-20 08:13:45.978: I/TestOrderdBroadcastReceiver(29914): FirstBroadcastReceiver: hello receiver.
- 12-20 08:13:46.088: I/TestOrderdBroadcastReceiver(29914): SecondBroadcastReceiver: hello receiver.@FirstBroadcastReceiver
- 12-20 08:13:46.138: I/TestOrderdBroadcastReceiver(29914): ThirdBroadcastReceiver: hello receiver.@FirstBroadcastReceiver@SecondBroadcastReceiver
我们看到接收是按照顺序的,第一个和第二个都在结果集中加入了自己的标记,并且向优先级低的接收者传递下去。
既然是顺序传递,试着终止这种传递,看一看效果如何,我们修改FirstReceiver的代码,在onReceive的最后一行添加以下代码:
- abortBroadcast();
- 12-20 08:13:45.978: I/TestOrderdBroadcastReceiver(29914): FirstBroadcastReceiver: hello receiver.
注:只有有序广播可以用abortBroadcast();来终止,其他广播是没有效果的。
BroadcastReceiver的生命周期
BroadcastReceiver的生命周期很短,也就是onReceive的执行时间,onReceive执行结束,BroadcastReceiver的实例就被销毁(不管那种注册方法)
Broadcast Action 大全
android.intent.action.BATTERY_CHANGED
充电状态,或者电池的电量发生变化
android.intent.action.BOOT_COMPLETED
在系统启动后,这个动作被广播一次(只有一次)
android.intent.action.CFF
语音电话的呼叫转移状态已经改变
android.intent.action.CONFIGURATION_CHANGED
设备的配置信息已经改变,参见 Resources.Configuration
android.intent.action.DATA_ACTIVITY
电话的数据活动(data activity)状态(即收发数据的状态)已经改变
android.intent.action.DATA_STATE
电话的数据连接状态已经改变
android.intent.action.DATE_CHANGED
日期被改变
android.server.checkin.FOTA_CANCEL
取消所有被挂起的 (pending) 更新下载
android.server.checkin.FOTA_INSTALL
更新已经被确认,马上就要开始安装
android.server.checkin.FOTA_READY
更新已经被下载,可以开始安装
android.server.checkin.FOTA_RESTART
恢复已经停止的更新下载
android.server.checkin.FOTA_UPDATE
通过 OTA 下载并安装操作系统更新
android.intent.action.MEDIABUTTON
用户按下了"Media Button"
android.intent.action.MEDIA_BAD_REMOVAL
扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)
android.intent.action.MEDIA_EJECT
用户想要移除扩展介质(拔掉扩展卡)
android.intent.action.MEDIA_MOUNTED
扩展介质被插入,而且已经被挂载
android.intent.action.MEDIA_REMOVED
扩展介质被移除
android.intent.action.MEDIA_SCANNER_FINISHED
已经扫描完介质的一个目录
android.intent.action.MEDIA_SCANNER_STARTED
开始扫描介质的一个目录
android.intent.action.MEDIA_SHARED
扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享
android.intent.action.MEDIA_UNMOUNTED
扩展介质存在,但是还没有被挂载 (mount)
android.intent.action.MWI
电话的消息等待(语音邮件)状态已经改变
android.intent.action.NETWORK_TICKLE_RECEIVED
设备收到了新的网络 "tickle" 通知
android.intent.action.PACKAGE_ADDED
设备上新安装了一个应用程序包
android.intent.action.PACKAGE_REMOVED
设备上删除了一个应用程序包
android.intent.action.PHONE_STATE
电话状态已经改变
android.intent.action.PROVIDER_CHANGED
更新将要(真正)被安装
android.intent.action.PROVISIONING_CHECK
要求 polling of provisioning service 下载最新的设置
android.intent.action.SCREEN_OFF
屏幕被关闭
android.intent.action.SCREEN_ON
屏幕已经被打开
android.intent.action.SERVICE_STATE
电话服务的状态已经改变
android.intent.action.SIG_STR
电话的信号强度已经改变
android.intent.action.STATISTICS_REPORT
要求 receivers 报告自己的统计信息
android.intent.action.STATISTICS_STATE_CHANGED
统计信息服务的状态已经改变
android.intent.action.TIMEZONE_CHANGED
时区已经改变
android.intent.action.TIME_SET
时间已经改变(重新设置)
android.intent.action.TIME_TICK
当前时间已经变化(正常的时间流逝)
android.intent.action.UMS_CONNECTED
设备进入 USB 大容量存储模式。
android.intent.action.UMS_DISCONNECTED
设备从 USB 大容量存储模式退出
android.intent.action.WALLPAPER_CHANGED
系统的墙纸已经改变
android.intent.action.XMPP_CONNECTED
XMPP 连接已经被建立
android.intent.action.XMPP_DI
XMPP 连接已经被断开