퍼옴 : http://blog.naver.com/PostView.nhn?blogId=nimbusob&logNo=147715066&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView

 

앞선 포스트
Android 개발가이드 - Bitmap.recycle()은 가급적 호출해 주는 편이 좋다. 에서는 명시적인 오브젝트 해제의 필요성을 알아보았다. VM 을 사용하는 언어에서 코딩함에도, c 스타일의 리소스 해제를 왜 해주어야 하는지에 대한 내용이었다. (안드로이드 프레임웍의 문제 및 특수성, 한정적인 VM Heap size 와 같은 이유 등)

그러나 안드로이드의 내부 동작으로 인해 Bitmap.recycle() 과는 별개로 view 에서의 메모리 누수 위험은 항상 잠재해 있다(자세한 내용은 안드로이드 내부 구현에 대한 이야기로 이어져야 하므로 생략). 결국 가장 안전한 방법은, view 에 할당된 모든 것을 초기화 해 버리는 것이다.

그럼 어떻게 할 것인가? 화면에 보이는 수많은 뷰들에 대해 일일히 null을 할 것인가?
이 점에 착안하여 아래와 같은 지저분한 트릭을 고안해 보았다.
왜 지저분하냐면, 정리 과정에 어느 정도의 성능 저하가 우려되기 때문이다.
주요 성능저하의 원인은 반복되는 재귀 호출 및 Root View 부터의 완전 트리 탐색 로직 때문이다.
게다가 마지막의 System.gc() 콜은 설명이 필요없이 지저분하다. (자세한 내용은 API 문서 참고 http://developer.android.com/reference/java/lang/System.html#gc() - GC호출은 GC를 보장하지 않음)

그러므로 가벼운 앱에서는 이 내용은 고민하지 않아도 무방하다.
View 갯수가 많은 화면을 Tab 혹은 ViewFlipper 등으로 구현한 경우와 같이,
메모리가 절박한 경우라면 이 트릭이 도움이 될 것이다.

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.ImageView;
/**
* Utility class to help unbinding resources consumed by Views in Activity.
*
* @author Hwan Jo(nimbusob@gmail.com)
*/
public class ViewUnbindHelper {
/**
* Removes the reference to the activity from every view in a view hierarchy
* (listeners, images etc.). This method should be called in the onDestroy() method
* of each activity.
* This code may stinks, but better than worse - suspiciously, Android framework
* does not free resources immediately which are consumed by Views and this leads to
* OutOfMemoryError sometimes although there are no user mistakes.
*
* @param view View to free from memory
*/
public static void unbindReferences(View view) {
try {
if (view != null) {
unbindViewReferences(view);
if (view instanceof ViewGroup) {
unbindViewGroupReferences((ViewGroup)view);
}
}
} catch (Exception ignore) {
/* whatever exception is thrown just ignore it because a crash is
* always worse than this method not doing what it's supposed to do
*/
}
}
/**
* Removes the reference to the activity from every view in a view hierarchy
* (listeners, images etc.). This method should be called in the onDestroy() method
* of each activity.
* This code may stinks, but better than worse - suspiciously, Android framework
* does not free resources immediately which are consumed by Views and this leads to
* OutOfMemoryError sometimes although there are no user mistakes.
*
* @param view View to free from memory
*/
public static void unbindReferences(Activity activity, int viewID) {
try {
View view = activity.findViewById(viewID);
if (view != null) {
unbindViewReferences(view);
if (view instanceof ViewGroup) {
unbindViewGroupReferences((ViewGroup)view);
}
}
} catch (Exception ignore) {
/* whatever exception is thrown just ignore it because a crash is
* always worse than this method not doing what it's supposed to do.
*/
}
}
private static void unbindViewGroupReferences(ViewGroup viewGroup) {
int nrOfChildren = viewGroup.getChildCount();
for (int i = 0; i < nrOfChildren; i++) {
View view = viewGroup.getChildAt(i);
unbindViewReferences(view);
if (view instanceof ViewGroup) {
unbindViewGroupReferences((ViewGroup)view);
}
}
try {
viewGroup.removeAllViews();
} catch (Exception ignore) {
// AdapterViews, ListViews and potentially other ViewGroups don't support the removeAllViews operation
}
}
private static void unbindViewReferences(View view) {
// Set everything to null (API Level 8)
try {
view.setOnClickListener(null);
} catch (Exception ignore) {}
try {
view.setOnCreateContextMenuListener(null);
} catch (Exception ignore) {}
try {
view.setOnFocusChangeListener(null);
} catch (Exception ignore) {}
try {
view.setOnKeyListener(null);
} catch (Exception ignore) {}
try {
view.setOnLongClickListener(null);
} catch (Exception ignore) {}
try {
view.setOnClickListener(null);
} catch (Exception ignore) {}
try {
view.setTouchDelegate(null);
} catch (Exception ignore) {}
Drawable d = view.getBackground();
if (d != null) {
try {
d.setCallback(null);
} catch (Exception ignore) {}
}
if (view instanceof ImageView) {
ImageView imageView = (ImageView)view;
d = imageView.getDrawable();
if (d != null) {
d.setCallback(null);
}
if (d instanceof BitmapDrawable) {
Bitmap bm = ((BitmapDrawable)d).getBitmap();
bm.recycle();
}
imageView.setImageDrawable(null);
} else if (view instanceof WebView) {
((WebView)view).destroyDrawingCache();
((WebView)view).destroy();
}
try {
view.setBackgroundDrawable(null);
} catch (Exception ignore) {}
try {
view.setAnimation(null);
} catch (Exception ignore) {}
try {
view.setContentDescription(null);
} catch (Exception ignore) {}
try {
view.setTag(null);
} catch (Exception ignore) {}
}
}

위 코드를 Activity 화면의 onDestroy 에서 호출해 주면 확실히 메모리 절약 효과가 있다. 우리가 미처 신경쓰지 못한 내부 callback 등까지 확실히 제거해 주므로 View 가 올바른 GC의 대상이 되기 때문이다

'Android > Tip&Tech' 카테고리의 다른 글

[펌]Android BitmapFactory.Options 설명  (0) 2013.06.18
[펌] 나인패치 자동 생성  (0) 2013.06.14
해상도 지원  (0) 2013.06.03
Android Samsung Spen Sdk API  (1) 2013.01.10
[펌] JSP 에서 엑셀 다운로드 받기  (0) 2012.11.12

+ Recent posts