Android保活sdk

Android 后台存活率提升实战:从"被厂商杀"到推送稳定送达(Android 7--16)

telegram contact:@lily059

开篇:先别急着写代码

"我的应用在小米上被杀得妈都不认识,在华为上活不过两小时。"

这句抱怨我在评论区看过太多次。但大多数人在动手之前,其实连自己的进程是被谁杀的都不知道------于是开始堆各种"保活奇技淫巧",最后既没解决问题,还把自己送进了应用市场的审核黑名单。

这篇文章不讲玄学,只讲三件事:

  1. 用系统 API 定位真实死因,而不是猜;
  2. 系统允许你做的合规且有效的手段,以及各自的适用边界;
  3. 哪些做法是红线------技术上"能用",但会带来审核、合规和卸载率的代价。

一、先分清 5 种"死法"

进程被杀不是一件事,是五件事。搞错病因,药就不可能对。

类型 触发者 典型表现 能否用合规手段缓解
LMK(低内存杀手) 内核 内存吃紧时按 oom_adj 依次回收 可以:降低自身占用、提升 oom_adj 优先级
后台执行限制 Android 8+ 框架 后台 startService() 抛 IllegalStateException;隐式广播收不到 可以:前台服务、显式广播、WorkManager
Doze / App Standby 系统省电 息屏静置后网络、闹钟、任务被延迟 可以:setExactAndAllowWhileIdle、电池优化白名单
厂商省电策略 MIUI / EMUI / ColorOS / Funtouch... 锁屏后清后台、自启动被拦、"关联启动"被禁 部分可以:引导用户开白名单,无法代码绕过
用户强停 / 划掉卡片 用户 最近任务划掉、设置里点"强制停止" 不应该缓解------这是用户的明确意图

最后一行请记住:用户强停是用户的权利,不是 bug。 后面会专门讲为什么去对抗它是笔亏本买卖。


二、用 ApplicationExitInfo 定位死因(这是本文最有价值的一段)

Android 11(API 30)之后,系统提供了官方接口查询进程历史退出原因,别再靠"我猜是 MIUI 杀的"了。

kotlin 复制代码
@RequiresApi(Build.VERSION_CODES.R)
fun dumpExitReasons(context: Context) {
    val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager

    // 参数:packageName(传 null 表示自己)、maxNum、maxNum 上限
    am.getHistoricalProcessExitReasons(null, 0, 20).forEach { info ->
        Log.i("ExitInfo", """
            time=${info.timestamp}
            reason=${info.reason.toReadable()}
            importance=${info.importance}   // 被杀时的前台/后台状态
            desc=${info.description ?: "-"}
            rss=${info.rss}                 // 峰值内存,排查 LMK 很有用
            pss=${info.pss}
        """.trimIndent())

        // 如果是崩溃/ANR,还能拿到 trace
        info.traceInputStream?.use { /* 交给崩溃平台解析 */ }
    }
}

@RequiresApi(Build.VERSION_CODES.R)
fun Int.toReadable(): String = when (this) {
    ApplicationExitInfo.REASON_LOW_MEMORY              -> "LMK 内存不足被杀"
    ApplicationExitInfo.REASON_USER_REQUESTED          -> "用户主动结束/强停"
    ApplicationExitInfo.REASON_OTHER                   -> "系统回收(厂商策略常见)"
    ApplicationExitInfo.REASON_FREEZER                 -> "被系统冻结后回收(Android 12+)"
    ApplicationExitInfo.REASON_ANR                     -> "ANR"
    ApplicationExitInfo.REASON_CRASH                   -> "Java 崩溃"
    ApplicationExitInfo.REASON_CRASH_NATIVE            -> "Native 崩溃"
    ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> "资源占用超限被回收"
    ApplicationExitInfo.REASON_DEPENDENCY_DIED         -> "依赖进程退出连带"
    ApplicationExitInfo.REASON_INITIALIZATION_FAILURE  -> "启动初始化失败"
    else -> "其他($this)"
}

