Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
先看ComponentCallbacks2源代码中如何描述这些值的意义:
java
package android.content
import android.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
扩展的ComponentCallbacks接口,增加了一个新的回调方法以实现更细粒度的内存管理。此接口可用于所有应用组件(包括android.app.Activity、 android.app.Service、 ContentProvider和 android.app.Application)。
你应实现 onTrimMemory方法,根据当前系统限制逐步释放内存。使用此回调方法释放资源有助于提高系统的整体响应速度,同时也有助于提升用户体验,因为系统可以保持你的进程存活更长时间。即,
如果你不根据此回调方法定义的内存级别来修剪资源,当你的进程在最近最少使用(LRU)列表中缓存时,系统更可能会终止你的进程,从而导致用户再次返回应用时需要重新启动和恢复所有状态。
onTrimMemory方法提供的值并不代表内存限制的单一线性进程,而是为你提供有关内存可用性的不同类型线索:
(A)当你的应用正在运行时:
1、TRIM_MEMORY_RUNNING_MODERATE
设备内存开始不足。你的应用正在运行且不会被终止。
2、TRIM_MEMORY_RUNNING_LOW
设备内存严重不足。你的应用正在运行且不会被终止,但请释放未使用的资源以提高系统性能(这将直接影响你应用的性能)。
3、TRIM_MEMORY_RUNNING_CRITICAL
设备内存极度不足。你的应用进程尚不会被视为可终止进程,但如果应用不释放资源,系统将开始终止后台进程,因此你现在应该释放非关键资源,以防止性能下降。
(B)当你的应用界面不可见时:
1、TRIM_MEMORY_UI_HIDDEN
你的应用界面不再可见,因此现在是释放仅由界面使用的大型资源的好时机。
(C)当你的应用进程位于后台LRU列表中时:
1、TRIM_MEMORY_BACKGROUND
系统内存不足,且你的进程接近LRU列表的开头。虽然你的应用进程被终止的风险不高,但系统可能已经开始终止LRU列表中的进程,因此你应释放容易恢复的资源,以便你的进程保留在列表中,并在用户返回你的应用时快速恢复。
2、TRIM_MEMORY_MODERATE
系统内存不足,且你的进程位于LRU列表的中间位置。如果系统内存进一步受限,你的进程有可能会被终止。
3、TRIM_MEMORY_COMPLETE
系统内存不足,且如果你的进程现在不被回收内存,那么当系统需要回收内存时,你的进程将是首批被终止的进程之一。你应释放所有非关键资源,以便恢复应用状态。
注意:为了支持低于API级别14的设备,你可以使用onLowMemory方法作为大致相当于 ComponentCallbacks2 TRIM_MEMORY_COMPLETE 级别的回退方法。
当系统开始终止LRU列表中的进程时,虽然它主要自下而上进行工作,但也会考虑哪些进程消耗的内存更多,因此终止这些进程可以获得更多的内存。因此,当你的进程在LRU列表中时,整体消耗的内存越少,保留在列表中的机会就越大,也就能在用户返回时更快恢复。
有关进程生命周期不同阶段(例如,进程被放入后台LRU列表的含义)的更多信息,请参阅进程和线程文档。
*/
public interface ComponentCallbacks2 extends ComponentCallbacks {
/** @hide */
@IntDef(prefix = { "TRIM_MEMORY_" }, value = {
TRIM_MEMORY_COMPLETE,
TRIM_MEMORY_MODERATE,
TRIM_MEMORY_BACKGROUND,
TRIM_MEMORY_UI_HIDDEN,
TRIM_MEMORY_RUNNING_CRITICAL,
TRIM_MEMORY_RUNNING_LOW,
TRIM_MEMORY_RUNNING_MODERATE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface TrimMemoryLevel {}
/**
onTrimMemory(int)的级别:进程已接近后台LRU列表的末尾,如果很快找不到更多内存,它将被终止。
@deprecated 从API级别34开始,应用将不会收到此级别的通知。
*/
@Deprecated
static final int TRIM_MEMORY_COMPLETE = 80;
/**
onTrimMemory(int)的级别:进程在后台LRU列表的中间位置;释放内存可以帮助系统稍后保持列表中其他进程的运行,从而提高整体性能。
@deprecated 从API级别34开始,应用将不会收到此级别的通知。
*/
@Deprecated
static final int TRIM_MEMORY_MODERATE = 60;
/**
onTrimMemory(int)的级别:进程已进入LRU列表。这是清理资源的好机会,这些资源可以在用户返回应用时高效且快速地重新构建。
*/
static final int TRIM_MEMORY_BACKGROUND = 40;
/**
onTrimMemory(int)的级别:进程之前一直在显示用户界面,但现在不再显示。此时应释放与界面相关的大型分配,以便更好地管理内存。
*/
static final int TRIM_MEMORY_UI_HIDDEN = 20;
/**
onTrimMemory(int)的级别:进程不是可有可无的后台进程,但设备运行内存极度不足,很快将无法保持任何后台进程运行。你的运行进程应释放尽可能多的非关键资源,以便在其他地方使用这些内存。接下来会发生的事情是调用onLowMemory(),报告后台无法保留任何内容,这种情况可能会开始明显影响用户。
@deprecated 从API级别34开始,应用将不会收到此级别的通知。
*/
@Deprecated
static final int TRIM_MEMORY_RUNNING_CRITICAL = 15;
/**
onTrimMemory(int)的级别:进程不是可有可无的后台进程,但设备运行内存不足。你的运行进程应释放不再需要的资源,以便在其他地方使用这些内存。
@deprecated 从API级别34开始,应用将不会收到此级别的通知。
*/
@Deprecated
static final int TRIM_MEMORY_RUNNING_LOW = 10;
/**
onTrimMemory(int)的级别:进程不是可有可无的后台进程,但设备运行内存略显不足。你的运行进程可能希望释放一些不再需要的资源以供其他进程使用。
@deprecated 从API级别34开始,应用将不会收到此级别的通知。
*/
@Deprecated
static final int TRIM_MEMORY_RUNNING_MODERATE = 5;
/**
当操作系统确定进程可以修剪其进程中不需要的内存时调用。
你永远不应与级别的确切值进行比较,因为可能会添加新的中间值------你通常希望比较该值是否大于或等于你感兴趣的级别。
要在任何时间点检索进程的当前修剪级别,你可以使用 android.app.ActivityManager#getMyMemoryState
ActivityManager.getMyMemoryState(RunningAppProcessInfo)}。
@param level 修剪的上下文,提示应用程序可能希望执行的修剪量。
*/
void onTrimMemory(@TrimMemoryLevel int level);
}
特别需要关注的有两部分:
(1)其中不少level值已经在Android新版,尤其是Android API 34后失效。在Android API 34后,目前只有两个level值:
40TRIM_MEMORY_BACKGROUND
20TRIM_MEMORY_UI_HIDDEN
有效,其他的level值Android已经废弃不用,关于40和20:
java
/**
* Level for {@link #onTrimMemory(int)}: the process has gone on to the
* LRU list. This is a good opportunity to clean up resources that can
* efficiently and quickly be re-built if the user returns to the app.
*/
static final int TRIM_MEMORY_BACKGROUND = 40;
/**
* Level for {@link #onTrimMemory(int)}: the process had been showing
* a user interface, and is no longer doing so. Large allocations with
* the UI should be released at this point to allow memory to be better
* managed.
*/
static final int TRIM_MEMORY_UI_HIDDEN = 20;
(2)不要寄希望于判断 level 值与某个内存水位线境界值精准匹配,而是要判断level值是否大于等于某个警戒水位线值。Android源代码中是这么说的:
You should never compare to exact values of the level, since new intermediate values may be added -- you will typically want to compare if the value is greater or equal to a level you are interested in.
你永远不应与级别的确切值进行比较,因为可能会添加新的中间值------你通常希望比较该值是否大于或等于你感兴趣的级别。
比如,经典开源框架Glide在trimMemory(int level)时,依赖关键的40( level ≥ TRIM_MEMORY_BACKGROUND)和20( level ≥ TRIM_MEMORY_UI_HIDDEN)做内存裁剪:
java
@Override
public void trimMemory(int level) {
if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
// Entering list of cached background apps
// Evict our entire bitmap cache
clearMemory();
} else if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
|| level == android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
// The app's UI is no longer visible, or app is in the foreground but system is running
// critically low on memory
// Evict oldest half of our bitmap cache
trimToSize(getMaxSize() / 2);
}
}