Android 四大组件之BroadcastReceiver
本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.youkuaiyun.com/rozol/article/details/79606175
广播接收者
四大组件(Activity / BroadcastReceiver / Service / ContentProvider)之一
四大组件均运行于主线程
广播接收者
使用
-
编写广播接受者代码
public class AppStateReceiver extends BroadcastReceiver { private static final String TAG = AppStateReceiver.class.getSimpleName(); @Override public void onReceive(Context context, Intent intent) { // 获取当前接收广播的动作 String action = intent.getAction(); // 获取应用Uri Uri packageUri = intent.getData(); // package:包名 // 获取应用包名 String packageName = intent.getDataString(); if (action.equals("android.intent.action.PACKAGE_INSTALL") || action.equals("android.intent.action.PACKAGE_ADDED")){ Log.i(TAG, "应用: " + intent.getDataString() + " 被安装了"); } else if(action.equals("android.intent.action.PACKAGE_REMOVED")){ Log.i(TAG, "应用: " + intent.getDataString() + " 被卸载了"); } } }
-
配置监听广播事件
<receiver android:name=".AppStateReceiver"> <intent-filter> <action android:name="android.intent.action.PACKAGE_INSTALL"/> <action android:name="android.intent.action.PACKAGE_ADDED"/> <action android:name="android.intent.action.PACKAGE_REMOVED"/> <data android:scheme="package"/> </intent-filter> </receiver>
动态注册广播接收者
在清单文件里配置无效的广播接收者, 必须使用代码注册
-
动态注册广播接收者
private ScreenReceiver screenReceiver; /** * 动态注册特殊的广播 * @param view */ public void onclick2(View view){ screenReceiver = new ScreenReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.SCREEN_OFF"); filter.addAction("android.intent.action.SCREEN_ON"); // 动态注册广播接收者 registerReceiver(screenReceiver, filter); } @Override protected void onDestroy() { // 动态注销广播接收者 unregisterReceiver(screenReceiver); super.onDestroy(); }
-
ScreenReceiver 代码处理 (略)
发送广播
有序广播
-
发送广播
/** * 发送无序广播 */ public void onclick(View view){ Intent intent = new Intent(); intent.setAction("luzhuo.me"); intent.putExtra("content", "这是一条无序广播."); // 发送一条无序广播 // 只发送广播, 不管有没有接收, 且不可被 篡改 / 终止 sendBroadcast(intent); }
-
接收广播
-
清单文件配置
<receiver android:name=".my.MyBroadcastReceiver"> <intent-filter> <action android:name="luzhuo.me"/> </intent-filter> </receiver>
-
代码处理
@Override public void onReceive(Context context, Intent intent) { String content = intent.getStringExtra("content"); Log.i(TAG, content); }
-
无序广播
-
发送广播
/** * 发送有序广播 */ public void onclick1(View view){ Intent intent = new Intent(); intent.setAction("luzhuo.me.my"); // 发送一条有序广播 // 发送有序广播, 优先级最高(1000)最先被接收, 指定最终接收者(若指定不管是否被终止, 都可接收), 可被 篡改 / 终止 // 最终广播接收者(MyOrderedBroadcast3)可不在清单文件中配置 sendOrderedBroadcast(intent, null, new MyOrderedBroadcast3(), null, 1, "这是一条有序广播", null); }
-
接收广播
-
清单文件配置
<receiver android:name=".my.MyOrderedBroadcast1"> <intent-filter android:priority="1000"> <action android:name="luzhuo.me.my"/> </intent-filter> </receiver> <receiver android:name=".my.MyOrderedBroadcast2"> <intent-filter android:priority="100"> <action android:name="luzhuo.me.my"/> </intent-filter> </receiver>
-
代码处理
// MyOrderedBroadcast1.class @Override public void onReceive(Context context, Intent intent) { // 获取广播发送的数据 String content = getResultData(); Log.i(TAG, content); // 篡改广播数据 setResultData("数据已被篡改."); } // MyOrderedBroadcast2.class @Override public void onReceive(Context context, Intent intent) { String content = getResultData(); Log.i(TAG, content); // 终止广播 abortBroadcast(); } // MyOrderedBroadcast3.class @Override public void onReceive(Context context, Intent intent) { String content = getResultData(); Log.i(TAG, content); }
-
常用的系统广播
电话的呼入与呼出
-
权限:
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
-
配置:
<receiver android:name=".CallReceiver"> <intent-filter android:priority="1000"> <!-- 接收的动作 --> <action android:name="android.intent.action.NEW_OUTGOING_CALL"/> <action android:name="android.intent.action.PHONE_STATE"/> </intent-filter> </receiver>
-
接收处理代码
@Override public void onReceive(Context context, Intent intent) { // <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/> String action = intent.getAction(); if (action.equals("android.intent.action.NEW_OUTGOING_CALL")) { // 获取号码 String number = getResultData(); setResultData("123456"); // 设置号码 Log.i(TAG, "电话号码: " + number); // <uses-permission android:name="android.permission.READ_PHONE_STATE"/> } else if (action.equals("android.intent.action.PHONE_STATE")){ TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); int state = telephony.getCallState(); switch (state) { case TelephonyManager.CALL_STATE_RINGING: String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER); Log.i(TAG, "电话号码: " + phoneNumber); //等待 Log.i(TAG, "电话等待接通中"); break; case TelephonyManager.CALL_STATE_IDLE: // 挂断 Log.i(TAG, "电话已被挂断"); break; case TelephonyManager.CALL_STATE_OFFHOOK: // 接通 Log.i(TAG, "电话通话中"); break; } } }
应用的安装与卸载状态
- 见 使用
开机
-
权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
-
清单配置
<receiver android:name=".BootReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>
-
处理代码(略)
接收短信
-
权限:
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
-
清单配置
<receiver android:name=".SMSReceiver"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver>
-
处理代码
public void onReceive(Context context, Intent intent) { // <uses-permission android:name="android.permission.RECEIVE_SMS"/> // 获取短信号码和内容 Object[] objects = (Object[]) intent.getExtras().get("pdus"); for (Object pdu : objects){ SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu); // 发送短信的内容 String body = smsMessage.getMessageBody(); // 发送者 String address = smsMessage.getOriginatingAddress(); Log.i(TAG, "发送者: " + address); Log.i(TAG, "短信内容: " + body); } }
SD卡挂在与弹出
-
权限:
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
-
清单配置
<receiver android:name=".SDCardReceiver"> <intent-filter> <action android:name="android.intent.action.MEDIA_UNMOUNTED"/> <action android:name="android.intent.action.MEDIA_MOUNTED"/> <data android:scheme="file"/> </intent-filter> </receiver>
-
代码处理 (略)
屏幕的锁屏与解锁
- 见 (广播接收者 - 动态注册广播接收者)
Android8.0 API26+ 隐式广播规则改变
-
Android8.0 API26+ 规则的改变, 使得大量在清单文件里配置的静态广播不再成功接收广播事件, 相应的在Log里会打印以下内容(Background execution not allowed: receiving Intent).
2019-05-28 16:47:08.656 1118-1135/? W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.PACKAGE_ADDED dat=package:com.melon.lazymelon flg=0x4000010 (has extras) } to me.luzhuo.apprecorder/.receiver.NewAppReceiver 2019-05-28 16:48:05.714 1118-1859/? W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.PACKAGE_REMOVED dat=package:com.melon.lazymelon flg=0x4000010 (has extras) } to me.luzhuo.apprecorder/.receiver.NewAppReceiver
-
也有部分的静态广播是可以被使用的, 见"不受限的系统广播"
受限的系统广播
- 使用动态注册的方式接收广播
-
清单文件依旧如此配置 (api26-还是可以用的)
<receiver android:name=".receiver.AppStateReceiver" > <intent-filter> <action android:name="android.intent.action.PACKAGE_INSTALL"/> <action android:name="android.intent.action.PACKAGE_ADDED" /> <action android:name="android.intent.action.PACKAGE_REMOVED" /> <data android:scheme="package" /> </intent-filter> </receiver>
-
再使用动态注册进行注册一遍
/** * 动态注册广播, 解决 api26+ 隐式广播限制 */ private void registerBroadcast() { appStateReceiver = new AppStateReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.PACKAGE_INSTALL"); filter.addAction("android.intent.action.PACKAGE_ADDED"); filter.addAction("android.intent.action.PACKAGE_REMOVED"); filter.addDataScheme("package"); registerReceiver(appStateReceiver, filter); } @Override protected void onDestroy() { unregisterReceiver(appStateReceiver); super.onDestroy(); }
-
不受限的系统广播
-
系统还有一些不受限制的, 依旧可以使用静态注册方式
-
静态注册
<receiver android:name=".receiver.LanguageReceiver"> <intent-filter> <action android:name="android.intent.action.LOCALE_CHANGED"/> </intent-filter> </receiver>
Implicit Broadcast Exceptions | Description |
---|---|
ACTION_LOCKED_BOOT_COMPLETED / ACTION_BOOT_COMPLETED | 只在首次启动时发送一次, 以便进行作业、闹铃等事项的安排 |
ACTION_USER_INITIALIZE / "android.intent.action.USER_ADDED" / "android.intent.action.USER_REMOVED" | 受特权保护, 大多数正常应用无法接收它们 |
"android.intent.action.TIME_SET" / ACTION_TIMEZONE_CHANGED / ACTION_NEXT_ALARM_CLOCK_CHANGED | 时钟应用可能需要接收这些广播, 以便更新闹铃 |
ACTION_LOCALE_CHANGED | 只在语言区域发生变化时发送 |
ACTION_USB_ACCESSORY_ATTACHED / ACTION_USB_ACCESSORY_DETACHED / ACTION_USB_DEVICE_ATTACHED / ACTION_USB_DEVICE_DETACHED | USB相关事件 |
ACTION_CONNECTION_STATE_CHANGED / ACTION_CONNECTION_STATE_CHANGED / ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED | 蓝牙事件 |
ACTION_CARRIER_CONFIG_CHANGED / TelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED / "TelephonyIntents.SECRET_CODE_ACTION" / ACTION_PHONE_STATE_CHANGED / ACTION_PHONE_ACCOUNT_REGISTERED / ACTION_PHONE_ACCOUNT_UNREGISTERED | OEM电话应用需要的广播 |
LOGIN_ACCOUNTS_CHANGED_ACTION | 登录帐号的变化 |
ACTION_ACCOUNT_REMOVED | 删除账户 |
ACTION_PACKAGE_DATA_CLEARED | 用户从 Settings 清除数据时发送 |
ACTION_PACKAGE_FULLY_REMOVED | 软件包被移除时 |
ACTION_NEW_OUTGOING_CALL | 用户拨打电话行为 |
ACTION_DEVICE_OWNER_CHANGED | 设备的安全状态已经改变 |
ACTION_EVENT_REMINDER | 由"日历提供商"发送, 以向日历应用发布活动提醒 |
ACTION_MEDIA_MOUNTED / ACTION_MEDIA_CHECKING / ACTION_MEDIA_UNMOUNTED / ACTION_MEDIA_EJECT / ACTION_MEDIA_UNMOUNTABLE / ACTION_MEDIA_REMOVED / ACTION_MEDIA_BAD_REMOVAL | 用户与设备的物理交互 (安装或移除存储卷) 或启动初始化 (作为已装载的可用卷) 的一部分 |
SMS_RECEIVED_ACTION / WAP_PUSH_RECEIVED_ACTION | SMS收件应用需要的广播 |
受限制的自定义广播
- 不指定接收者, 即隐式广播
-
清单文件配置:
<receiver android:name=".receiver.MyBroadcastReceiver"> <intent-filter> <action android:name="luzhuo.me"/> </intent-filter> </receiver>
-
发送隐式广播
/** * 受限的自定义广播 * @param view */ public void myBroadcast(View view) { // 发送一条受限的自定义广播 (没有指定接收者, 即隐式广播) Intent intent = new Intent(); intent.setAction("luzhuo.me"); intent.putExtra("content", "这是一条无序广播."); sendBroadcast(intent); }
-
不受限制的自定义广播
- 指定接收者和发送者, 即显示广播
-
清单文件配置 (同 受限制的自定义广播)
-
发送显示广播
/** * 不受限的自定义广播 * @param view */ public void myBroadcastException(View view) { // 发送一条不受限的自定义广播 (指定接收者, 即显式广播) Intent intent = new Intent("me.luzhuo.broadcastreceiversdk26"); intent.setPackage(getPackageName()); intent.setAction("luzhuo.me"); intent.putExtra("content", "这是一条无序广播."); sendBroadcast(intent); }
-