怎么读这些数据:

  • 大量 REASON_LOW_MEMORY → 你的常驻内存太大,先做内存优化,别急着加服务;
  • 大量 REASON_OTHER / REASON_FREEZER → 大概率是厂商策略,属于"引导用户开白名单"的场景;
  • REASON_USER_REQUESTED 占比高 → 停下来想想产品体验,用户在用脚投票;
  • REASON_CRASH* / REASON_ANR → 先修 bug,崩溃的进程谈何存活。

小提示:这套 API 在部分 OEM ROM 上粒度有限,REASON_OTHER 占比会偏高。配合"进程启动时间 + 前后台切换时间点"一起埋点,才能还原完整链路。


三、合规且有效的手段(按场景选型)

3.1 前台服务:首选,但类型必须选对

Android 14(API 34)起 foregroundServiceType 是强制项 ,且必须声明对应的 FOREGROUND_SERVICE_* 权限,否则直接崩溃。

xml 复制代码
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

<service
    android:name=".SyncService"
    android:exported="false"
    android:foregroundServiceType="dataSync" />
kotlin 复制代码
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    startForeground(
        NOTIFY_ID,
        buildNotification(),                               // 必须用户可见,别做成透明通知
        ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC       // Android 10+ 可显式指定
    )
    return START_STICKY
}

几个必须知道的边界:

  • 类型不是随便选的 :dataSync 用于数据同步,mediaPlayback 用于播放,location 用于导航------选错类型在市场审核时会被打回;
  • Android 15 起 ,dataSync / mediaProcessing 类型的前台服务有"24 小时内累计 6 小时"的时长上限,超时会收到 onTimeout() 回调,必须在那里收尾,别装作没看见;
  • 从后台启动前台服务有限制 (Android 12+ 收紧了 exception 列表),Android 15 起 SYSTEM_ALERT_WINDOW 也不再是豁免理由。想靠"悬浮窗权限换后台起服务"的老套路已经不通了。

3.2 推送通道:真正决定"消息能不能到"的,是它

如果你的诉求是"消息别丢",那么长连接和推送通道的权重,远高于进程存活。进程被杀后能被推送拉起,比死撑进程省电得多。

  • 海外 :老老实实用 FCM;
  • 国内 :接入厂商推送 (小米、华为 HMS、OPPO、vivo、荣耀、魅族)是硬需求------这些通道在应用被杀后仍可送达,而且不需要你保活;
  • 需要自建长连接时,做好断线重连退避、心跳与厂商通道的互补(在线走长连接,离线走推送)。

一句话:能用推送解决的,不要用保活解决。

3.3 WorkManager:可延迟任务的正确姿势

kotlin 复制代码
val request = PeriodicWorkRequestBuilder<SyncWorker>(15, TimeUnit.MINUTES)
    .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.MINUTES)
    .build()

WorkManager.getInstance(context)
    .enqueueUniquePeriodicWork("sync", ExistingPeriodicWorkPolicy.KEEP, request)
  • 周期任务最小间隔 15 分钟,别指望它做秒级轮询;
  • ExistingPeriodicWorkPolicy.KEEP 避免每次启动都重建任务;
  • WorkManager 保证会执行,但不保证准时(受 Doze / 电池策略影响),对时效敏感就别用它。

3.4 精确闹钟:注意 Android 12/13 的权限变化

kotlin 复制代码
val am = context.getSystemService(AlarmManager::class.java)
val triggerAt = System.currentTimeMillis() + 5 * 60_000

val canExact = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    am.canScheduleExactAlarms()          // Android 12+ 必须运行时判断
} else true

if (canExact) {
    am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
} else {
    // 降级为不精确闹钟,同样能穿透 Doze,只是时间有偏差
    am.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
}

