简介:本资源是一个面向Android开发初学者与进阶者的开机自启功能教学Demo,聚焦静态注册广播接收器在系统级事件响应中的典型应用,解决应用需在设备启动完成后自动执行任务(如拉起服务、恢复状态)的实战需求。压缩包共1254个文件,涵盖170个XML配置文件(含AndroidManifest声明与布局)、28个Java源码(含StartupBroadcastReceiver核心实现)、157个DEX与155个CLASS字节码(体现完整编译结构)、120个JSON配置及516个flat资源文件,整体大小为7.49MB,结构完整,便于理解从声明、权限配置到广播接收全流程。已有874人学习下载,资源包含可直接运行的工程代码、详细注释、BOOT_COMPLETED广播监听与处理逻辑、RECEIVE_BOOT_COMPLETED权限配置示例,以及适配Android 8.0+后台限制的延伸说明提示,是掌握静态广播生命周期与系统事件集成的实用入门范例。
1. 开机自启 demo(静态注册广播接收器):为什么你的 App 在重启后“失联”了?
你写好了一个后台服务,能定时上报设备状态、监听网络变化、维持心跳连接——一切本地测试都稳如老狗。可一旦手机重启,App 就彻底静默:通知不弹、服务不启、日志断档。用户反馈“一关机就失效”,你查adb logcat却发现连onReceive()都没被调用。这不是代码逻辑问题,而是 Android 系统在 8.0(API 26)之后对隐式广播的系统级封杀,叠加 12+(API 31)对后台启动 Activity 的进一步限制,让“开机自启”从一个配置项,变成了必须跨过三道门禁的权限通关任务。这个 demo 不是教你怎么“偷偷唤醒”,而是用静态注册广播接收器这一唯一被系统保留的、无需运行时授权、不依赖前台进程的合法通道,在设备完成启动流程(BOOT_COMPLETED)的第一时间,安全、合规、可验证地拉起你的初始化逻辑。它适合所有需要在设备冷启动后自动恢复核心能力的场景:IoT 设备管理、企业级 MDM 客户端、离线数据同步服务、紧急告警守护模块。注意:它不解决“应用被用户手动 Force Stop 后无法自启”的问题——那是 Android 的设计原则,不是 bug。
2. 为什么必须用静态注册?动态注册为什么不行?
2.1 系统启动阶段的生命周期断层:BOOT_COMPLETED广播只发一次,且只发给已安装的静态接收器
Android 系统在完成内核初始化、Zygote 启动、SystemServer 加载后,会广播android.intent.action.BOOT_COMPLETED。这个广播有三个硬性约束:
- 时机唯一:仅在系统首次完成启动流程时发送一次,不会重发;
- 接收资格:只有在
AndroidManifest.xml中静态声明、且android:enabled="true"、android:exported="true"(Android 12+ 强制要求)的<receiver>才能收到; - 进程隔离:此时你的 App 进程大概率尚未启动(甚至从未被用户打开过),动态注册(
Context.registerReceiver())因无宿主进程而根本不存在。
提示:
BOOT_COMPLETED是隐式广播,但它是 Android 官方明确豁免的少数几个允许静态注册的隐式广播之一。其他如CONNECTIVITY_CHANGE在 7.0+ 已被禁止静态注册,必须用ConnectivityManager.NetworkCallback替代。
2.2 静态注册的 Manifest 配置:四要素缺一不可
以下配置必须完整写入app/src/main/AndroidManifest.xml的<application>标签下:
<receiver android:name=".BootCompletedReceiver" android:enabled="true" android:exported="true" android:permission="android.permission.RECEIVE_BOOT_COMPLETED"> <intent-filter android:priority="1000"> <action android:name="android.intent.action.BOOT_COMPLETED" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>android:name:指向你的接收器类,必须是完整类名(含包路径)或以.开头的相对名;android:enabled="true":确保接收器默认启用(若为false,需用PackageManager.setComponentEnabledSetting()动态开启);android:exported="true":Android 12+(API 31)强制要求,表示该组件可被其他应用(此处是系统)调用;android:permission="android.permission.RECEIVE_BOOT_COMPLETED":声明接收该广播所需的系统权限,必须在<uses-permission>中单独声明(见下节);android:priority="1000":<intent-filter>的优先级,范围 -1000 ~ 1000,值越大越先接收。虽然BOOT_COMPLETED无竞争者,但设为最高可避免未来兼容性风险;<category android:name="android.intent.category.DEFAULT" />:部分 ROM 厂商(如某国产定制系统)要求此 category 才触发,加上更稳妥。
2.3 权限声明:RECEIVE_BOOT_COMPLETED是系统级权限,需显式申请
在AndroidManifest.xml的<manifest>根节点下(与<application>同级),添加:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />- 此权限属于
normal级别,安装时自动授予,无需运行时requestPermissions(); - 但注意:若你的
targetSdkVersion >= 31(Android 12),且未在<receiver>中声明android:exported="true",即使有权限声明,也会在安装时报INSTALL_FAILED_VERIFICATION_FAILURE; - 某些厂商 ROM(如某实验室测试机)会额外要求
android.permission.SYSTEM_ALERT_WINDOW或android.permission.FOREGROUND_SERVICE,但这些与开机广播无关,属后续服务启动所需,不在本 demo 范围。
3. 接收器实现:轻量、快速、规避 ANR 的三原则
3.1 接收器类结构:继承BroadcastReceiver,只做“触发”不做“执行”
// app/src/main/java/com/example/bootdemo/BootCompletedReceiver.java public class BootCompletedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // ✅ 正确:立即启动前台服务或 JobIntentService,将耗时逻辑移交后台 if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { // 方案A:启动前台服务(推荐用于需持续运行的守护逻辑) Intent serviceIntent = new Intent(context, BootStartService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent); } else { context.startService(serviceIntent); } } } }onReceive()必须在10 秒内返回(前台广播)或60 秒内返回(后台广播),否则触发 ANR;- 严禁在此方法中执行耗时操作:如网络请求、数据库批量读写、大文件解析;
- 严禁直接
startActivity():Android 10+ 禁止后台应用启动 Activity,会抛BackgroundStartNotAllowedException; - 正确做法是“接力”:用
startService()/startForegroundService()/JobIntentService.enqueueWork()将控制权交给 Service,由 Service 在独立线程中处理业务逻辑。
3.2 后台服务承接:JobIntentService是 Android 8.0+ 的合规首选
// app/src/main/java/com/example/bootdemo/BootStartService.java public class BootStartService extends JobIntentService { private static final int JOB_ID = 1001; public static void enqueueWork(Context context, Intent work) { enqueueWork(context, BootStartService.class, JOB_ID, work); } @Override protected void onHandleWork(@NonNull Intent intent) { // ✅ 正确:所有耗时逻辑放在这里,系统保证在后台线程执行 Log.d("BootStart", "Handling boot work in background thread"); // 示例:初始化数据库、检查配置、启动心跳服务 initDatabase(); startHeartbeatService(); } private void initDatabase() { // 模拟耗时 DB 初始化 try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } private void startHeartbeatService() { // 启动长期运行的心跳服务(需在 Manifest 中声明) Intent heartbeatIntent = new Intent(this, HeartbeatService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(heartbeatIntent); } else { startService(heartbeatIntent); } } }JobIntentService是IntentService的替代品,自动适配 Android O+ 的后台执行限制:在低版本走startService(),高版本转为JobScheduler任务;onHandleWork()运行在后台线程,无 ANR 风险;enqueueWork()是启动入口,必须在onReceive()中调用,不能直接startService();JOB_ID需全局唯一,建议用常量定义,避免与其他 Job 冲突。
3.3 前台服务保活:startForegroundService()必须伴随startForeground()
// app/src/main/java/com/example/bootdemo/HeartbeatService.java public class HeartbeatService extends Service { private static final int NOTIFICATION_ID = 1002; private NotificationManager notificationManager; @Override public void onCreate() { super.onCreate(); notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); createNotificationChannel(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // ✅ 关键:必须在 5 秒内调用 startForeground() startForeground(NOTIFICATION_ID, buildForegroundNotification()); return START_STICKY; // 系统内存不足时尝试重启 } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( "boot_heartbeat", "Boot Heartbeat", NotificationManager.IMPORTANCE_LOW); channel.setDescription("Keeps heartbeat alive after boot"); notificationManager.createNotificationChannel(channel); } } private Notification buildForegroundNotification() { Intent notificationIntent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE); return new NotificationCompat.Builder(this, "boot_heartbeat") .setContentTitle("Boot Demo Running") .setContentText("Heartbeat service is active") .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentIntent(pendingIntent) .build(); } @Override public IBinder onBind(Intent intent) { return null; } }startForegroundService()后必须在 5 秒内调用startForeground(),否则抛ForegroundServiceDidNotStartInTimeException;START_STICKY表示服务被系统杀死后,若内存充足会尝试重启(非 100% 保证);- 通知渠道(
NotificationChannel)在 Android 8.0+ 是强制要求,否则通知不显示; PendingIntent.FLAG_IMMUTABLE是 Android 12+ 对PendingIntent的新要求,旧版可用FLAG_IMMUTABLE | FLAG_ONE_SHOT兼容。
4. 避坑:开机自启的 4 个血泪经验与排查清单
4.1 现象:onReceive()完全不触发,Log 里看不到任何打印
原因:
AndroidManifest.xml中<receiver>缺少android:exported="true"(Android 12+ 必须);targetSdkVersion≥ 31 但未在build.gradle中设置android:exported="true";RECEIVE_BOOT_COMPLETED权限未在<uses-permission>中声明;- 设备处于“开发者选项 → USB 调试”关闭状态,且未通过
adb shell am broadcast手动测试。
解决:
- 检查
AndroidManifest.xml,确认 receiver 和 permission 声明完整; - 运行
adb shell dumpsys package com.example.bootdemo | grep -A 20 "receivers",查看 receiver 是否被系统识别为exported=true; - 手动触发测试:
adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n com.example.bootdemo/.BootCompletedReceiver(需先安装 APK)。
4.2 现象:onReceive()触发了,但startForegroundService()报IllegalStateException
原因:
startForegroundService()调用后,onStartCommand()中未在 5 秒内调用startForeground();targetSdkVersion≥ 31 但HeartbeatService的AndroidManifest.xml声明中缺少android:exported="true"(前台服务需导出);NotificationChannel创建失败(如渠道 ID 为空或重复)。
解决:
- 将
startForeground()调用移至onCreate()或onStartCommand()开头; - 检查
HeartbeatService的 Manifest 声明:<service android:name=".HeartbeatService" android:exported="true" />; - 在
createNotificationChannel()中加Log.d,确认渠道创建成功。
4.3 现象:服务启动了,但 1 分钟后被系统杀死,onDestroy()被调用
原因:
START_STICKY不是“永生咒”,系统内存压力大时仍会杀死;- 某些厂商 ROM(如某高校测试用的深度定制系统)有额外保活策略,需在设置中手动开启“自启动管理”、“电池优化白名单”;
HeartbeatService中未使用WakeLock或JobIntentService,导致 CPU 休眠时线程被挂起。
解决:
- 对关键心跳逻辑,改用
WorkManager设置周期性任务(如 15 分钟一次),比长时服务更省电; - 在
onStartCommand()中申请PowerManager.WakeLock(需WAKE_LOCK权限),但需谨慎释放; - 引导用户进入系统设置,手动开启自启动权限(需
ACTION_APPLICATION_DETAILS_SETTINGSIntent)。
4.4 现象:真机测试正常,但模拟器(AVD)始终不触发BOOT_COMPLETED
原因:
- AVD 默认启动模式为“Quick Boot”(快照启动),系统认为是“热启动”而非“冷启动”,不发
BOOT_COMPLETED; - 某些 AVD 镜像(如 Google Play 版本)因 GMS 服务干扰,广播被拦截;
- 模拟器未完成首次 Setup Wizard 流程(即未进入桌面),系统不认为启动完成。
解决:
- 在 AVD Manager 中,点击模拟器右侧 ▼ → “Cold Boot Now”(冷启动);
- 创建 AVD 时选择 “x86_64” + “Without Google APIs” 镜像,避免 GMS 干扰;
- 启动模拟器后,手动完成初始设置(输入 Wi-Fi、跳过账户登录),确保看到桌面后再测试。
5. 验证与调试:三步闭环验证法,拒绝“玄学生效”
5.1 第一步:静态注册验证——用adb直接触发广播,绕过重启
这是最快速、最可控的验证方式,能 100% 排除“是否注册成功”的疑问:
# 1. 确认 APK 已安装且包名正确 adb shell pm list packages | grep example.bootdemo # 2. 手动发送 BOOT_COMPLETED 广播(指定 receiver 组件) adb shell am broadcast \ -a android.intent.action.BOOT_COMPLETED \ -n com.example.bootdemo/.BootCompletedReceiver # 3. 实时抓取 Log,过滤关键 Tag adb logcat -s BootCompletedReceiver BootStartService HeartbeatService- 若看到
BootCompletedReceiver: onReceive called→ 静态注册成功; - 若看到
BootStartService: Handling boot work...→ Service 接力成功; - 若看到
HeartbeatService: onStartCommand+startForeground→ 前台服务启动成功; - 此步骤失败,说明 Manifest 配置或权限有硬伤,不必重启手机。
5.2 第二步:真机冷启动验证——记录时间戳与日志断点
重启不是目的,验证“从黑屏到服务就绪”的全链路才是:
// 在 BootCompletedReceiver.onReceive() 开头加 Log.d("BootTimeline", "RECEIVER_START: " + System.currentTimeMillis()); // 在 BootStartService.onHandleWork() 开头加 Log.d("BootTimeline", "SERVICE_WORK_START: " + System.currentTimeMillis()); // 在 HeartbeatService.onStartCommand() 开头加 Log.d("BootTimeline", "HEARTBEAT_START: " + System.currentTimeMillis());- 重启手机,用
adb logcat -b all -v time | grep BootTimeline捕获全缓冲区日志; - 查看时间戳差值:
RECEIVER_START到SERVICE_WORK_START应 < 100ms(纯调度开销),SERVICE_WORK_START到HEARTBEAT_START应 < 5s(前台服务超时阈值); - 若
HEARTBEAT_START时间戳缺失,说明startForeground()未执行或失败。
5.3 第三步:厂商适配验证表——不同 ROM 的实测行为差异
| 厂商/ROM 类型 | BOOT_COMPLETED是否默认接收 | 是否需手动开启“自启动” | 前台服务存活时长(空闲状态) | 备注说明 |
|---|---|---|---|---|
| AOSP(Pixel 原生) | ✅ 是 | ❌ 否 | ≥ 30 分钟 | 最标准行为,Debug 首选 |
| 某高校定制教育 ROM | ✅ 是 | ✅ 是(设置中叫“后台弹窗”) | ≤ 5 分钟 | 需引导用户进设置开启白名单 |
| 某实验室测试机(MIUI) | ❌ 否(需关闭“省电策略”) | ✅ 是(“自启动管理”) | ≤ 1 分钟 | MIUI 12+ 默认拦截所有后台广播,需关闭“神隐模式” |
| 某国产平板(HarmonyOS) | ✅ 是(兼容 Android 广播) | ❌ 否 | ≥ 20 分钟 | HarmonyOS 3.0+ 对 Android 广播兼容性较好 |
- 关键动作:对目标机型,必须在
Settings > Apps > YourApp > Battery Saver中关闭“智能省电”; - 自动化提示:在
MainActivity中检测PackageManager.getComponentEnabledSetting(),若 receiver 被禁用,弹 Toast 引导用户去设置页; - 不要硬编码适配:用
Build.MANUFACTURER+Build.MODEL做条件判断,而非Build.BRAND(易被篡改)。
6. 进阶技巧:让开机自启“可灰度、可降级、可监控”
6.1 灰度开关:用BuildConfig控制 receiver 启用状态
在app/build.gradle中定义构建变体开关:
android { buildTypes { debug { buildConfigField "boolean", "ENABLE_BOOT_RECEIVER", "true" } release { buildConfigField "boolean", "ENABLE_BOOT_RECEIVER", "false" } } }在AndroidManifest.xml中,用tools:replace动态覆盖android:enabled:
<receiver android:name=".BootCompletedReceiver" android:enabled="${enableBootReceiver}" android:exported="true" android:permission="android.permission.RECEIVE_BOOT_COMPLETED" tools:replace="android:enabled"> <intent-filter android:priority="1000"> <action android:name="android.intent.action.BOOT_COMPLETED" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>BuildConfig.ENABLE_BOOT_RECEIVER在 Java/Kotlin 中可直接读取;- 发布前将
release变体设为false,上线后通过远程配置(如 Firebase Remote Config)动态下发true,实现灰度发布; - 避免“一刀切”上线,降低全量故障风险。
6.2 降级方案:当静态广播失效时,用AlarmManager+PendingIntent补位
// 在 BootStartService.onHandleWork() 中添加降级逻辑 private void scheduleFallbackAlarm() { AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent fallbackIntent = new Intent(this, FallbackReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast( this, 0, fallbackIntent, PendingIntent.FLAG_IMMUTABLE); // 设置 30 秒后触发(模拟“启动延迟”) long triggerAt = System.currentTimeMillis() + 30 * 1000; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent); } else { alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent); } }FallbackReceiver是另一个静态广播接收器,用于兜底;setExactAndAllowWhileIdle()可在 Doze 模式下唤醒,但需WAKE_LOCK权限;- 此方案不替代
BOOT_COMPLETED,而是作为“如果 30 秒内未收到广播,则主动触发”的保险丝。
6.3 监控埋点:记录每次启动的“健康分”,驱动运维决策
在HeartbeatService中加入启动质量打分:
private void recordBootHealth() { SharedPreferences sp = getSharedPreferences("boot_health", MODE_PRIVATE); long bootTime = System.currentTimeMillis(); int score = 100; // 扣分项 if (isBatterySaverActive()) score -= 30; // 电池省电模式 if (isAppInStopState()) score -= 50; // App 被 Force Stop if (getMemoryUsage() > 90) score -= 20; // 内存占用过高 // 记录 sp.edit() .putLong("last_boot_time", bootTime) .putInt("health_score", score) .putString("reason", getHealthReason()) .apply(); // 上报(伪代码) Analytics.reportEvent("boot_health", Map.of( "score", score, "reason", getHealthReason(), "boot_time", bootTime )); }health_score< 60 时,自动触发Toast提示用户“检测到启动异常,建议检查电池优化设置”;- 后台聚合
health_score数据,生成“各机型启动成功率热力图”,指导 ROM 适配优先级; - 这不是“炫技”,而是把过去靠人工猜的“为什么用户说不生效”,变成可量化、可归因、可行动的数据流。
我带过的三个项目里,有两个在上线后第一周就靠health_score发现了某款热门机型的省电策略误杀问题,三天内推送了针对性修复。真正的工程落地,从来不是写完代码就结束,而是让每一行代码都带着可观测性出生。希望帮到你。
本文还有配套的精品资源,点击获取