当前位置: 首页 > news >正文

Android普通应用切到后台后,多长时间会被系统回收

Android 普通应用切换到后台后,并没有一个固定的回收时间。其回收完全取决于系统当前的内存压力和应用的优先级。回收时间不是一个定时器,而是由系统根据当前可用内存的紧张程度和应用进程的重要性(优先级) 动态决定的。

系统内存充足时,你的应用可能在后台存活很久;而内存紧张时,高优先级的应用会更晚被回收。

影响回收时间的因素非常多,主要包括:

  1. 设备物理内存(RAM)大小:内存越小的设备,系统越激进,后台应用存活时间越短。
  2. 当前系统内存压力:用户同时运行的应用越多,系统内存越紧张,回收越快。
  3. 应用进程的优先级(最重要因素):
  4. 前台进程 (Foreground Process):用户正在交互的App(如正在使用的Activity、前台Service)。几乎不会被回收,除非内存极端不足。
  5. 可见进程 (Visible Process):不在前台但仍对用户可见(如一个Activity上弹出一个非全屏的对话框)。优先级很高,不易被回收。
  6. 服务进程 (Service Process):托管了已启动服务的应用(如正在播放音乐的后台服务)。优先级中等,内存紧张时会被回收。
  7. 后台进程 (Cached/Background Process):完全进入后台,没有任何活跃组件(Activity完全不可见,Service已停止)。这是“普通应用”切后台后最常见的状态,优先级最低,最先被回收。
  8. 空进程 (Empty Process):不包含任何活跃组件的缓存进程,保留它只是为了下次启动更快。最先被回收。

厂商定制(非常重要):不同手机厂商(如华为、小米、OPPO、vivo)都有自己的省电策略和后台管理机制。它们可能会比原生Android系统更激进地杀死后台进程以节省电量,这会导致你的应用在后台存活时间大大缩短,甚至几分钟内就被“清理”。

总结一下时间范围:一个纯粹的后台进程(没有任何服务等活跃组件),在内存充足的旗舰机上可能存活几小时甚至更久,但在内存紧张的中低端机或受到厂商省电策略影响时,可能几分钟到半小时内就会被回收。

回收机制与负责模块

是系统的哪个模块负责回收?

Android 的进程回收主要涉及两个层次:

Application Framework 层:负责管理应用组件的生命周期(如 Activity、Service),并在适当时机通知内核层某个进程可以被回收。

Linux 内核层:这是实际执行进程回收(杀死) 的“执法部门”,其核心是 Low Memory Killer (LMK) 驱动。LMK 基于 Linux 内核的 OOM Killer 机制,但针对移动设备的特点进行了优化。

主要负责回收应用进程的模块是 Linux内核中的 Low Memory Killer (LMK) 驱动。但整个决策流程是由 用户空间的系统服务 ActivityManagerService (AMS) 和 LMK 协同工作完成的。

ActivityManagerService (AMS):

归属:frameworks/base/services/core/java/com/android/server/am/

职责:它是Android系统管理的“大脑”。它负责管理四大组件(Activity, Service, BroadcastReceiver, ContentProvider)的生命周期,并维护所有应用进程的优先级列表(OOM_ADJ)

它决定“该杀谁”。AMS会根据应用组件的状态(如是否有Activity可见、是否有Service在运行)动态计算每个进程的 oom_adj_score(Out Of Memory Adjustment值)。这个分数值越高,进程优先级越低,越容易被杀。

Low Memory Killer (LMK) 驱动:

归属:Linux内核的一部分(通常是 drivers/staging/android/lowmemorykiller.c 或类似路径)。

职责:它监听内核的内存压力事件。当系统可用内存降到某个阈值时,它被触发。

它负责“动手”。LMK会检查AMS预先设置好的oom_adj分数阈值,然后找到分数高于阈值且分数最高的进程(即优先级最低的进程),将其杀死,释放其占用的内存。

关系可以简单理解为:

AMS 是 法官,它根据法律(优先级规则)给每个犯人(进程)判刑(贴上oom_adj标签)。

LMK 是 刽子手,它会在资源不足(饥荒)时,根据法官设定的处决标准(阈值),从刑期最重(oom_adj分数最高)的犯人开始处决。

回收流程

回收流程可以概括为:分层评估 + 按需回收。下图描绘了一个应用从进入后台到可能被回收的历程,以及系统中各模块的分工:

这个过程的核心在于 oom_adj 值(Android 10 及之后为 oom_score_adj)和内存阈值。

AMS 的角色:

