본문 바로가기

android

[Android][Kotlin]Canvas: trying to draw too large , 비트맵 이슈

Exception이 발생했다!!!!!! 으악!!!!!!! 고쳐보자 고쳐보자

 

이슈

- Fatal Exception: java.lang.RuntimeException

- Canvas: trying to draw too large(163840000bytes) bitmap.

android.view.DisplayListCanvas.throwIfCannotDraw

 

원인

- 이미지를 로딩하는 중에 해당 스마트폰의 최대 메모리를 넘어서서? 

- ui보다 큰 이미지를 view에 올렸을 때?

 

해결 방안

1. Manifest <application>에 androd:largeHeap:"true" 추가

-> 안됨,,, 왜 안됐지 ㅜ 기억나면 다시 적어야지

 

2. BitmapFactory 이용하여 비트맵 사이즈 줄이기

-> 나는 BitmapUtil.kt 파일을 하나 만들었당 

fun decodeSampledBitmapFromResource(
  context: Context,
  resId: Int,
  reqWidth: Int,
  reqHeight: Int
): Bitmap {
// First decode with inJustDecodeBounds=true to check dimensions
  return BitmapFactory.Options().run {     //디코딩을 하면서 비트맵에 메모리를 할당하려고 하므로 쉽게 OutOfMemory 예외가 발생할 수 있음
    inJustDecodeBounds = true  //속성 값을 true로 지정해 메모리 할당을 막음 
    BitmapFactory.decodeResource(context.resources, resId, this)

    // Calculate inSampleSize
    inSampleSize = calculateInSampleSize(this, dpToPx(context,reqWidth), dpToPx(context,reqHeight))

    // Decode bitmap with inSampleSize set
    inJustDecodeBounds = false

    BitmapFactory.decodeResource(context.resources, resId, this)
  }
}



fun dpToPx(context: Context, dp: Int): Int { //dp를 px로 바꿔주는 코드
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), context.resources.displayMetrics)
.toInt()
}



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
    }
  }
  Logger.v("TAG","inSampleSize = $inSampleSize")
  return inSampleSize 
}

// 원본 이미지의 해상도가 2048x1536이고, inSampleSize가 4로 디코딩된 이미지는 약 512x384의 비트맵을 생성.

 이 비트맵을 메모리에 로드하면 전체 이미지에 12MB 대신 0.75MB가 사용된다. 

 해상도는 각각 sampleSize만큼 나눠주고, memory는 2의 4제곱으로 나눠주는 듯 !

 

마지막으로 불러주는 곳 

binding.ImageView.setImageBitmap(decodeSampledBitmapFromResource(requireContext(),R.drawable.image,100,100))

 

* 문제점 *

-> 갤럭시 s7 edge의 경우, 메모리에 비해 해상도가 너무 높아!!  inSampleSize값이 1로 나와서 이미지 해상도가 줄여지지 않음 ㅜㅡㅜ 

-> 하드코딩으로 줄여줘야함 ,,, 별로야

 

3.  그냥.. 이미지 해상도 줄여...

나는 기존에 1600*1600 썼는데 800*800으로 줄이니까 아주~ 자알~ 동작한다 ㅎ ㅎㅎ ㅎ;

 


비트맵 Exception을 잡으면서 메모리에 대해 공부 할 수 있었다.

나중엔 메모리에 대해 정리해야지 ,,,

 

아래는 메모리 관련 메소드!!

 

val am = requireActivity().getSystemService(ACTIVITY_SERVICE) as ActivityManager?
val largeMemoryClass = am!!.largeMemoryClass   //이거는 largeHeap = true로 지정했을 때 최대 메모리 
val memoryClass = am!!.memoryClass

Log.v(TAG, "largeMemoryClass:$largeMemoryClass") 
Log.v(TAG, "memoryClass:$memoryClass")

 

 

 

참고 사이트 : https://developer.android.com/topic/performance/graphics/load-bitmap#java

'android' 카테고리의 다른 글

[Android] custom toast 만들기, android 11 setView is deprecated  (0) 2021.05.26