
简介一份面向Android开发者的后台服务保活资料包围绕如何降低进程被系统回收风险、如何在进程被杀后重新拉活等难题整理了前台服务、绑定服务、JobScheduler/WorkManager、AlarmManager、广播拉活等多种实现思路。包内KeepLiveDemo工程包含5个Java源文件、8个XML布局与配置、5张演示图片、Gradle构建脚本及README说明共22个文件压缩包约45KB目录结构清晰便于对照关键代码进行调试和二次开发。对于需要提升推送到达率、延长任务执行时间、或在特定系统事件后自动恢复服务的应用场景可直接作为基础模板修改使用。已有5108人学习下载适合正在处理服务存活率、兼容Doze模式与App Standby限制的中级Android开发人员。通过学习这份示例可快速掌握进程优先级提升、粘性意图恢复、推送唤醒等常见保活手段并理解各方案背后的系统机制与取舍例如通过startForeground提升服务优先级、利用WorkManager设定延迟任务、监听BOOT_COMPLETED广播触发重启等都能在工程中找到对应代码实现从而在真实项目中合理平衡服务持续运行与系统资源占用。 做Android开发的朋友基本都遇到过这个场景App一切到后台过几分钟再点开发现进程没了数据要重新加载音乐停了导航断了消息也不推送了。用户第一反应就是骂App垃圾但搞过的人心里都清楚这锅不全在App身上——从Android 6.0的Doze到Android 12的“休眠应用”再到各家厂商激进的后台清理策略系统对后台的管控一年比一年狠。这篇文章就围绕“Android App如何保证后台服务不被杀死”这个核心问题把进程优先级、内存回收、Doze机制、厂商ROM策略这些底层逻辑讲清楚再给出真正能落地、合规的实操方案包括前台服务、WorkManager、跨进程守护和引导用户加白名单。适合正在做IM、音乐、导航、运动健康这类强后台需求的App开发者参考也适合刚接触后台任务、对保活方案一头雾水的新手。1. 项目背景与核心思路1.1 保活的本质不是“不死”而是“死得体面”先说一个最容易踩的误区很多人一上来就想找“怎么让进程永远不被杀”的偏方比如1像素Activity、锁屏清理、反复拉活互相守护。这类方案我不是没试过实测下来要么在Android 8.0之后被系统直接按死要么被应用商店检测到下架要么白耗电导致用户主动卸载。真正的保活思路应该是让系统认为你的进程“值得留”并且在被清理后能快速恢复而不是跟系统玩猫鼠游戏。Android后台被杀的根源在于系统在内存不足或电量紧张时要根据一套优先级规则回收进程。你的App能不能活下来取决于它处于哪一级优先级而不是你用了多少“黑科技”。所以做保活之前先要把Android的进程生杀大权理解透。1.2 方案选型合规优先引导用户比对抗系统更有效我做了几年Android之后最大的体会是在国产ROM上技术手段的边际效应非常低。你代码写得再漂亮小米的MIUI、华为的EMUI、vivo的OriginOS照杀不误因为这些系统有自己的一套后台管理策略App只能被动适配。所以我的整体思路分三层第一层用系统官方推荐的API比如前台服务、WorkManager把进程优先级抬高第二层通过合理的架构设计如多进程隔离、广播拉活、账号同步拉活把被杀的恢复时间压缩到最短第三层引导用户把你App加入电池优化白名单、自启动白名单这层虽然不算纯技术手段但实测是保活效果最明显的。这篇文章会按这三层逐层拆开讲。2. 后台服务的生死逻辑系统凭什么杀你2.1 进程优先级与LMK回收机制Android系统里的进程从系统的角度看并不是“平等”的。系统根据进程当前做的事把进程划分成五个优先级层级从高到低大致是优先级进程类型典型场景被杀概率1前台进程正在交互的Activity、正在执行onStartForeground的服务极低2可见进程被前台Activity绑定、正在显示但无焦点的界面低3服务进程已启动的Service且未转到前台中4后台进程已退到后台的Activity高5空进程无活跃组件的进程最高每一层进程都有一个oom_adj值数值越大表示优先级越低、越容易被杀。Low Memory KillerLMK就是根据这个值来决定“杀谁”的。我在开发过程中常用的一个排查命令是adb shell cat /proc/[pid]/oom_adj可以直接看到自己App当前被系统标记的adj值。如果你发现它的值一直在10以上说明系统随时可能回收你的进程这时候再谈什么保活都是空话。2.2 Doze模式与应用待机的叠加限制Android 6.0引入的Doze模式是很多老开发的噩梦。设备充电且静止不动一段时间后系统进入睡眠状态会暂停网络访问、延迟JobScheduler任务、禁止WakeLock让你的后台任务几乎全部失效。Android 7.0又加了“应用待机”机制如果用户长期不打开某个App系统会把它标记为待机状态后台任务和推送全部被限制。到了Android 8.0连Service本身都被限制了startService()在后台直接抛IllegalStateException必须改成startForegroundService()并在5秒内调用startForeground()否则直接崩。Android 9.0的App Standby Buckets把应用分成活跃、工作、频繁、罕见四类Android 12的“休眠应用”机制甚至会在后台强制停止应用。这一整套组合拳下来想靠裸Service在后台一直跑已经不现实了。2.3 厂商ROM的“第二套规则”如果说Google原生的机制还能通过官方API去适配那国产ROM就是“另一个世界”。MIUI有“省电策略”、EMUI有“应用启动管理”、ColorOS有“智能后台管理”它们不只拦截Service还会拦截广播、限制WakeLock、杀掉互相拉起的进程。我做双进程守护方案的时候在原生Android上跑得好好的放到某些国产机型上一锁屏不到十分钟两个进程全被清掉。注意厂商ROM的限制通常不在AOSP代码里而是写在厂商自己的系统框架层比如MIUI的MemInfo里会有额外的进程清理逻辑这些在公开文档里查不到只能通过实测去总结规律。3. 核心保活方案落地实操3.1 前台服务最正统、最有效的方案如果你的App有音乐播放、导航、运动记录这类需要长时间运行的功能最标准的方案就是给Service挂一个常驻通知变成前台服务。前台服务属于“前台进程”级别oom_adj值非常低基本不会被杀。Android 8.0之后要使用startForegroundService()启动然后在Service的onCreate()或onStartCommand()里5秒内调用startForeground()。class MusicService : Service() { override fun onCreate() { super.onCreate() val channelId music_playback val channel NotificationChannel( channelId, 音乐播放, NotificationManager.IMPORTANCE_LOW ).apply { description 音乐播放控制 setShowBadge(false) } getSystemService(NotificationManager::class.java).createNotificationChannel(channel) val notification NotificationCompat.Builder(this, channelId) .setContentTitle(正在播放夜曲) .setContentText(周杰伦) .setSmallIcon(R.drawable.ic_music_note) .setOngoing(true) .build() startForeground(NOTIFICATION_ID, notification) } }这段代码看着简单实际操作里有几个细节要特别注意。Android 13开始通知需要动态申请POST_NOTIFICATIONS权限如果用户拒绝授权前台服务通知会不显示但服务本身还是能启动。Android 14更是强制要求声明foregroundServiceType如果你在后台启动服务但没声明类型系统会直接抛ForegroundServiceStartNotAllowedException。manifest uses-permission android:nameandroid.permission.FOREGROUND_SERVICE / uses-permission android:nameandroid.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK / uses-permission android:nameandroid.permission.POST_NOTIFICATIONS / service android:name.MusicService android:foregroundServiceTypemediaPlayback android:exportedfalse / /manifest3.2 WorkManager延迟任务的正确打开方式很多开发想把“定时上传数据”“每天同步一次”这类需求做成保活Service这其实属于过度设计。这类不需要持续运行的任务用WorkManager就够了。WorkManager会根据系统电量、网络状态、设备待机情况自动选择执行时机而且兼容到API 14比裸写JobScheduler省心太多。val syncRequest PeriodicWorkRequestBuilderSyncWorker(15, TimeUnit.MINUTES) .setConstraints( Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() ) .build() WorkManager.getInstance(context).enqueueUniquePeriodicWork( sync_worker, ExistingPeriodicWorkPolicy.KEEP, syncRequest )这里有个容易踩的坑PeriodicWorkRequest的周期最短是15分钟这是系统限制你设10分钟它会自动调整到15分钟。而且WorkManager不保证精确按时执行它的设计哲学是“在满足条件的前提下尽快执行”不是“严格按周期执行”。所以如果你的业务真的需要精确到秒的后台行为比如IM实时消息那还得走前台服务加推送通道的方案。3.3 跨进程守护与拉活双进程的“野路子”还有效吗双进程守护是很多老Android开发记忆里的“神技”两个进程互绑A死了B拉活AB死了A拉活B实现方式一般通过AIDL绑定Service利用Binder的linkToDeath监听对方死亡。我当年也是这么干的在Android 5.0、6.0时代效果确实不错。但Android 8.0之后后台绑定服务受限这个方案在原生系统上基本废了。实测下来双进程守护如今更多是配合推送服务的“保活辅助”。比如极光推送、友盟推送就是用类似方式提高推送到达率。它能保证的是“进程被系统杀掉后在特定时机比如屏幕解锁、网络切换、开机收到广播并重新拉起”而不是“永远不被杀”。如果你要做的业务是IM类或资讯类需要保证消息秒级到达建议优先靠厂商推送通道小米推送、华为推送、OPPO推送再配合常驻前台服务而不是指望双进程守护。// 守护进程中的Binder死亡监听 private val deathRecipient object : IBinder.DeathRecipient { override fun binderDied() { // 主进程死亡尝试重新绑定或拉起 reconnect() } } override fun onServiceConnected(name: ComponentName?, binder: IBinder?) { super.onServiceConnected(name, binder) binder?.linkToDeath(deathRecipient, 0) }3.4 引导用户加白名单保活效率最高的“软方案”做保活这么久我最大的心得是技术方案做得再好不如用户一个设置。把App加进电池优化白名单、自启动白名单、后台运行白名单比任何代码都管用。系统提供了ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS这个Intent可以直接引导用户跳过电池优化。if (pm.isIgnoringBatteryOptimizations(packageName)) { // 已经加入白名单 } else { val intent Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply { data Uri.parse(package:$packageName) } startActivity(intent) }但要提醒的是这个Intent并非在所有ROM上都有效部分国产ROM会把“忽略电池优化”和“自启动管理”分开处理你得再引导用户去系统设置里手动开启。这里比较稳妥的方式是把引导流程做成应用内的“保活指南”一步步告诉用户点哪里、开哪个开关同时给出“是否已开启”的检测接口每次启动时检测一次并提醒用户。这比后台代码做什么都直观。4. 常见问题与排查技巧实录4.1 为什么我加了前台服务App还是被杀这是我在网上被问得最多的问题。很多人加了startForeground()就以为万事大吉结果跑到国产ROM上还是被杀原因大概率出在以下几个方面。现象可能原因排查方式进程被清理但通知还在通知渠道被系统识别为不重要的通知进程被判为可回收检查通知渠道重要性是否为IMPORTANCE_LOW以上锁屏几分钟后被杀被厂商省电策略清理前台服务类型不被识别检查是否声明了foregroundServiceType收到推送但进程没启动开机广播受限应用被系统标记为“待机”状态检查是否被加入系统的“特殊访问权限”白名单运行中突然崩溃可能触发了Android 8.0后台Service限制查看logcat中是否有IllegalStateException排查的时候我建议先用adb shell dumpsys activity services看一下自己的Service是不是处于started状态再用adb shell cat /proc/[pid]/oom_adj看进程优先级。如果adj值正常在0~2之间服务状态也是started但进程还是被杀那基本就是厂商ROM的“额外管理”在起作用这种情况除了引导用户加白名单谁也没办法。4.2 保活的“度”在哪里什么时候该停手最后聊一个很多人忽略的问题保活不是越强越好。我做项目的时候经常看到有人把保活方案搞得极其激进多个进程互相守护、定时拉活、后台频繁唤醒。这样做的结果往往是用户手机电量尿崩、流量耗费暴涨最后被用户手动卸载甚至被应用商店以“恶意消耗资源”的名义下架。Android系统本身对后台的限制就是“用户体验优先”的思路你的App想长期待在后台前提一定是能为用户提供长期价值。我的原则是能用前台服务解决的就别用多进程能用WorkManager解决的就别用前台服务需要实时消息的先考虑厂商推送通道能引导用户加白名单的就别在代码里搞对抗。把保活做好最终是让App和系统“和平共处”而不是“你死我活”。4.3 排查工具与自查清单这里分享一套我常用的自查流程先确认目标机型是原生Android还是国产ROM再判断App的业务场景是需要持续运行、定时运行还是实时接收消息。持续运行对应前台服务定时运行对应WorkManager实时接收消息对应推送通道加双进程守护辅助。然后把服务声明、通知渠道、权限、电池优化白名单逐个检查一遍基本能解决九成以上“后台被杀”的问题。# 查看自己App的进程状态 adb shell ps -A | grep your.package.name # 查看Service运行状态 adb shell dumpsys activity services your.package.name # 查看进程adj优先级 adb shell cat /proc/[pid]/oom_adj # 查看最近被杀进程的记录 adb shell dumpsys activity processes | grep your.package.name个人经验是做后台保活一定不要在真机上“我觉得没问题”就完事同一套代码在原生Android和国产ROM上的差异极大发布前至少要在两三台不同品牌的真机上做锁屏、后台切换、内存压力测试。只有把厂商ROM的行为摸透了才能针对性地设计保活策略否则代码写得再华丽用户一锁屏就原形毕露。在实际项目里我越来越觉得保活这件事技术只是其中一部分更重要的是对用户场景的理解和对系统的敬畏。如果你的App确实需要常驻后台那就老老实实做前台服务并给出合理的用户引导如果只是定时同步类需求WorkManager已经足够了。别总想着对抗系统顺着系统的规则来反而能活得最久。本文还有配套的精品资源点击获取