안드로이드 이미지뷰에 비트맵 이미지를 로딩하다 보면 java.lang.OutOfMemoryError 가 발생하는 경우가 있다.
이미지 용량이 너무 커서 발생하는 에러이다.
이미지 용량이 몇 메가 이상일 때(?) 라는 정해진 용량은 없는것 같다. 스마트폰에 따라 또는 안드로이드 OS에 따라 에러가 발생하는 이미지의 용량은 다를 수 있다.
그럼 이 현상을 어떻게 처리하면 될 것인가?
로드할 비트맵 이미지의 사이즈를 줄여서 이미지의 용량을 줄이는 방법을 사용한다.
하지만 이미지 사이즈를 너무 줄이면 해상도가 떨어져 알아볼 수 없을 정도가 되기 때문에 적당한 크기로 줄이는 것이 좋을 것이다.
아래에 OutOfMemoryError가 발생할 때 처리하는 소스 코드를 살펴본다.
코딩에 앞서 res/drawable/ 폴더에 약 6MB의 sample_image.jpg 이미지를 저장한다.
* activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button01"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="원본 이미지 출력"
android:textColor="#000000"
android:textSize="18dp"/>
<Button
android:id="@+id/button02"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="적당히 사이즈 줄인 이미지 출력"
android:textColor="#000000"
android:textSize="18dp"/>
<Button
android:id="@+id/button03"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="너무 많이 축소 했을때 이미지 출력"
android:textColor="#000000"
android:textSize="18dp"/>
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
레이아웃에 버튼 3개를 준비한다. - (원본 사이즈의 이미지를 출력할 버튼, 이미지 사이즈를 적당히 줄여 출력할 버튼, 이미지 사이즈를 너무 많이 줄여 출력할 버튼)
그리고 이미지를 출력할 이미지뷰를 준비한다.
* MainActivity.java
package com.studio572.samplebitmapfactory;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
private final int REQUEST_WIDTH = 512;
private final int REQUEST_HEIGHT = 512;
private Button button01, button02, button03;
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.imageView);
button01 = (Button) findViewById(R.id.button01);
button02 = (Button) findViewById(R.id.button02);
button03 = (Button) findViewById(R.id.button03);
button01.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 리소스의 대용량의 이미지를 다른 작업없이 화면에 노출한다. - 이 때 OutOfMemoryError가 발생할 것이다.-
imageView.setImageResource(R.drawable.sample_image);
}
});
button02.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
BitmapFactory.Options options = new BitmapFactory.Options();
// inJustDecodeBounds = true일때 BitmapFactory.decodeResource는 리턴하지 않는다.
// 즉 bitmap은 반환하지않고, options 변수에만 값이 대입된다.
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.sample_image, options);
// 이미지 사이즈를 필요한 사이즈로 적당히 줄이기위해 계산한 값을
// options.inSampleSize 에 2의 배수의 값으로 넣어준다.
options.inSampleSize = setSimpleSize(options, REQUEST_WIDTH, REQUEST_HEIGHT);
// options.inJustDecodeBounds 에 false 로 다시 설정해서 BitmapFactory.decodeResource의 Bitmap을 리턴받을 수 있게한다.
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sample_image, options);
// 이미지 size가 재설정된 이미지를 출력한다.
imageView.setImageBitmap(bitmap);
}
});
button03.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
BitmapFactory.Options options = new BitmapFactory.Options();
// 이미지 size를 너무 많이 줄일 때 이미지 해상도가 떨어질 수 있으므로 해상도에 알맞게 resize 하는 것이 좋다.
options.inSampleSize = 200;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sample_image, options);
imageView.setImageBitmap(bitmap);
}
});
}
// 이미지 Resize 함수
private int setSimpleSize(BitmapFactory.Options options, int requestWidth, int requestHeight){
// 이미지 사이즈를 체크할 원본 이미지 가로/세로 사이즈를 임시 변수에 대입.
int originalWidth = options.outWidth;
int originalHeight = options.outHeight;
// 원본 이미지 비율인 1로 초기화
int size = 1;
// 해상도가 깨지지 않을만한 요구되는 사이즈까지 2의 배수의 값으로 원본 이미지를 나눈다.
while(requestWidth < originalWidth || requestHeight < originalHeight){
originalWidth = originalWidth / 2;
originalHeight = originalHeight / 2;
size = size * 2;
}
return size;
}
}
위 소스 코드에 주석에 설명을 달아 놓았다.
button01 : 클릭시 이미지 용량이 크다면 OutOfMemoryError가 발생할 것이다. 만약 발생하지 않는다면 용량이 좀 더 큰 이미지를 넣어 출력해본다.
button02 : 클릭시 화면에 출력전에 이미지 사이즈를 구하고 사이즈가 가로/세로(512 * 512)보다 클경우 512 * 512 사이즈보다 작을때까지 줄여서 출력한다. (여기서는 512 * 512로 작업했지만 여러분이 필요로 하는 이미지 크기로 작업하시면 될 것이다.)
button03 : 클릭시 임의로 이미지의 1/200로 이미지 사이즈를 줄였다. 이 때 이미지 해상도가 너무 떨어져 알아보기 어려울 정도로 깨졌다. 그렇기 때문에 이미지 사이즈를 적당히 줄이는 것이 중요하다.
아래 이미지는 위 소스의 출력물
아래 동영상은 위 소스의 출력 동영상이다.
'프로그래밍 > 안드로이드' 카테고리의 다른 글
[안드로이드] Margin 과 Padding의 차이점과 사용방법 - (마진과 패딩 차이점) (0) | 2017.08.25 |
---|---|
[안드로이드] ActionBar에서 Tab, ViewPager 사용하기 (2) | 2017.08.24 |
[안드로이드] ImageView scaleType 속성별 차이 예제 (4) | 2017.08.22 |
[안드로이드] ImageView에 이미지 출력하기 (1) | 2017.08.21 |
[안드로이드] Thread(쓰레드)에서 UI 변경하기 - runOnUiThread 사용하기 (2) | 2017.08.20 |
댓글