Android13 研究可以静态注册的广播
一、前言
静态广播就是 AndroidManifest.xml中定义的广播,应用启动一次后续就能监听到。
在开发过程中,除了用到开机广播是静态注册的,其他广播没啥印象是可以静态注册有效的;
心血来潮,看看有啥静态广播可用。
之前分析过Android13 不能静态注册的几个广播,比如:
android.intent.action.SCREEN_ON //屏幕亮起
android.intent.action.SCREEN_OFF//屏幕亮起
android.intent.action.BATTERY_CHANGED //电池电量改变
android.intent.action.CONFIGURATION_CHANGED //配置改变,界面语言,设备方向等配置信息
android.intent.action.TIME_TICK //每分钟回调一次
主要是为了系统安全问题,这些广播都是比较频繁的,或者是重要时机的,避免普通应用乱用。
那么那些广播能静态注册呢?
本文简单分析介绍一下,有兴趣可以看看。
二、静态注册和代码示例
1、AI搜到可静态注册的常见系统广播类型
Intent 广播类型 | 作用 | 所需权限 |
---|---|---|
android.intent.action.BOOT_COMPLETED |
系统启动完成 | RECEIVE_BOOT_COMPLETED |
android.intent.action.ACTION_POWER_CONNECTED |
电源连接 | 无 |
android.intent.action.ACTION_TIMEZONE_CHANGED |
时区变更 | 无 |
android.intent.action.TIME_SET |
时间被设置 | 无 |
android.intent.action.ACTION_LOCALE_CHANGED |
语言切换 | 无 |
android.intent.action.ACTION_BATTERY_LOW |
电量不足 | 无 |
android.intent.action.ACTION_MY_PACKAGE_REPLACED |
应用更新完成 | 无 |
android.hardware.usb.action.USB_DEVICE_ATTACHED |
USB设备连接 | 无 |
android.hardware.usb.action.USB_DEVICE_DETACHED |
USB断开 | 无 |
上面是AI 搜索提供的可静态注册的广播,但是实际真的可以吗?
可能Android8 或者更旧的版本,很多广播可以静态注册,但是Android新版本上基本不行了!
下面是实测示例。
三、静态注册、动态注册对比示例
这个有静态和动态监听广播代码,一个Activity和一个Service动态监听、一个Broadcast静态监听;
1、 AndroidManifest.xml代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:sharedUserId="android.uid.system" //系统签名应用哦!
xmlns:tools="http://schemas.android.com/tools">
<application ...>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyService"
android:exported="false">
</service>
<receiver
android:name=".MyBroadcast"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.MY_CUSTOM_ACTION" />
<action android:name="TestBroadcast" />
<!-- 可添加多个 action 或 category -->
</intent-filter>
<!-- 系统启动完成广播 -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<!-- 网络连接变化广播 -->
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
<!-- 应用安装/卸载广播(需指定包名) -->
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
<!-- other intent-filter...-->
<intent-filter>
<action android:name="com.android.intent.action.SHOW_CONTRAST_DIALOG" />
<action android:name="android.intent.action.TIME_TICK" />
<action android:name="android.intent.action.ACTION_TIMEZONE_CHANGED" />
<action android:name="android.intent.action.ACTION_LOCALE_CHANGED" />
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
</intent-filter>
</receiver>
</application>
</manifest>
注册activity、service和receiver;receiver 定义静态广播监听的广播类型;
demo的是系统签名应用,否则无法接收到开机广播;
2、MyBroadcast.java
静态接收广播的类
package com.liwenzhi.broadcasttest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MyBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
LogUtil.debug("received broadcast intent action = " + intent.getAction());
context.startService(new Intent(context, MyService.class));
}
}
这个比较简单,接收静态广播,并拉起服务;
服务只会启动一次,所以多次调用是没有问题的。
3、MySerivce.java
普通的Service,启动会回动态监听广播,并且拉起Activity。
package com.liwenzhi.broadcasttest;
import static com.liwenzhi.broadcasttest.MainActivity.*;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.IBinder;
import androidx.core.content.ContextCompat;
/**
* 普通服务类
*/
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
LogUtil.debug("onCreate");
//动态注册广播
registerBroadcast(this);
//测试发送自定义广播
Intent intent = new Intent("TestBroadcast");
sendBroadcast(intent);
//拉起Activity,广播或者Service拉起Activity必须添加 FLAG_ACTIVITY_NEW_TASK flags
Intent intent2 = new Intent(this, MainActivity.class);
intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //不加这个会报错!
startActivity(intent2);
LogUtil.debug("onCreate End");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtil.debug("onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
LogUtil.debug("onBind");
return null;
}
public void registerBroadcast(Context context) {
LogUtil.debug("");
IntentFilter filter = new IntentFilter();
filter.addAction("TestBroadcast"); //自定义广播
filter.addAction(Intent.ACTION_TIME_CHANGED);//time_set
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(Intent.ACTION_BOOT_COMPLETED); //开机广播
filter.addAction(Intent.ACTION_PACKAGE_ADDED); //应用安装广播
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); //网络监听广播
filter.addDataScheme("package"); // 必须添加,否则接收不到广播ACTION_PACKAGE_ADDED
registerReceiver(mReceiver, filter);
}
//动态接收广播监听对象
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
LogUtil.debug("onReceive: action = " + action);
switch (action) {
case ACTION_NET_CHANGE_ACTION:
LogUtil.debug("网络变化");
break;
case "TestBroadcast":
LogUtil.debug("自定义广播接收");
break;
}
}
};
}
这个Service主要功能:动态监听广播+打印,发送测试广播,拉起Activity;
这个Service其实写不写都可以,动态广播,在Activity监听是一样的效果;
这里只是看看Activity界面退出后,广播监听的效果;
开发的实际场景中也是有很多情况是在Service后台中动态监听广播的。
4、MainActivity.java代码
Activity 界面代码如下:
package com.liwenzhi.broadcasttest;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LogUtil.debug("");
registerBroadcast(this);//动态监听广播
}
@Override
protected void onDestroy() {
super.onDestroy();
unRegisterBroadcast(this);
}
//发送网络变化广播
public void sendBroadcast(View view) {
LogUtil.debug("");
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
sendBroadcast(intent);
}
//发送开机广播,拉起Service
public void sendBroadcast2(View view) {
LogUtil.debug("");
Intent intent = new Intent("android.intent.action.BOOT_COMPLETED");
sendBroadcast(intent);
startService(new Intent(this, MyService.class));
LogUtil.debug("End");
}
//发送自定义广播
public void sendBroadcast3(View view) {
LogUtil.debug("");
Intent intent = new Intent("TestBroadcast");
sendBroadcast(intent);
}
//注册广播监听
public void registerBroadcast(Context context) {
LogUtil.debug("");
IntentFilter filter = new IntentFilter();
filter.addAction("TestBroadcast"); //自定义广播
filter.addAction(Intent.ACTION_TIME_CHANGED);//time_set
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(Intent.ACTION_BOOT_COMPLETED); //开机广播
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); //网络监听广播
filter.addDataScheme("package"); // 必须添加,否则接收不到广播ACTION_PACKAGE_ADDED
registerReceiver(mReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED);
}
public void unRegisterBroadcast(Context context) {
context.unregisterReceiver(mReceiver);
}
//广播接收者+打印
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
LogUtil.debug("onReceive: action = " + action);
switch (action) {
case ACTION_NET_CHANGE_ACTION:
LogUtil.debug("网络变化");
break;
case "TestBroadcast":
LogUtil.debug("自定义广播接收");
break;
}
}
};
}
上面Activity 界面起来后,进行动态监听广播;
添加了三个按钮,1、发送网络变化广播;2、发送开机广播、拉起Service;3、发送自定义广播;
应用可以发送网络变化广播和开机广播?
其实是可以的,但是必须要系统签名权限,否则会报错;
但是实际场景,基本不会发出这种系统类型的广播。
5、验证过程
(1)开机重启后应用启动的广播日志
//(1)开机后静态广播监听接收到开机广播
07-09 06:01:46.413 3331 3331 I XXXAppLog: MyBroadcast.java (11 )::onReceive - received broadcast intent action = android.intent.action.BOOT_COMPLETED
//(2)拉起Service,动态监听广播
07-09 06:01:46.422 3331 3331 I XXXAppLog: MyService.java (27 )::onCreate - onCreate
07-09 06:01:46.422 3331 3331 I XXXAppLog: MyService.java (52 )::registerBroadcast -
07-09 06:01:46.458 3331 3331 I XXXAppLog: MyService.java (36 )::onCreate - onCreate End
07-09 06:01:46.459 3331 3331 I XXXAppLog: MyService.java (41 )::onStartCommand - onStartCommand
07-09 06:01:46.461 3331 3331 I XXXAppLog: MyService.java (72 )::onReceive - onReceive: action = android.net.conn.CONNECTIVITY_CHANGE
07-09 06:01:46.461 3331 3331 I XXXAppLog: MyService.java (75 )::onReceive - 网络变化
07-09 06:01:46.462 3331 3331 I XXXAppLog: MyService.java (72 )::onReceive - onReceive: action = TestBroadcast
07-09 06:01:46.462 3331 3331 I XXXAppLog: MyService.java (84 )::onReceive - 自定义广播接收
//(3)Service拉起Activity
07-09 06:01:49.730 3331 3331 I XXXAppLog: MainActivity.java (35 )::onCreate -
07-09 06:01:49.730 3331 3331 I XXXAppLog: MainActivity.java (91 )::registerBroadcast -
可以看到开机广播日志,和Service、activity启动日志;
这能监听到静态开机广播是因为应用添加了系统签名;
网络变化广播,这个广播是应用刚监听就会接收到一次的,不管系统网络是否变化。
(2)开关wifi,触发网络变化广播
07-09 06:03:40.033 3331 3331 I XXXAppLog: MyService.java (72 )::onReceive - onReceive: action = android.net.conn.CONNECTIVITY_CHANGE
07-09 06:03:40.033 3331 3331 I XXXAppLog: MyService.java (75 )::onReceive - 网络变化
07-09 06:03:40.033 3331 3331 I XXXAppLog: MainActivity.java (116 )::onReceive - onReceive: action = android.net.conn.CONNECTIVITY_CHANGE
07-09 06:03:40.034 3331 3331 I XXXAppLog: MainActivity.java (119 )::onReceive - 网络变化
上面可以看到,动态监听的广播可以接收到网络变化的广播,静态广播无法监听到网络变化广播。
(3)切换语言后的广播日志:
07-09 06:04:34.615 3331 3331 I XXXAppLog: MainActivity.java (35 )::onCreate -
07-09 06:04:34.615 3331 3331 I XXXAppLog: MainActivity.java (91 )::registerBroadcast -
07-09 06:04:34.643 3331 3331 I XXXAppLog: MyService.java (72 )::onReceive - onReceive: action = android.intent.action.LOCALE_CHANGED
这里可以看到切换语言后,Activity会重启,无法接收到切换语言的广播;
静态广播也是无法接收到切换语言的广播;
Service一直在后台所以会接收到语言切换的广播;
(4)时间的广播
修改时间和每分钟一次的日志:
MyService.java (72 )::onReceive - onReceive: action = android.intent.action.TIME_SET
MainActivity.java (116 )::onReceive - onReceive: action = android.intent.action.TIME_SET
MyService.java (72 )::onReceive - onReceive: action = android.intent.action.TIME_TICK
MainActivity.java (116 )::onReceive - onReceive: action = android.intent.action.TIME_TICK
静态广播也是无法接收到时间变化的广播;
Service和Activity是可以接收端时间变化的广播;
这里MyService比Activity广播接收到早,是因为MyService先注册的广播。
(5)U盘插拔广播
U盘插入和拔出日志:
//U盘插入
08-09 06:25:39.718 3303 3303 I XXXAppLog: MyService.java (72 )::onReceive - onReceive: action = android.hardware.usb.action.USB_DEVICE_ATTACHED
08-09 06:25:39.718 3303 3303 I XXXAppLog: MainActivity.java (116 )::onReceive - onReceive: action = android.hardware.usb.action.USB_DEVICE_ATTACHED
08-09 06:25:39.748 3303 3303 I XXXAppLog: MyBroadcast.java (11 )::onReceive - received broadcast intent action = android.hardware.usb.action.USB_DEVICE_ATTACHED
08-09 06:25:39.752 3303 3303 I XXXAppLog: MyService.java (41 )::onStartCommand - onStartCommand
08-09 06:25:39.756 3598 3598 I XXXAppLog: MyBroadcast.java (11 )::onReceive - received broadcast intent action = android.hardware.usb.action.USB_DEVICE_ATTACHED
//U盘拔出
08-09 06:25:56.179 3303 3303 I XXXAppLog: MyService.java (72 )::onReceive - onReceive: action = android.hardware.usb.action.USB_DEVICE_DETACHED
08-09 06:25:56.180 3303 3303 I XXXAppLog: MainActivity.java (116 )::onReceive - onReceive: action = android.hardware.usb.action.USB_DEVICE_DETACHED
08-09 06:25:56.212 3303 3303 I XXXAppLog: MyBroadcast.java (11 )::onReceive - received broadcast intent action = android.hardware.usb.action.USB_DEVICE_DETACHED
08-09 06:25:56.215 3303 3303 I XXXAppLog: MyService.java (41 )::onStartCommand - onStartCommand
08-09 06:25:56.220 3598 3598 I XXXAppLog: MyBroadcast.java (11 )::onReceive - received broadcast intent action = android.hardware.usb.action.USB_DEVICE_DETACHED
静态广播、Service和Activity都是可以接收到U盘插拔的广播;
这里静态广播会接收到两次,不清楚具体原因!
Service和Activity是可以接收端时间变化的广播;
这里MyService和Activity广播比MyBroadcast接收到早,是因为动态注册的会先监听到广播;
(6)应用安装
应用安装也是只能动态监听到,无法静态监听到;
刚开始动态广播也是无法监听到,后面搜索发现,需要添加 package声明才能正常监听。
filter.addAction(Intent.ACTION_PACKAGE_ADDED); //应用安装
filter.addAction(Intent.ACTION_PACKAGE_REMOVED); //应用卸载
filter.addDataScheme("package"); // 必须添加,否则接收不到应用安装和协助广播,这个不影响其他广播接收
6、验证可静态注册的常见系统广播类型
Intent 广播类型 | 作用 | 所需权限 |
---|---|---|
android.intent.action.BOOT_COMPLETED |
系统启动完成 | RECEIVE_BOOT_COMPLETED |
android.hardware.usb.action.USB_DEVICE_ATTACHED |
USB设备连接 | 无 |
android.hardware.usb.action.USB_DEVICE_DETACHED |
USB断开 | 无 |
实测中只有上面三个广播是可以静态注册有效的;
BOOT_COMPLETED 广播普通应用是无法接收到的,系统签名应用才可以接收到。
难道只有上面几个广播可以静态注册,没有其他了吗,其实系统还是有一些不常用的静态广播,
可以看看原生Settings的注册广播:
apps\Settings\AndroidManifest.xml
//电池电量:
<receiver android:name="com.android.settings.fuelgauge.batteryusage.BatteryUsageBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BATTERY_LEVEL_CHANGED"/>
<action android:name="com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA"/>
<action android:name="com.android.settings.battery.action.ACTION_BATTERY_PLUGGING"/>
<action android:name="com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING"/>
</intent-filter>
</receiver>
//蓝牙配对请求:
<receiver android:name=".bluetooth.BluetoothPairingRequest"
android:exported="true">
<intent-filter>
<action android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
<action android:name="android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE"/>
</intent-filter>
</receiver>
翻了一下,除了开机广播,就是上面几个电量和蓝牙配对的广播了,
还有几个看不懂的广播,估计也是用得很少的。
另外电量广播和蓝牙的广播也是需要特定的权限,普通应用也是无法监听。
电量广播:
framework/base/core/java/android/content/Intent.java
/**
* Broadcast Action: Sent when the current battery level or plug type changes.
*
* It has {@link android.os.BatteryManager#EXTRA_EVENTS} that carries a list of {@link Bundle}
* instances representing individual battery level changes with associated
* extras from {@link #ACTION_BATTERY_CHANGED}.
*
* <p class="note">
* This broadcast requires {@link android.Manifest.permission#BATTERY_STATS} permission.
*
* @hide
*/
@SystemApi
public static final String ACTION_BATTERY_LEVEL_CHANGED =
"android.intent.action.BATTERY_LEVEL_CHANGED";
蓝牙配对广播:
packages/modules/Bluetooth/framework/java/android/bluetooth/BluetoothDevice.java
/**
* Broadcast Action: This intent is used to broadcast PAIRING REQUEST
*/
@RequiresLegacyBluetoothAdminPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PAIRING_REQUEST =
"android.bluetooth.device.action.PAIRING_REQUEST";
所以,普通应用看起来只有一个U盘插拔的广播可以静态使用!
三、其他
1、Android13 研究可以静态注册的广播小结
(1)Android系统常用的静态广播有:开机广播、U盘插拔广播
(2)普通应用能用的静态广播只有U盘插拔广播
(3)静态能监听的广播动态也能监听
(4)动态监听的广播比静态监听的广播会接收到快一点
(5)U盘插/拔,静态广播会接收到两次
所以说静态广播没啥好研究的,普通应用想用来做保活不可能;
系统应用也不需要广播来进行保活,只需要在AndroidManifest定义 android:persistent=“true” 即可。
2、开机广播也可以动态监听?
是的。开机广播也可以动态监听。
正常情况,SystemServer 启动后到静态开机广播接收大概要一分多钟;
如果 SystemServer 启动后,拉起某个Service,动态监听开机广播,大概半分钟就能接收到开机广播。
静态开机广播是在上层大部分逻辑初始完成+Launcher启动一会后才能发出,
所以我在记忆热点启动都是在系统服务动态监听开机广播的中进行拉起热点或者进行一些系统初始化的。
总结一句话就是:任何静态监听的广播都可以动态监听,并且动态监听会比静态监听的更快接收到广播。
3、如何添加新的静态广播?
在framework/base/core/java/android/content/Intent.java 添加 Action定义就可以了吗?
其实不行,试了一下并没有用;
只是添加了Action后,应用导入更新的framework jar,可以应用调用到自定义的Intent变量而已。
但是也要注意Action的格式 android.intent.action.XXX:
public static final String ACTION_BRO_TEST = "android.intent.action.BRO_TEST";
如果后面的字符串随便写个AA或者XXX是会编译报错的,需要另外配置允许的命名规则。
目前还没找到哪里是定义可以静态监听的地方,有兴趣的可以自行研究看看。
网上搜到: 白名单机制
源码中定义了允许静态注册的广播列表,如 BOOT_COMPLETED
被明确列入:
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private static final ArraySet<String> sStaticBroadcastWhitelist = new ArraySet<>();
static {
sStaticBroadcastWhitelist.add(Intent.ACTION_BOOT_COMPLETED);
sStaticBroadcastWhitelist.add(Intent.ACTION_USER_PRESENT);
// 其他允许静态注册的广播...
}
但是从Android13 的代码是无法查看到这段代码的,并且这几个关键字都没有,设置开机广播这里都没有找到。
可能在某个封装类里面有定义白名单列表的代码,但是我找不到。
4、Android广播接收者使用总结
BroadcastReceiver广播接收者是Android四大组件之一。
一般的都要在AndroidManifest中静态注册,但是只有广播接收者可以使用java代码的方法来动态注册。
https://blog.csdn.net/wenzhi20102321/article/details/53127914
5、Android13 不能静态注册的几个广播
本文介绍一些广播相关的知识,主要是静态广播注册无效的介绍。
其实从Android 8.0 就开始有这个问题的,只是本文的源码是基于Android13 分析的。
https://blog.csdn.net/wenzhi20102321/article/details/134956090
6、Android adb命令发送广播介绍
Android 广播除了代码中发送,还可以使用命令模拟发送,只要应用代码中进行了监听,都可以正常接收到的。
掌握手动发送广播命令,可以方便某些代码的调试。
https://blog.csdn.net/wenzhi20102321/article/details/136895508