低内存优化

低内存优化

四月 27, 2018

概念介绍

在LRU整个列表中,你消耗越少的内存,留在列表里的机会就更大

OnLowMemory:所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,在系统内存还不足时,系统会调用OnLowMemory

OnTrimMemory:Android 4.0之后提供的API,系统会根据不同的内存状态来回调

在内存紧张的时候,会回调OnLowMemory/OnTrimMemory,需要在回调方法中编写释放资源的代码。
可以在资源紧张的时候,释放UI 使用的资源资:Bitmap、数组、控件资源

OnLowMemory

  • Application.onLowMemory()
  • Activity.OnLowMemory()
  • Fragement.OnLowMemory()
  • Service.OnLowMemory()
  • ContentProvider.OnLowMemory()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class MainActivity extends AppCompatActivity
implements ComponentCallbacks2 {
// Other activity code ...
/**
* Release memory when the UI becomes hidden or when system resources become low.
* @param level the memory-related event that was raised.
*/
public void onTrimMemory(int level) {
// Determine which lifecycle or system event was raised.
switch (level) {
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
/*
Release any UI objects that currently hold memory.
The user interface has moved to the background.
*/
break;
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
/*
Release any memory that your app doesn't need to run.
The device is running low on memory while the app is running.
The event raised indicates the severity of the memory-related event.
If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system will
begin killing background processes.
*/
break;
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
/*
Release as much memory as the process can.
The app is on the LRU list and the system is running low on memory.
The event raised indicates where the app sits within the LRU list.
If the event is TRIM_MEMORY_COMPLETE, the process will be one of
the first to be terminated.
*/
break;
default:
/*
Release any non-critical data structures.
The app received an unrecognized memory level value
from the system. Treat this as a generic low-memory message.
*/
break;
}
}
}

//再通过Context.registerComponentCallbacks(ComponentCallbacks callback)注册

OnTrimMemory

  • Application.onTrimMemory(int level)
  • Activity.onTrimMemory(int level)
  • Fragement.OnTrimMemory(int level)
  • Service.onTrimMemory(int level)
  • ContentProvider.OnTrimMemory(int level)
1
2
3
4
5
6
7
8
9
10
11
12
13
//level内存状态,紧急程度有高到低
TRIM_MEMORY_COMPLETE = 80:内存不足,并且该进程在后台进程列表最后一个,马上就要被清理。基本与onLowMemory()情况相同;
TRIM_MEMORY_MODERATE = 60:内存不足,并且该进程在后台进程列表的中部;
TRIM_MEMORY_BACKGROUND = 40:内存不足,并且该进程是后台进程;
TRIM_MEMORY_UI_HIDDEN = 20:内存不足,并且该进程的UI已经不可见了

//4.1之后增加的,是当我们的应用程序真正运行时的回调,紧急程度有高到低
TRIM_MEMORY_RUNNING_CRITICAL = 15:内存不足(后台进程不足3个)
TRIM_MEMORY_RUNNING_LOW = 10:内存不足(后台进程不足5个)
TRIM_MEMORY_RUNNING_MODERATE = 5:内存不足(后台进程超过5个)

//ComponentCallbacks2继承ComponentCallbacks,多了一个方法:
void onTrimMemory(int level);

OnLowMemory和OnTrimMemory的比较

  1. OnLowMemory 已经没有后台进程,系统内存还低,会被回调;而onTrimMemory被回调时,还有后台进程;
  2. OnLowMemory是在最后一个后台进程被杀时调用,一般情况是low memory killer 杀进程后触发;而OnTrimMemory的触发更频繁,每次计算进程优先级时,只要满足条件,都会触发
  3. 通过一键清理后,OnLowMemory不会被触发,而OnTrimMemory会被触发一次

低内存时,下面几种资源需要进行释放

  • 缓存:包括一些文件缓存,图片缓存等,在用户正常使用的时候这些缓存很有作用,但当你的应用程序UI不可见的时候,这些缓存就可以被清除以减少内存的使用.比如第三方图片库的缓存;
  • 动态生成动态添加的View:这些动态生成和添加的View且少数情况下才使用到的View,这时候可以被释放,下次使用的时候再进行动态生成即可

例子

清除Bitmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
if(bitmap != null && !bitmap.isRecycled()){  
bitmap.recycle();
bitmap = null;
}