AMS 根据进程中运行组件的状态(如是否有前台 Activity、可见 Activity、运行中的 Service 等)动态计算每个进程的 oom_adj 值。oom_adj 值越高,进程优先级越低,越容易被杀死。下表列出了常见的进程优先级和大致 adj 值(注意:不同厂商和系统版本可能调整):

进程类型 (Importance Level) 大致 oom_adj 描述

前台进程 (Foreground) 0 用户正在交互的进程(如正在使用的App)。

可见进程 (Visible) 100 用户可见但未在前台交互(如弹出对话框后的原Activity)。

服务进程 (Service) 500 进程中有一个已启动且正在运行的 Service(如正在播放音乐)。

后台进程 (Background) 600-700 进程完全进入后台(如按Home键返回桌面),用户不可见。

空进程 (Empty) 900+ 进程中不再包含任何活跃的组件,仅作为缓存保留以加快下次启动速度。

LMK 的角色:

LMK 驱动预置了内存阈值(/sys/module/lowmemorykiller/parameters/minfree)和对应的 adj 阈值(/sys/module/lowmemorykiller/parameters/adj)。当系统可用内存低于某个 minfree 阈值时,LMK 就会从 adj 阈值列表中找到对应的 adj 值,并杀死 adj 值大于或等于该阈值的优先级最低的进程,直到可用内存恢复到令人满意的水平。

流程步骤详解:

状态变更与优先级计算:

用户点击Home键或切换到其他应用,当前应用的Activity完全不可见。

AMS检测到这一变化,将该应用进程的优先级从foreground 降级为 cached 或 background。

AMS根据新的状态,重新计算该进程的 oom_adj_score(例如,从一个较低的值提升到一个较高的值,如900),这个分数表示它被杀死的“容易程度”。

分数同步:

AMS通过写入Linux proc文件系统,将这个 oom_adj 分数设置给内核中的对应进程。具体路径是 /proc/[pid]/oom_score_adj。

内存监控与触发:

系统持续运行,内存被逐渐占用(例如用户打开了新的应用)。

当系统可用内存(free memory) 下降到LMK预设的某个阈值(例如18432KB)时,LMK驱动被触发。

选择并杀死进程:

LMK查看预设的oom_adj阈值数组(例如:[0, 100, 200, 500, 900])和对应的内存阈值([8192, 12288, 16384, 18432, 22528])。

假设当前可用内存降到了18000KB,这个值低于18432KB这个阈值点。LMK就会去找oom_adj分数大于等于该阈值点对应的分数(500)的进程。

它会从所有满足条件的进程中,找到oom_adj分数最高的那个进程(也就是最不重要、最适合被杀死的进程)。

LMK向该目标进程发送一个 SIGKILL 信号,强制终止它。

清理与通知:

进程被杀死后,Linux内核会回收该进程占用的所有内存资源。

AMS会通过Binder机制检测到进程死亡,然后清理掉该进程在系统服务中注册的所有信息(例如注销它的BroadcastReceiver,停止它的Service等),避免系统尝试与一个不存在的进程通信。

给开发者的建议

既然回收不可避免,作为开发者,我们应该让应用优雅地应对回收:

重要状态及时保存在 onPause() 或 onSaveInstanceState(Bundle) 中保存用户的输入、界面状态等关键数据。这样即使进程被杀死,用户返回时也能恢复现场。

谨慎使用后台 Service:长时间运行的后台 Service 会提高进程的优先级,但也增加了功耗。优先考虑使用 WorkManager 或 JobScheduler 来调度后台任务。

响应内存事件:在 Application 类或 Activity 中重写 onTrimMemory(int level) 方法。当系统内存不足时,它会回调此方法(尤其是 LEVEL_BACKGROUND 级别)。此时应主动释放非必要的资源(如缓存图片、临时数据等),从而降低自身进程被杀的几率。

避免“杀进程”保活:手动调用 System.exit() 或 Process.killProcess() 通常是不必要的,且可能干扰系统的内存管理策略,导致更差体验。

不要依赖后台进程长期存活:设计应用时,要假设进程随时可能被杀死。使用 onSaveInstanceState() 和 ViewModel 来保存界面状态,使用持久化存储(如数据库、SharedPreferences)来保存重要数据。

使用适合的后台任务机制:对于需要在后台执行的任务,根据其特性选择正确的工具:

立即执行、用户可感知的:使用前台服务 (Foreground Service),它会显著降低进程的oom_adj分数,极大提高存活率。

