9.BroadcastReceiver
9.1.什么是BroadcastReceiver
- 安卓四大组件之一, 用来接收广播, 当收到广播的时候, 可以执行一段代码.
- 安卓手机在一些事件发生的时候会发出广播, 例如: 开机, 收到短信, 呼出电话, 网络切换, 电量低…
- 我们定义一个广播接收者, 就可以接收这些广播, 在事件发生的时候, 做一些事情.
- 我们也可以自己调用API发送广播, 通知其他接收者做一些事情.
9.2.如何收广播, 创建BroadcastReciever
- 定义类继承BroadcastReceiver, 实现onReceive()方法
- 清单文件注册
<receiver>
标签, 用<intent-filter>
和<action>
声明接收哪一类广播 - 当收到广播的时候, 就会自动执行onReceive()方法
创建
public class SmsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) { // 收到广播时自动执行
System.out.println("收到广播啦");
}
}
清单文件
<receiver android:name="net.dxs.broadcastreceiver.SmsReceiver" >
<intent-filter android:priority="2147483647" >
<!-- 接收短信广播 -->
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
注意: 从安卓3.1版本之后, 广播默认不会启动一个安装到手机上却从未启动过的应用. 发广播时希望包含未启动的应用要设置intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
示例源码->百度网盘
9.3.如何发广播
- a.普通广播:
- 接收者之间不能传递数据, 不能中断
- 创建Intent, 指定Action, 使用sendBroadcast()发送广播
- b.有序广播
- 接收者之间可以传递数据, 可以中断
- 创建Intent, 指定Action, 使用sendOrderedBroadcast()发送广播
发送普通广播
public void sendNormal(View v) {
Intent intent = new Intent("dxs.broadcast.NORMAL");
intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); // 包含从未启动过的应用(3.1之后默认不包含)
intent.putExtra("data", "普通广播Intent发送的数据");
sendBroadcast(intent, null);
}
发送有序广播
public void sendOrdered(View v) {
Intent intent = new Intent("dxs.broadcast.ORDERED");
intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
intent.putExtra("data", "有序广播Intent发送的数据");
Bundle bundle = new Bundle();
bundle.putString("name", "张三");
bundle.putInt("age", 21);
sendOrderedBroadcast(intent, "dxs.permission.BROADCAST", new ResultReceiver(), null, 1, "MainActivity", bundle);
}
示例源码->百度网盘
示例源码->百度网盘
示例源码->百度网盘
普通广播和有序广播接收顺序指定
- 接收者的顺序通过<intent-filter android:priority="2">
指定, 越大越先收到
- 优先级的取值范围: -2147483648 至 2147483647
9.4.发广播时传递数据
- a.使用Intent传递数据
- 无论普通广播, 还是有序广播, 都可以使用Intent给所有的接收者传递数据
- 该数据在多个接收者之间不能修改
- b.使用ResultAPI
- 有序广播可以使用ResultAPI传递数据
- 其他接收者收到这些数据后是可以修改的
9.5.ResultReceiver
- sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)方法的第三个参数可以传递一个BroadcastReceiver
- 这个接收者不用在清单中注册, 它会在最后一个收到广播
- 可以用他来检测广播传递的数据最后被修改成什么样了
9.6.中断广播
- 有序广播可以使用abortBroadcast()中断, 阻止后面的接收者接收广播(ResultReceiver除外)
9.7.广播的权限
- a.发送端要求接收端的权限
- 在发送广播的时候, 第二个参数可以传入一个权限, 接收端必须持有该权限才能接收广播(ResultReceiver除外)
- b.接收端要求发送端的权限
- 接收端在
<receiver>
标签的android:permission
属性中可以指定发送端必须持有某个权限, 如果发送端没有权限, 接收端将不接收广播
- 接收端在
9.8.代码注册临时的BroadcastReceiver
- 使用清单文件注册的广播接收者在手机上永久有效(除非卸载)
- registerReceiver()可以注册一个临时的广播接收者
- unregisterReceiver()可以把临时的接收者注销
示例源码->百度网盘
9.9.BroadcastReceiver中耗时操作的问题
- BroadcastReceive是在主线程中执行的, 不能执行一些耗时的操作
- 广播接收者的生命周期从收到广播的时候开始, 到onReceive()方法执行结束后结束. 进程中如果没有其他组件, 容易被杀死.
- 如果在新线程中做耗时的操作, 由于进程会被杀, 操作可能就不能执行完毕.
9.10.Activity中耗时操作的问题
- Activity也是主线程中执行代码, 同样不能执行耗时的操作
- Activity退出后进程中如果没有其他组件, 就成为了空的进程, 非常容易被杀死.
- Activity在后台的时候, 如果进程中没有其他组件, 也有可能被杀死.
- 如果在新线程中做耗时的操作, 由于进程会被杀, 操作可能就不能执行完毕.
9.11.短信黑名单
- 定义广播接收者接收android.provider.Telephony.SMS_RECEIVED, 需要android.permission.RECEIVE_SMS权限
- 收到广播后获取短信数据”pdus”, 封装成SmsMessage对象, 过滤数据
- 如果遇到敏感数据, abortBroadcast()
public class SmsReceiver extends BroadcastReceiver {
// 系统收到短信 -> 发出广播 -> 自定义接收者 -> 过滤数据(中断) -> 短信应用(广播接收者) -> 通知
@Override
public void onReceive(Context context, Intent intent) { // 收到广播时自动执行
System.out.println("onReceive");
Object[] pdus = (Object[]) intent.getExtras().get("pdus");
for (Object pdu : pdus) {
SmsMessage sms = SmsMessage.createFromPdu((byte[]) pdu); // 把pdu封装成SmsMessage对象
Date date = new Date(sms.getTimestampMillis());
String num = sms.getOriginatingAddress();
String body = sms.getMessageBody();
System.out.println(date + ": " + num + ": " + body);
if ("18600012345".equals(num))
abortBroadcast();
}
}
}
示例源码->百度网盘
9.12.自动IP拨号
- 在呼出电话的时候, 系统会发出一个广播
- 我们定义接收者, 接收这个广播, 改变其中的电话号码
- 下一个接收者(系统的呼叫功能), 呼叫的号码就是我们修改后的号码了
public class IPReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String number = getResultData();
if (!number.startsWith("17951"))
setResultData("17951" + number);
// setResultData("");
}
}
附录广播的一些权限
BATTERY_CHANGED_ACTION 充电状态,或者电池的电量发生变化 "android.intent.action.BATTERY_CHANGED"
BOOT_COMPLETED_ACTION 在系统启动后,这个动作被广播一次(只有一次) "android.intent.action.BOOT_COMPLETED"
CALL_FORWARDING_STATE_CHANGED_ACTION 语音电话的呼叫转移状态已经改变 "android.intent.action.CFF"
CONFIGURATION_CHANGED_ACTION 设备的配置信息已经改变,参见 Resources.Configuration. "android.intent.action.CONFIGURATION_CHANGED" Creator CREATOR 无 无
DATA_ACTIVITY_STATE_CHANGED_ACTION 电话的数据活动(data activity)状态(即收发数据的状态)已经改变。"android.intent.action.DATA_ACTIVITY"
DATA_CONNECTION_STATE_CHANGED_ACTION 电话的数据连接状态已经改变 "android.intent.action.DATA_STATE"
DATE_CHANGED_ACTION 日期被改变 "android.intent.action.DATE_CHANGED"
FOTA_CANCEL_ACTION 取消所有被挂起的 (pending) 更新下载 "android.server.checkin.FOTA_CANCEL"
FOTA_INSTALL_ACTION 更新已经被确认,马上就要开始安装 "android.server.checkin.FOTA_INSTALL"
FOTA_READY_ACTION 更新已经被下载,可以开始安装 "android.server.checkin.FOTA_READY"
FOTA_RESTART_ACTION 恢复已经停止的更新下载 "android.server.checkin.FOTA_RESTART"
FOTA_UPDATE_ACTION 通过 OTA 下载并安装操作系统更新 "android.server.checkin.FOTA_UPDATE"
MEDIABUTTON_ACTION 用户按下了“Media Button” "android.intent.action.MEDIABUTTON"
MEDIA_BAD_REMOVAL_ACTION 扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount) "android.intent.action.MEDIA_BAD_REMOVAL"
MEDIA_EJECT_ACTION 用户想要移除扩展介质(拔掉扩展卡) "android.intent.action.MEDIA_EJECT"
MEDIA_MOUNTED_ACTION 扩展介质被插入,而且已经被挂载 "android.intent.action.MEDIA_MOUNTED"
MEDIA_REMOVED_ACTION 扩展介质被移除。 "android.intent.action.MEDIA_REMOVED"
MEDIA_SCANNER_FINISHED_ACTION 已经扫描完介质的一个目录 "android.intent.action.MEDIA_SCANNER_FINISHED"
MEDIA_SCANNER_STARTED_ACTION 开始扫描介质的一个目录 "android.intent.action.MEDIA_SCANNER_STARTED"
MEDIA_SHARED_ACTION 扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享 "android.intent.action.MEDIA_SHARED"
MEDIA_UNMOUNTED_ACTION 扩展介质存在,但是还没有被挂载 (mount) "android.intent.action.MEDIA_UNMOUNTED"
MESSAGE_WAITING_STATE_CHANGED_ACTION 电话的消息等待(语音邮件)状态已经改变 "android.intent.action.MWI"
TIMEZONE_CHANGED_ACTION 时区已经改变 "android.intent.action.TIMEZONE_CHANGED"
TIME_CHANGED_ACTION 时间已经改变(重新设置) "android.intent.action.TIME_SET"
TIME_TICK_ACTION 当前时间已经变化(正常的时间流逝) "android.intent.action.TIME_TICK"
UMS_CONNECTED_ACTION 设备进入 USB 大容量存储模式 "android.intent.action.UMS_CONNECTED"
UMS_DISCONNECTED_ACTION 设备从 USB 大容量存储模式退出 "android.intent.action.UMS_DISCONNECTED"
WALLPAPER_CHANGED_ACTION 系统的墙纸已经改变 "android.intent.action.WALLPAPER_CHANGED"
XMPP_CONNECTED_ACTION XMPP 连接已经被建立 "android.intent.action.XMPP_CONNECTED"
XMPP_DISCONNECTED_ACTION XMPP 连接已经被断开 "android.intent.action.XMPP_DI
NETWORK_TICKLE_RECEIVED_ACTION 设备收到了新的网络 "tickle" 通知 "android.intent.action.NETWORK_TICKLE_RECEIVED"
PACKAGE_ADDED_ACTION 设备上新安装了一个应用程序包 "android.intent.action.PACKAGE_ADDED"
PACKAGE_REMOVED_ACTION 设备上删除了一个应用程序包 "android.intent.action.PACKAGE_REMOVED"
PHONE_STATE_CHANGED_ACTION 电话状态已经改变 "android.intent.action.PHONE_STATE"
PROVIDER_CHANGED_ACTION 更新将要(真正)被安装 "android.intent.action.PROVIDER_CHANGED"
PROVISIONING_CHECK_ACTION 要求 polling of provisioning service 下载最新的设置 "android.intent.action.PROVISIONING_CHECK"
SCREEN_OFF_ACTION 屏幕被关闭 "android.intent.action.SCREEN_OFF"
SCREEN_ON_ACTION 屏幕已经被打开 "android.intent.action.SCREEN_ON"
SERVICE_STATE_CHANGED_ACTION 电话服务的状态已经改变 "android.intent.action.SERVICE_STATE"
SIGNAL_STRENGTH_CHANGED_ACTION 电话的信号强度已经改变 "android.intent.action.SIG_STR"
STATISTICS_REPORT_ACTION 要求 receivers 报告自己的统计信息 "android.intent.action.STATISTICS_REPORT"
STATISTICS_STATE_CHANGED_ACTION 统计信息服务的状态已经改变 "android.intent.action.STATISTICS_STATE_CHANGED"
Intent.ACTION_AIRPLANE_MODE_CHANGED; //关闭或打开飞行模式时的广播
Intent.ACTION_BATTERY_CHANGED;//充电状态,或者电池的电量发生变化
//电池的充电状态、电荷级别改变,不能通过组建声明接收这个广播,只有通过Context.registerReceiver()注册
Intent.ACTION_BATTERY_LOW;//表示电池电量低
Intent.ACTION_BATTERY_OKAY;//表示电池电量充足,即从电池电量低变化到饱满时会发出广播
Intent.ACTION_BOOT_COMPLETED;//在系统启动完成后,这个动作被广播一次(只有一次)。
Intent.ACTION_CAMERA_BUTTON;//按下照相时的拍照按键(硬件按键)时发出的广播
Intent.ACTION_CLOSE_SYSTEM_DIALOGS;//当屏幕超时进行锁屏时,当用户按下电源按钮,长按或短按(不管有没跳出话框),进行锁屏时,android系统都会广播此Action消息
Intent.ACTION_CONFIGURATION_CHANGED;//设备当前设置被改变时发出的广播(包括的改变:界面语言,设备方向,等,请参考Configuration.java)
Intent.ACTION_DATE_CHANGED;//设备日期发生改变时会发出此广播
Intent.ACTION_DEVICE_STORAGE_LOW;//设备内存不足时发出的广播,此广播只能由系统使用,其它APP不可用?
Intent.ACTION_DEVICE_STORAGE_OK;//设备内存从不足到充足时发出的广播,此广播只能由系统使用,其它APP不可用?
Intent.ACTION_DOCK_EVENT;发出此广播的地方frameworks\base\services\java\com\android\server\DockObserver.java
Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE;移动APP完成之后,发出的广播(移动是指:APP2SD)
Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;//正在移动APP时,发出的广播(移动是指:APP2SD)
Intent.ACTION_GTALK_SERVICE_CONNECTED;//Gtalk已建立连接时发出的广播
Intent.ACTION_GTALK_SERVICE_DISCONNECTED;//Gtalk已断开连接时发出的广播
Intent.ACTION_HEADSET_PLUG;//在耳机口上插入耳机时发出的广播
Intent.ACTION_INPUT_METHOD_CHANGED;//改变输入法时发出的广播
Intent.ACTION_LOCALE_CHANGED;//设备当前区域设置已更改时发出的广播
Intent.ACTION_MANAGE_PACKAGE_STORAGE;//
Intent.ACTION_MEDIA_BAD_REMOVAL;//未正确移除SD卡(正确移除SD卡的方法:设置--SD卡和设备内存--卸载SD卡),但已把SD卡取出来时发出的广播//广播:扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)
Intent.ACTION_MEDIA_BUTTON;//按下"Media Button" 按键时发出的广播,假如有"Media Button"按键的话(硬件按键)
Intent.ACTION_MEDIA_CHECKING;//插入外部储存装置,比如SD卡时,系统会检验SD卡,此时发出的广播?
Intent.ACTION_MEDIA_EJECT;//已拔掉外部大容量储存设备发出的广播(比如SD卡,或移动硬盘),不管有没有正确卸载都会发出此广播?//广播:用户想要移除扩展介质(拔掉扩展卡)。
Intent.ACTION_MEDIA_MOUNTED;//插入SD卡并且已正确安装(识别)时发出的广播//广播:扩展介质被插入,而且已经被挂载。
Intent.ACTION_MEDIA_NOFS;//
Intent.ACTION_MEDIA_REMOVED;//外部储存设备已被移除,不管有没正确卸载,都会发出此广播?// 广播:扩展介质被移除。
Intent.ACTION_MEDIA_SCANNER_FINISHED;//广播:已经扫描完介质的一个目录
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE;//
Intent.ACTION_MEDIA_SCANNER_STARTED;//广播:开始扫描介质的一个目录
Intent.ACTION_MEDIA_SHARED;// 广播:扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享。
Intent.ACTION_MEDIA_UNMOUNTABLE;//
Intent.ACTION_MEDIA_UNMOUNTED// 广播:扩展介质存在,但是还没有被挂载 (mount)。
Intent.ACTION_NEW_OUTGOING_CALL;//
Intent.ACTION_PACKAGE_ADDED;//成功的安装APK之后//广播:设备上新安装了一个应用程序包。//一个新应用包已经安装在设备上,数据包括包名(最新安装的包程序不能接收到这个广播)
Intent.ACTION_PACKAGE_CHANGED;//一个已存在的应用程序包已经改变,包括包名
Intent.ACTION_PACKAGE_DATA_CLEARED;//清除一个应用程序的数据时发出的广播(在设置--应用管理--选中某个应用,之后点清除数据时?)//用户已经清除一个包的数据,包括包名(清除包程序不能接收到这个广播)
Intent.ACTION_PACKAGE_INSTALL;//触发一个下载并且完成安装时发出的广播,比如在电子市场里下载应用?//
Intent.ACTION_PACKAGE_REMOVED;//成功的删除某个APK之后发出的广播//一个已存在的应用程序包已经从设备上移除,包括包名(正在被安装的包程序不能接收到这个广播)
Intent.ACTION_PACKAGE_REPLACED;//替换一个现有的安装包时发出的广播(不管现在安装的APP比之前的新还是旧,都会发出此广播?)
Intent.ACTION_PACKAGE_RESTARTED;//用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播)
Intent.ACTION_POWER_CONNECTED;//插上外部电源时发出的广播
Intent.ACTION_POWER_DISCONNECTED;//已断开外部电源连接时发出的广播
Intent.ACTION_PROVIDER_CHANGED;//
Intent.ACTION_REBOOT;//重启设备时的广播
Intent.ACTION_SCREEN_OFF;//屏幕被关闭之后的广播
Intent.ACTION_SCREEN_ON;//屏幕被打开之后的广播
Intent.ACTION_SHUTDOWN;//关闭系统时发出的广播
Intent.ACTION_TIMEZONE_CHANGED;//时区发生改变时发出的广播
Intent.ACTION_TIME_CHANGED;//时间被设置时发出的广播
Intent.ACTION_TIME_TICK;//广播:当前时间已经变化(正常的时间流逝)。//当前时间改变,每分钟都发送,不能通过组件声明来接收,只有通过Context.registerReceiver()方法来注册
Intent.ACTION_UID_REMOVED;//一个用户ID已经从系统中移除发出的广播//
Intent.ACTION_UMS_CONNECTED;//设备已进入USB大容量储存状态时发出的广播?
Intent.ACTION_UMS_DISCONNECTED;//设备已从USB大容量储存状态转为正常状态时发出的广播?
Intent.ACTION_USER_PRESENT;//
Intent.ACTION_WALLPAPER_CHANGED;//设备墙纸已改变时发出的广播