权限上有两个坑:

  • SCHEDULE_EXACT_ALARM(Android 12+):用户可撤销,必须运行时判断 canScheduleExactAlarms();
  • USE_EXACT_ALARM(Android 13+):仅限闹钟、日历等以精确时间为核心功能的应用,Google Play 有明确的用途审核,别拿来当"免申请精确闹钟"的后门。

3.5 电池优化白名单:能要,但要说清为什么

kotlin 复制代码
val pm = context.getSystemService(PowerManager::class.java)
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
    // 先给用户一个"为什么"的说明页,再跳转,转化率会高很多
    startActivity(
        Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
            .setData(Uri.parse("package:$packageName"))
    )
}

注意:Google Play 对 REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 的使用场景有白名单限制(报警、VoIP 等),滥用会被下架。要在隐私政策与商店说明里写清楚用途。

3.6 厂商自启动 / 后台白名单:没有统一 API,只能好好引导

这是国内场景绕不开的一环,但要接受一个现实:没有稳定、官方支持的跳转接口。

  • 各家设置路径都不同(MIUI 自启动、华为"应用启动管理"、OPPO/vivo"后台高耗电"、三星"未监控的应用"...),写死厂商 Activity 的做法会随 ROM 版本失效;
  • 稳妥做法:先 try/catch 尝试跳转,失败就兜底到本应用详情页,让用户自己找到开关;
kotlin 复制代码
// 兜底方案:永远不会失效
startActivity(
    Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
        .setData(Uri.parse("package:$packageName"))
)
  • 更好的做法是在 App 内做图文引导(机型识别 + 截图指引),比直接甩一个系统设置页有效得多。

3.7 如果是真有硬件,用 CompanionDeviceManager

应用确实要长期和 BLE 设备(手环、车机、健康设备)保持连接时,Android 12+ 的 CompanionDeviceService 是官方为你准备的正当方案,系统会给予后台运行豁免。

前提是:你真的有配对设备。 为了保活伪造一个 BLE 关联,既过不了审核,也拿不到系统信任。

3.8 系统绑定服务:功能必须匹配

NotificationListenerService、AccessibilityService、DeviceAdminService、VpnService 都能带来极高的进程优先级,但它们都有强烈的"用途契约":

  • 通知监听 → 你要真的做通知聚合/过滤;
  • 无障碍 → 你要真的有辅助功能(否则属高危,Play 会重点审查);
  • VPN → 你要真的做流量处理,而不是建一个 127.0.0.1 的回环隧道;
  • DeviceAdmin → 你要真的是企业设备管理场景。

"为了保活而申请"是这个领域最常见的自杀行为:要么审核不过,要么被用户投诉后下架。


四、明确劝退的反模式(红线)

下面这些在技术上都"做得到",但我不建议你在任何要上架的产品里使用,理由写在后面。

反模式 为什么不要做
1 像素 / 透明 Activity 拉活 违反 Android 10+ 后台启动 Activity 限制;"莫名弹出"是用户投诉与"欺骗行为"判定的高发点
播放无声音频 抢占音频焦点、显著增加耗电;部分 ROM 直接判定异常进程
空 SyncAdapter / 空账户 只为触发同步 账户与同步必须有真实用途,属审核重点
双进程 / 空进程互拉 Android 8+ 后台服务限制下已基本失效,且明确被视为滥用
滥用 MediaStyle / CallStyle 绕过 POST_NOTIFICATIONS 这是绕过权限管控,与 Android 13+ 的通知权限设计初衷冲突,风险极高
反射隐藏 API、直调 AMS、"防强停" 用户在设置里点"强制停止"是明确意图;对抗它=更高的卸载率 + 市场审核风险
am instrument 拉活 滥用测试框架,属明确异常行为

需要特别说明"防强停":这类方案在某些灰色场景(如靠后台复活刷广告曝光)里被当作卖点,但对正经产品它是负资产------你对抗的是掏钱买你服务的用户,赢了一次强停,输掉的是留存和口碑。

合规上还有一条通用红线:Google Play 的 Device and Network Abuse 、Foreground Service 政策,以及国内各应用市场的后台行为规范,都会对上述行为做拦截或下架处理。