可延迟、不需要用户感知的:使用 WorkManager,它会在合适的时机(例如设备充电、空闲、有网络时)执行任务,并能保证任务最终会完成。

处理好进程重建:应用从后台被杀死后,用户再切回来,Activity会被重建。要保证用户体验无缝,正确恢复之前的状态。

厂商策略与生态

需要注意的是,不同品牌的 Android 设备由于其定制系统和省电策略的存在,后台进程的回收机制和宽容度可能会有显著差异。一些厂商为了追求更长的续航或更简洁的后台管理,可能会更积极地回收后台进程。这意味着你的应用在一台手机上可能能在后台存活数小时,而在另一台手机上可能几分钟就被回收了。

Android 中 `onTrimMemory(int level)` 方法最佳实践

概述

onTrimMemory() 是 Android 系统在内存不足时向应用发送的回调,旨在通知应用释放不必要的内存资源,从而帮助系统维持整体稳定性和多任务处理能力。这是一个协作机制,积极响应该回调可以显著降低你的应用被系统强制终止的概率,从而提升用户体验。

它与 onLowMemory()(相当于 TRIM_MEMORY_COMPLETE)类似,但提供了更细粒度的上下文信息,让你能做出更智能的释放决策。

Level 取值及含义

这些 level 常量主要是在 API Level 14 (Android 4.0/ICS) 及以上引入的。它们可以分为两大类:与应用可见性相关 和 与应用进程状态相关

第一类:与应用可见性相关(Your App‘s UI is Visible)

这些回调发生在你的应用正在运行且对用户可见时。

TRIM_MEMORY_UI_HIDDEN

含义:你的应用用户界面(如 Activity)刚刚被隐藏(例如用户按了Home键或返回键退出了应用,但应用进程还保留在后台)。

时机:这是最温和的提示。此时应用已进入后台。

最佳实践:释放仅由UI使用的大量资源。例如:

清除仅为界面显示而缓存的图片、视图等。

注意:不要释放所有资源,因为用户可能很快会返回你的应用。你需要保留一些状态以确保快速恢复。

第二类:与应用进程状态相关(Your App‘s Process is in Background)

这些回调发生在你的应用进程已经处于后台时。系统会根据进程的“LRU列表”(最近使用列表)位置来发送不同级别的提示。LRU列表越靠前,被杀死的风险越低。

风险从低到高:

TRIM_MEMORY_RUNNING_MODERATE

含义:你的应用正在运行(不是后台,而是作为前台进程或服务运行),但系统认为设备开始出现内存压力。你的进程目前没有被杀死的风险。

最佳实践:开始评估和释放一些可有可无的资源,为可能更糟的情况做准备。

TRIM_MEMORY_RUNNING_LOW

含义:你的应用正在运行,且设备内存已经很低。如果系统无法回收更多内存,可能会开始杀死后台进程。

最佳实践:强烈建议立即释放不必要的资源(例如缓存的对象、图片等),以避免系统杀死你的进程。

TRIM_MEMORY_RUNNING_CRITICAL

含义:你的应用仍在运行,但系统内存已极度紧张,并且系统已经杀死了大部分后台进程。你的进程是下一个被杀死的高风险目标。

最佳实践:尽可能多地释放非关键资源。系统正在努力保持前台进程(包括你的)存活,你需要全力配合。如果此时不释放内存,你的应用很可能很快会被杀死。

你的应用进程已经在后台(Background Process):

TRIM_MEMORY_BACKGROUND

含义:系统正在运行大量后台进程,内存开始紧张。你的进程位于LRU列表的开头(即最近刚被用到,被杀风险较低)。

最佳实践:可以考虑释放一些容易重建的资源,为系统腾出一点内存。此时风险还不算高。

TRIM_MEMORY_MODERATE

含义:系统内存不足,你的进程位于LRU列表的中间位置。如果系统需要更多内存,你的进程有可能被杀死。

最佳实践:释放更多的资源。例如,可以清理掉一部分内存缓存。

TRIM_MEMORY_COMPLETE

含义:系统内存极度紧张,你的进程是最先被杀死的候选之一(位于或接近LRU列表的末尾)。如果系统无法回收到足够内存,你的进程将很快被杀死。

最佳实践:尽一切努力释放所有非必需资源。此时保命要紧,如果不释放,进程肯定会被杀死。这与 onLowMemory() 回调的严重程度类似。

最佳实践总结

处理 onTrimMemory() 的核心思想是:根据严重程度,分层、逐步地释放资源。

使用统一的资源管理类:

