출처 : http://blog.vizpei.kr/105116344
[Intro]
보통 BitmapFactory의 decode 함수들은 메모리 Leak이 존재한다고 알려져 있습니다.
(2.1에서 수정이 되었는지 아직도 그대로인지는 잘 모르겠습니다.)
실제로 안그럴지 몰라도,
decode를 하면 할 수록 메모리 Leak의 위험부담은 더 커지기 마련이죠.
제가 처음에 Drawable을 Bitmap으로 바꿀 때 BitmapFactory를 사용 했었습니다.
정확히 말하면 Drawable을 Bitmap으로 바꾼 것이 아니라
RawResource를 InputStream으로 얻어와서 BitmapFactory로 decode한 것이었죠.
Bitmap bitmap;
InputStream stream;
stream = context.getResources().openRawResource(resource);
try {
bitmap = BitmapFactory.decodeStream(stream);
}
finally {
try { stream.close(); }
catch(IOException e) {}
}
하지만 위의 코드는 계속 BitmapFactory를 호출 하기 때문에
잠재적인 위험을 가지고 있습니다.
그렇다면 리소스로 부터 Bitmap을 얻어내고 싶다면 어떻게 해야 할까요?
[Googling...]
일단 포스팅 하기전에 구글링을 좀 해봤습니다.
상위 몇개의 검색 결과를 살펴보니...
구글링으로 살펴본 결과들은 대부분 아래와 같이 되어있었습니다.
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);
1. 빈 Bitmap을 만들고
2. Canvas를 연결 한 뒤
3. Drawable의 draw메소드를 통해 Bitmap과 연결된 Canvas에 Drawable의 내용을 그립니다.
물론 이 방법이 틀린 것은 아닙니다.
하지만 만들어 줘야 하는것이,
1. Drawable 크기만한 빈 Bitmap
2. Bitmap에 연결할 Canvas
3. 크기를 가지는 Width, Height 변수
Drawable 객체를 제외하고 두개의 객체와 두개의 변수를 더 만들어야 합니다.
중간에 setBounds() 메소드도 호출 해야 겠죠.
근데 여기서 한가지 잘 생각해 봐야 할 것이 있습니다.
View안에서는 Drawable을 잘 사용해서 이미지를 표시해 주고 있다는 점입니다.
Layout XML 파일 안에서 ImageView의 이미지를 지정해 줄 때
Drawable을 잘~ 사용해왔다는 것이 하나의 예가 될 수 있겠네요.
그렇다면 Drawable이 당연히 Bitmap을 가지고 있어야 하지 않을까요?
[BitmapDrawable]
질문에 대한 답은 바로 BitmapDrawable에 담겨있습니다.
아래의 코드를 보시죠!
BitmapDrawable drawable =
(BitmapDrawable) getResources().getDrawable(R.drawable.icon);
Bitmap bitmap = drawable.getBitmap();
읭...? 이게 끝입니다.
BitmapDrawable을 사용하면 Bitmap을 손쉽게 얻어 올 수 있습니다.
위의 길고 긴 코드가 단 두 줄로 줄어 들었습니다.
따로 Bitmap을 만들지 않아도 됩니다.
그냥 Drawable안에 있는 Bitmap을 사용하기만 하면 됩니다.
[주의사항!]
BitmapDrawable을 사용하면 손쉽게 Bitmap을 얻을 수는 있지만,
Drawable이 꼭 BitmapDrawable만 존재 하는 것은 아닙니다.
대표적인 예로 ShapeDrawable이 있을 수 있겠네요.
ShapeDrawable을 사용하면 원하는 도형(Shape 객체)을 Drawable로 사용 할 수 있습니다.
하지만 getBitmap() 메소드가 없기 때문에
ShapeDrawable로 부터 Bitmap을 얻어 올 수는 없습니다.
굳이 도형을 Bitmap으로 바꾸고 싶다면 위에서 봤던 빈 Bitmap과 Canvas를 만들어서
draw() 메소드를 통해 그리는 방법 밖에는 없습니다.
아마도 대부분의 경우 drawable 디렉토리에 있는 이미지들을 Bitmap으로 사용하려고 하지,
Shape을 Bitmap으로 사용하려고 하지는 않을 거라 생각합니다.
네네... 그럴겁니다...
[BitmapDrawable Bitmap의 특징]
BitmapDrawable에서 얻어온 Bitmap 객체는 보통녀석이 아닙니다.
특징을 한번 살펴 봅시다.
1. 우선, Bitmap을 얻어 올 때는 final 입니다.
레퍼런스에 보면 final로 선언되어 있습니다.
즉, 변경하지 않겠다는 의지를 표현 한 것이죠.
사실 리턴에 final을 붙여봤자 대입되는 변수와는 아무 상관이 없습니다... 네... 넘어가죠.
2. Immutable 입니다.
좀 더 강력한 녀석이 나왔습니다. Immutable, 즉, 변경 불가입니다.
Canvas canvas = new Canvas(bitmap);
만약 위와 같은 시도를 한다면, 아래와 같은 Exception이 발생 할겁니다.
Caused by: java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor
3. 절대 recycle() 메소드를 호출 해서는 안됩니다!
Bitmap을 얻어와서 그릴거 다 그렸다고 무의식적으로 recycle() 메소드를 호출 했다...
그럼 아래와 같은 메세지를 볼 수 있습니다.
java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@43774438
위의 상황은 ImageView 같은데서 사용하고 있던 Drawable의 Bitmap 객체를 얻어와서
그 Bitmap 객체에 recycle() 메소드를 호출 한 상황입니다.
한마디로 Bitmap 객체를 공유해서 사용한다고 볼 수 있겠죠.
[One more Tip - Bitmap copy]
그렇다면 BitmapDrawable로 부터 얻어낸 Bitmap 객체를
마음대로 바꾸고, 쓰고, 버리고 싶다면 어떻게 해야 할까요?
그냥 copy() 하면 됩니다.
Bitmap bitmap = drawable.getBitmap().copy(Config.ARGB_8888, true);
Mutable로 복사하면 마음대로 변경해서 사용 할 수 있습니다!
[Outro]
이번에는 BitmapDrawable과 Bitmap에 대해서 살짝 살펴보았습니다.
앞에서는 BitmapDrawable을 Bitmap으로 바꾸는 이야기만 했었지만,
반대로 Bimap을 Drawable로 바꾸고 싶다면
BitmapDrawable의 생성자를 사용하면 간단히 Drawable로 만들 수 있습니다.
물론 구글링해서 찾은 방법이 틀린 방법은 아닙니다.
이미지와 관련이 없는 Drawable을 다루고자 할 때는
번거롭게도 draw() 메소드를 이용 해야 하는것이 맞지만,
이미지와 관련된 Drawable을 다루고자 할 때는 분명 BitmapDrawable을 사용하는 것이 더 편합니다.
네... 제가 하고 싶은말은 그겁니다.
이미지 파일 힘들게 바꾸지 맙시다!
'Program > Android' 카테고리의 다른 글
(안드로이드 게임 점프점프 다운)JumpJump [Android Game] 추천 (0) | 2011.02.24 |
---|---|
memory leak 회피하기 (0) | 2010.11.12 |
JAVA - Android Audio record and playback is corrupt (0) | 2010.11.11 |
안드로이드 얼굴 인식 (0) | 2010.10.08 |
안드로이드 단말(기기) 모델(기종) 정보 뽑아 오기 (0) | 2010.08.27 |