(#13) 큰 사이즈 표시하기(OOM : out of memory 를 피해보자)

생각없이.. 무분별하게 순수 기록용이므로 말이 어수선하고 정리가 안되있습니다. 혹시 글을 읽으신다면 양해바랍니다 :(

안드로이드는 일명 OOM, Out of Memory 에러를 많이 뱉어낸다. 기기가 좋을 수록 다음과 같은 에러가 나올 확률은 적지만 대부분의 셀폰은 성능이 좋지 않기 때문에..

안드로이드 허니콤(3.0) 부터는 Bitmap 메모리가 가상머신(달빅)이 처리하므로 GC의 대상이 된다. 그래서 .recycle() 이용하더라도 처리가 안된다.

그리고 가끔 디자이너와 작업할 때 이미지를 큰 걸로 주는 디자이너가 있다. 웹페이지 하시는 분이 앱으로 넘어오시면 사이즈별 크기에서부터 mdpi, xhdpi, xxhdpi 등 사이즈 별로 테블릿 별로 많기 때문에 고민을 많이 하신다.

간단하게 정리해보면

로고의 경우

mdpi — 48 x 48

hdpi — 72 x 72

xhdpi — 96 x 96

xxhdpi — 144 x 144

xxxhdpi — 192 x 192

가 된다.

이미지를 잘 랜더링 하는 방법은 여러가지 있습니다. 모두 설명할 수는 없지만 이번 프로젝트에 적용하는 방법을 적어보겠습니다.

<application
android:largeHeap="true"

다음과 같이 heap 을 늘려줄 수 도 있습니다.

비트맵을 처리하는 방법으로는..

원인이 큰 이미지를 읽으면 읽어온 이미지가 VM(Virtual machine)메모리에 들어가지 않으므로 OOM(Out of memory)이 발생하므로 이를 막기위해 이미지를 리샘플링합니다. 방법은 inJustDecodeBounds 플래그를 true로 해주어 이미지의 헤더 정보만 읽어와 이를 이용해 리사이징 하는 방법입니다.

코드로 보여드리겠습니다.

/** 이미지 정보를 가져온다. */
BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

Bitmap test = BitmapFactory.decodeResource(getResources(), R.drawable.rest, options);

이 코드는 이미지의 헤더를 가져와 width와 height을 구하는 작업입니다.

이제 구해온 option을 가지고 이미지 사이즈를 얼마나 줄여줘야하는지 구해야합니다. reqWidth, reqHeight은 얼마로 해야할까요? 물론 사용자가 원하는 크기가 있지만 내가 이미지를 넣고자하는 view의 크기를 알고 싶겠지요.

private int calculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

final int height = options.outHeight;
final int width = options.outWidth;

int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {

if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}

여기서 삽질을 많이했습니다.

— 삽질내용 —

/** Metrics 값 구하기 */
DisplayMetrics displayMetrics = getApplicationContext().getResources().getDisplayMetrics();
int MAX_WIDTH = displayMetrics.widthPixels;
int MAX_HEIGHT = displayMetrics.heightPixels;

/** width, height 구하기 */
Display display = getWindowManager().getDefaultDisplay();
int MAX_WIDTH2 = display.getWidth();
int MAX_HEIGHT2 = display.getHeight();

/** Metrics 값 구하기 */
DisplayMetrics displayMetrics3 = this.getResources().getDisplayMetrics();
int MAX_WIDTH3 = displayMetrics3.widthPixels;
int MAX_HEIGHT3 = displayMetrics3.heightPixels;

여기서 view, matrix, layout 등등 많이 값을 받아와봤지만 모두 0이라는 값이 나옵니다. 그 이유는 아직 페이지가 랜더링 되지도 않았는데 값을 읽어들이기 때문입니다. 이를 피하기 위해서 다음과 같은 콜백 메소드를 사용해야합니다.

ViewTreeObserver vto = relativeLayout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
setImage(relativeLayout);
}
});

relative layout의 width값과 height 값을 구하기 위해 다음과 같이 ViewTreeObserver를 이용하여 랜더링이 다 되었을 때 메소드가 실행되게 하면 됩니다.

이를 이용하면 6000 x 4800 짜리로 실험을 해봤는데 성공적입니다!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.