//主动释放ImageView的图片资源
private static void recycleImageViewBitMap(ImageView imageView) {
if (imageView != null) {
BitmapDrawable bd = (BitmapDrawable) imageView.getDrawable();
rceycleBitmapDrawable(bd);
}
}
private static void rceycleBitmapDrawable(BitmapDrawable bitmapDrawable) {
if (bitmapDrawable != null) {
Bitmap bitmap = bitmapDrawable.getBitmap();
rceycleBitmap(bitmap);
}
bitmapDrawable = null;
}
private static void rceycleBitmap(Bitmap bitmap) {
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
}

//主动释放ImageView的背景资源
public static void recycleBackgroundBitMap(ImageView view) {
if (view != null) {
BitmapDrawable bd = (BitmapDrawable) view.getBackground();
rceycleBitmapDrawable(bd);
}
}
public static void recycleImageViewBitMap(ImageView imageView) {
if (imageView != null) {
BitmapDrawable bd = (BitmapDrawable) imageView.getDrawable();
rceycleBitmapDrawable(bd);
}
}
private static void rceycleBitmapDrawable(BitmapDrawable bitmapDrawable) {
if (bitmapDrawable != null) {
Bitmap bitmap = bitmapDrawable.getBitmap();
rceycleBitmap(bitmap);
}
bitmapDrawable = null;
}

清除UI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//来自Launcher.java:
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
mAppsCustomizeTabHost.onTrimMemory();
}
}

//AppsCustomizeTabHost.java:
public void onTrimMemory() {
mContent.setVisibility(GONE);
// Clear the widget pages of all their subviews - this will trigger the widget previews
// to delete their bitmaps
mPagedView.clearAllWidgetPages();
}

//AppsCustomizePagedView.java:
public void clearAllWidgetPages() {
cancelAllTasks();
int count = getChildCount();
for (int i = 0; i < count; i++) {
View v = getPageAt(i);
if (v instanceof PagedViewGridLayout) {
((PagedViewGridLayout) v).removeAllViewsOnPage();
mDirtyPageContent.set(i, true);
}
}
}

//PagedViewGridLayout.java
@Override
public void removeAllViewsOnPage() {
removeAllViews();
mOnLayoutListener = null;
setLayerType(LAYER_TYPE_NONE, null);
}

清除Cache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if(list != null) {
list.clear();
list = null;
}

//Contact,联系人
@Override
public void onTrimMemory(int level) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
// Clear the caches. Note all pending requests will be removed too.
clear();
}
}
public void clear() {
mPendingRequests.clear();
mBitmapHolderCache.evictAll();
mBitmapCache.evictAll();
}

关闭查询数据库游标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Cursor cursor = getContentResolver().query(uri ...);
if (cursor.moveToNext()) {
... ...
}
//改为:
Cursor cursor = null;
try {
cursor = getContentResolver().query(uri ...);
if (cursor != null && cursor.moveToNext()) {
... ...
}
} finally {
if (cursor != null) {
try {
cursor.close();
} catch (Exception e) {
//ignore this
}
}
}

ListView缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public View getView(int position, View convertView, ViewGroup parent) {
View view = new Xxx(...);
... ...
return view;
}

//改为:
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
populate(view, getItem(position));
...
} else {
view = new Xxx(...);
...
}
return view;
}

释放对象的引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DemoActivity extends Activity {
... ...
private Handler mHandler = ...
private Object obj;
public void operation() {
obj = initObj();
...
[Mark]
mHandler.post(new Runnable() {
public void run() {
useObj(obj);
}
});
}
}
//可以在[Mark]的位置释放对象的引用,而代码可以修改为:
public void operation() {
obj = initObj();
...
final Object o = obj;
obj = null;
mHandler.post(new Runnable() {
public void run() {
useObj(o);
}
}
}

OnTrimMemory和onStop的关系

  • onTrimMemory()方法中的TRIM_MEMORY_UI_HIDDEN回调只有当我们程序中的所有UI组件全部不可见的时候才会触发
  • onStop()方法只是当一个Activity完全不可见的时候就会调用。在onStop()方法中去释放一些Activity相关的资源,比如说取消网络连接或者注销广播接收器等
  • onTrimMemory的TRIM_MEMORY_UI_HIDDEN 等级是在onStop方法之前调用的