建议使用一个单例或依赖注入来管理你的内存缓存(如图片缓存、网络响应缓存、对象池等)。这样你可以在 onTrimMemory() 中简单地调用这个管理类的方法,而不需要将逻辑散落在各个组件中。

分层释放策略:

TRIM_MEMORY_UI_HIDDEN / TRIM_MEMORY_BACKGROUND:

释放仅用于UI的昂贵资源(如位图 Bitmap)。

清理视图层级(View Hierarchy)的缓存。

TRIM_MEMORY_MODERATE / TRIM_MEMORY_RUNNING_MODERATE / TRIM_MEMORY_RUNNING_LOW:

清理或缩小你的内存缓存(例如,将 LruCache 的大小减半,或直接 evictAll())。

释放非核心功能模块占用的资源。

TRIM_MEMORY_RUNNING_CRITICAL / TRIM_MEMORY_COMPLETE:

清空所有非必需的内存缓存(LruCache.evictAll())。

关闭数据库连接等昂贵资源(如果可以,但要注意后续重建的开销)。

释放一切可以释放的东西,只保留维持应用核心状态所必需的最小资源(例如用户登录状态),以便在进程存活时能够快速恢复。

不要过度释放:

避免释放那些难以重建或对应用恢复至关重要的资源。例如,不要轻易释放用户当前正在编辑的表单数据。权衡释放与重建的开销。

在何处监听:

可以在 Application 类、Activity 类、Service 类或 Fragment 中重写 onTrimMemory() 方法。通常,在 Application 类中统一处理是最佳选择,因为它可以协调所有组件的资源释放。

public class MyApplication extends Application {

    @Override

    public void onTrimMemory(int level) {

        super.onTrimMemory(level);

        // 将级别传递给统一的资源管理器

        MemoryManager.getInstance().onTrimMemory(level);

    }

}

public class MemoryManager {

    private static MemoryManager sInstance;

    private LruCache<String, Bitmap> mMemoryCache;

    // ... 初始化缓存等其他代码 ...

    public void onTrimMemory(int level) {

        if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {

            // 中度或更高压力:清空整个缓存

            mMemoryCache.evictAll();

        } else if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {

            // 仅在后台,压力不大:将缓存大小缩减到当前大小的一半

            mMemoryCache.trimToSize(mMemoryCache.size() / 2);

        }

        // 对于 TRIM_MEMORY_UI_HIDDEN,我们可能选择不立即操作,

        // 或者只释放一些特定的UI相关资源。

    }

}

http://www.dtcms.com/a/393682.html

相关文章:

  • 【Elasticsearch面试精讲 Day 21】地理位置搜索与空间查询
  • 【Android】View 的滑动
  • 【深度学习的优化理论】如何理解OT与欧几里得距离均值的区别
  • 【Android】Room数据库的基本使用
  • 项目:仿muduo库的高并发服务器
  • Oracle普通用户报错ORA-31603处理
  • 网络安全期末大论文
  • 23种设计模式之【工厂方法模式】-核心原理与 Java实践
  • cocos 添加背景,帧动画,贴图
  • 亚马逊云科技重磅推出 Amazon S3 Vectors:首款大规模支持原生向量的云存储服务
  • SQLite Expert:一款功能强大的SQLite管理工具
  • Python 2025:供应链安全威胁与防御实战
  • 队列+宽搜(BFS)-429.N叉树的层序遍历-力扣(LeetCode)
  • 【Linux命令从入门到精通系列指南】rm 命令详解:安全删除文件与目录的终极实战手册
  • Springboot使用dockerfile-maven-plugin部署镜像
  • 安卓蓝牙键盘和鼠标6.10.4去更新汉化版 手机变为蓝牙键盘和鼠标
  • 工作笔记-----lwip的内存管理策略解析
  • 量子计算学习笔记(1)
  • Python爬虫基础与应用
  • Rabbitmq 集群初始化,配置导入
  • 云计算与虚拟化技术详解
  • elasticsearch 的配制
  • React学习教程,从入门到精通,React Hook 详解 —— 语法知识点、使用方法与案例代码(26)
  • ELK日志分析性能瓶颈问题排查与解决实践指南
  • 【Unity】【Photon】Fusion2中的匹配API 学习笔记
  • (3-1) Html
  • 《人机协同的边界与价值:开放世界游戏系统重构中的AI工具实战指南》
  • 数据库造神计划第十九天---事务(2)
  • Python到剪映草稿生成及导出工具,构建全自动化视频剪辑/混剪流水线
  • WordPress给指定分类文章添加一个自动化高亮(一键复制)功能