我最近在 Managing Your App's Memory 上阅读了这篇文章,我强烈建议您阅读它,如果您是AndroidDev并且从未这样做过。
有许多好的做法,我从未碰巧知道的一件事是系统在每个Activity / Fragment上调用的onTrimMemory(int level)方法,用于通知应该或可以释放哪些内存的事件。
以下是该文章的引用:
请注意,您的应用会收到onTrimMemory()回调 TRIM_MEMORY_UI_HIDDEN仅适用于您应用的所有UI组件 进程对用户隐藏。这不同于 onStop()回调,在Activity实例变为时调用 隐藏,即使用户移动到另一个活动时也会发生 你的应用。所以虽然你应该实现onStop()来释放 活动资源,例如网络连接或取消注册 广播接收器通常不应该释放您的UI资源 直到您收到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)。这确保了 如果用户从您应用中的其他活动导航回来, 您的UI资源仍可用于快速恢复活动。
我真的很有兴趣在我的应用程序中实现良好的内存管理,所以我期待以正确的方式实现onTrimMemory()。
我只有几个问题:
在onStop()之后调用onTrimMemory(TRIM_MEMORY_UI_HIDDEN)吗?
“释放您的UI资源”意味着什么?只是为了清理Bitmap缓存,或者实际删除并销毁View树中的每个View?我通常会破坏 onDestroy()或 onDestroyView()方法中的视图,我现在想知道我是否正确行事。
是否有{/ 3}}的双人/通讯回叫?例如 onCreate-onDestroy , onStart-onStop , onCreateView-onDestroyView 。我要求了解在调用onTrimMemory(TRIM_MEMORY_UI_HIDDEN)后调用活动/片段之后我应该在何处以及如何恢复UI状态。
答案 0 :(得分:29)
onTrimMemory实际上是在onStop之前调用的。当onStop被调用时,它意味着活动真的停止了,如果需要,Android操作系统可能会立即将其杀掉,所以除了onRestart和有时onDestroy之外,你不应该期待在此之后再调用该活动的回调。 / p>
“释放您的UI资源”实际上是关于缓存之类的东西。您通常不必担心管理视图或UI组件,因为操作系统已经这样做了,这就是为什么存在创建,启动,暂停,停止和销毁活动的所有回调的原因。但是,有时为了提高性能,您必须增加内存使用量,例如缓存活动使用的一些数据。这是调用onTrimMemory时应该释放的资源类型,因此您的应用程序使用的内存较少,即使它会影响性能。你应该担心内存泄漏。如果您的活动停止,请确保不要保留对其视图的任何引用,因为这样可以防止活动被垃圾收集,从而无法收集整个上下文,这很糟糕,主要是因为您希望保持应用程序正常运行几小时或几天(比如实施服务时)。
不,onTrimMemory没有相应的回调。但是,你不应该需要一个。正如我之前所说,如果你保留一些资源的缓存来提高性能,那就把它清空,如果需要,让它再次增长。如果内存级别保持低,则可以很快再次调用onTrimMemory,具有相同的内存级别。顺便说一下,请记住,将使用几个不同的内存级别调用onTrimMemory,而不仅仅是TRIM_MEMORY_UI_HIDDEN。
答案 1 :(得分:15)
示例实施
public class AppContext extends Application {
//This my introduce OutOfMemoryException if you don't handle register and removal quiet well, better to replace it with weak reference
private static List<IMemoryInfo> memInfoList = new ArrayList<AppContext.IMemoryInfo>();
public static abstract interface IMemoryInfo {
public void goodTimeToReleaseMemory();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
//don't compare with == as intermediate stages also can be reported, always better to check >= or <=
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
try {
// Activity at the front will get earliest than activity at the
// back
for (int i = memInfoList.size() - 1; i >= 0; i--) {
try {
memInfoList.get(i).goodTimeToReleaseMemory();
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
*
* @param implementor
* interested listening in memory events
*/
public static void registerMemoryListener(IMemoryInfo implementor) {
memInfoList.add(implementor);
}
public static void unregisterMemoryListener(IMemoryInfo implementor) {
memInfoList.remove(implementor);
}
}
public class ActivityParent extends Activity implements AppContext.IMemoryInfo {
protected ActivityParent child;
@Override
protected void onStop() {
super.onStop();
try {
if (child != null)
AppContext.unregisterMemoryListener(child);
} catch (Exception e) {
}
}
}
public class ActivityChild extends ActivityParent {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
child = this;
}
/---move following onResume() in parent as following eg:
/*
*@Override
* protected void onResume() {
* super.onResume();
* if(null != child){
* AppContext.registerMemoryListener(this);
* }
* }
*/
@Override
protected void onResume() {
super.onResume();
AppContext.registerMemoryListener(this);
}
@Override
public void goodTimeToReleaseMemory() {
super.goodTimeToReleaseMemory();
//remove your Cache etc here
}
//--NO Need because parent implementation will be called first, just for the sake of clarity
@Override
protected void onStop() {
super.onStop();
try {
if (null != child)
AppContext.unregisterMemoryListener(child);
} catch (Exception e) {
}
}
更多信息:
当您的应用运行时: TRIM_MEMORY_RUNNING_MODERATE 设备开始内存不足。您的应用正在运行且无法播放。
TRIM_MEMORY_RUNNING_LOW 设备的内存运行速度要低得多。您的应用正在运行且无法播放,但请释放未使用的资源以提高系统性能(这会直接影响应用的性能)。
TRIM_MEMORY_RUNNING_CRITICAL 设备内存运行极低。您的应用程序尚未被视为可填充进程,但如果应用程序未释放资源,系统将开始终止后台进程,因此您应立即释放非关键资源以防止性能下降。
当您的应用可见性发生变化时: TRIM_MEMORY_UI_HIDDEN 您的应用的用户界面不再可见,因此现在是发布仅由您的用户界面使用的大量资源的好时机。
当您的应用进程位于后台LRU列表中时:
TRIM_MEMORY_BACKGROUND
系统内存不足,您的进程接近LRU
列表的开头。虽然您的应用程序进程没有被杀死的高风险,但系统可能已经在LRU
列表中查杀进程,因此您应该释放易于恢复的资源,这样您的进程将保留在列表中并恢复当用户返回您的应用时,请快速。
TRIM_MEMORY_MODERATE 系统内存不足,您的进程接近LRU列表的中间位置。如果系统进一步受限于内存,那么您的进程可能会被杀死。
TRIM_MEMORY_COMPLETE
系统内存不足,如果系统现在没有恢复内存,那么您的进程是最先杀死的进程之一。你应该绝对释放对恢复你的app状态不重要的一切。
要支持低于14的API级别,您可以使用onLowMemory()
方法作为大致相当于TRIM_MEMORY_COMPLETE
级别的后备。
http://developer.android.com/reference/android/content/ComponentCallbacks2.html
答案 2 :(得分:1)
我正在强制关闭显示器时从未调用onTrimMemory()的问题。 因此,我尝试使用ActivityLifecycleCallbacks解决方法: 我用了一个简单的计数器:
onActivityStarted(){
i++;
}
onActivityStopped(){
i--;
if(i==0) // no more open activities, thus screen probably turned off
}
它对我有用,但我不确定它是否安全。 RFC
更新:启动相机意图,应用程序也关闭,因此它的行为并不完全正常。
使用此代码。工作得很好:
private void registerBroadcastReceiver() {
final IntentFilter theFilter = new IntentFilter();
theFilter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver screenOnOffReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String strAction = intent.getAction();
if (strAction.equals(Intent.ACTION_SCREEN_OFF)) {
// do sth
}
}
};
getApplicationContext()
.registerReceiver(screenOnOffReceiver, theFilter);
}