RuntimeException : try to draw too large ( 바이트 수 ) Bitmap
용량이 큰 이미지를 View에 그리려고 시도했을 때 발생하는 에러입니다. application의 메모리는 제한되어 있기 때문에 큰 이미지를 표시하려면 사이즈를 줄여주는 Bitmap Resize 방법을 시도할 수 있습니다.
※ Glide 등의 이미지 라이브러리를 사용하고 있다면 라이브러리에서 제공해 주는 기능을 사용하면 더 쉽습니다.
문제의 Bitmap의 크기를 구하기
크기를 알기 위해서는 이미지를 Bitmap 형태로 만들어주겠습니다. BitmapFactory class는 Bitmap을 생성하기 위한 여러 가지 Decode Method를 제공합니다.
decodeByteArray(), decodeFile(), decodeResource() ...
하지만 우리의 문제인 큰 이미지를 Bitmap으로 바로 만들어버리면 메모리에 할당하면서 Exception을 발생시킬 수 있습니다.
그래서 우리는 BitmapFactory.Options 사용해서 크기를 구해야 합니다.
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeResource(resources, R.id.myimage, options)
val imageHeight: Int = options.outHeight
val imageWidth: Int = options.outWidth
val imageType: String = options.outMimeType
BitmapFactory.Options의 속성 인 inJustDecodeBounds는 크기 정보만 미리 구하고자 할때 사용됩니다. 해당 속성에 true를 주면, 반환값인 Bitmap은 null을 반환하지만, options 변수에 해당 Bitmap의 가로 세로 크기와 Type이 저장됩니다.
이를 이용해 Image의 크기만 먼저 가져와 application UI에 알맞은 크기를 계산한 뒤 다시 decode를 하는 것입니다.
크기 줄이기
적용하고자 하는 이미지의 크기를 알아왔으니, 원하는 크기에 맞춰서 줄이면 됩니다.
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
// Raw height and width of image
val (height: Int, width: Int) = options.run { outHeight to outWidth }
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight: Int = height / 2
val halfWidth: Int = width / 2
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
BitmapFactory.Options의 속성 중 inSampleSize 속성 값을 구하는 함수입니다. 원하는 가로, 세로 크기가 함수 인자 reqWidth , reqHeight에 들어오고 미리 구한 Bitmap의 크기가 options.run { outHeight to outWidth }에 있습니다.
미리 로드한 Bitmap의 크기가 reqWidth, reqHeight 보다 작을 때까지 알맞은 SampleSize의 값을 구하고 반환하는 함수입니다.
fun decodeSampledBitmapFromResource(
res: Resources,
resId: Int,
reqWidth: Int,
reqHeight: Int
): Bitmap {
// First decode with inJustDecodeBounds=true to check dimensions
return BitmapFactory.Options().run {
inJustDecodeBounds = true
BitmapFactory.decodeResource(res, resId, this)
// Calculate inSampleSize
inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)
// Decode bitmap with inSampleSize set
inJustDecodeBounds = false
BitmapFactory.decodeResource(res, resId, this)
}
}
반환된 inSampleSize 값으로 이제 UI에 알맞은 Size인 Bitmap를 실제로 로딩하기 위해, inJustDecodeBounds = false 설정해 준 뒤
Decode 합니다.
imageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100))
참고
https://developer.android.com/topic/performance/graphics/load-bitmap#java
'Android > Error' 카테고리의 다른 글
[Android] contact picker doesn't work on Android 11 (2) | 2023.03.16 |
---|---|
[Android] Snackbar에서 발생하는 ScrollView can host only one direct child (0) | 2023.03.02 |
[Android] Kotlin runBlockingTest 오류 (0) | 2021.05.27 |
[Android] Dagger2 적용 후 guava Compile Error (3) | 2020.12.04 |