五、落地:指标 + 选型表

5.1 建议埋点的指标

指标 采集方式 用来判断
进程退出原因分布 ApplicationExitInfo 死因构成,方向对不对
前后台切换时间点 onTrimMemory(TRIM_MEMORY_UI_HIDDEN) / ProcessLifecycleOwner 被杀时是否在后台
冷启动次数 / 日 启动埋点去重 存活率最直观的代理指标
推送到达率 推送平台侧数据 最终业务指标,比存活率更重要
前台服务时长与超时次数 onTimeout() 回调 Android 15 时长限制是否踩线

5.2 场景 → 方案

场景 推荐组合
IM / 社交消息 厂商推送 + 前台服务(dataSync)+ WorkManager 兜底
音乐 / 播客播放 前台服务(mediaPlayback)+ MediaSession(真播放时)
导航 / 运动轨迹 前台服务(location)+ 精准闹钟
IoT / BLE 外设 CompanionDeviceService + 前台服务
企业设备管理 Device Owner + DeviceAdminService
天气 / 资讯定时刷新 WorkManager(15 分钟 +)
闹钟 / 提醒类 精确闹钟 + USE_EXACT_ALARM(符合政策前提)

六、上线前检查清单

  • 已用 ApplicationExitInfo 统计过真实死因分布,且方向与数据匹配
  • 前台服务:类型选对、FOREGROUND_SERVICE_* 权限齐全、通知用户可见
  • Android 15 时长限制场景已实现 onTimeout() 收尾
  • 已完成 FCM / 厂商推送接入,消息链路不依赖"进程活着"
  • 精确闹钟做了 canScheduleExactAlarms() 判断与降级
  • 电池优化白名单、自启动引导均有用途说明,且跳转有兜底
  • 未使用第四节表格里的任何反模式
  • 隐私政策、商店说明与申请的权限一一对应
  • 真机矩阵覆盖:小米 / 华为 / OPPO / vivo / 三星 / 原生

结语

后台存活率这件事,核心不是"堆多少种保活手段",而是"用对系统给你的那把钥匙":把消息交给推送通道,把可延迟任务交给 WorkManager,把持续工作交给带正确类型的前台服务,把系统限制的边界用引导和授权去沟通------而不是去绕过它。

你的机型上有遇到过 REASON_OTHER 特别高的死亡姿势吗?欢迎在评论区贴出你的 exitReason 分布,一起看看是谁在动手。

相关推荐
2501_916007471 小时前
苹果应用商店App Store上架费用标准及原因解析
android·ios·小程序·https·uni-app·iphone·webview
脚踏实地,坚持不懈!2 小时前
Linux 内核源码解析:从 secondary_startup_64 到 pick_eevdf 的完整调用栈分析
android·linux·arm开发
事圆则缓2 小时前
面向对象六大基本原则:从概念到 Android 实战
android
cakeism8253 小时前
金融行业 DevOps 平台推荐:2026年主流方案对比与 Gitee 选型解析
金融·gitee·devops
程序员-珍3 小时前
关于协程相关问题
android·安卓
wdfk_prog3 小时前
Wi-Fi Direct 教程 04:Interface 协议子系统初始化——WPA/EAPOL、WPS、DPP/NAN、GAS 与 P2P callback
android·运维·服务器·ubuntu·p2p·wps·wifi-direct
警醒与鞭策12 小时前
【无标题】
android·unity·性能优化·游戏引擎·perforce
传奇开心果编程14 小时前
【Jetpack Compose进阶学与练】第14课:系列收尾复习总结;Compose项目常见坑点汇总;学习路线与后续学习方向
android·学习·ui·kotlin·android jetpack
李游Leo16 小时前
《HarmonyOS 7 精准碰一碰跨设备协作开发实战》07:异常恢复、状态机与CrossDrop工程收尾【鸿蒙心迹】
android·harmonyos