티스토리 뷰
업무 중에큰 이미지뷰가 화면에 겹쳐 올라가면서 많은 메모리를 잡아먹고 끝내는 OOM을 외치며 죽어가는 앱을 보았습니다..
이 몇장의 이미지는 꽤 큰 (대략 1200*900) 이미지 5~6장이 겹쳐져야 했습니다.
이런 부분에대해서 해결방안을 찾던중 저의 영원한 스승님 물깡이님께서 조언을 주셨고
그 조언을 내용을 바탕으로 테스트 코드를 만들어보았습니다.
노아님의 포스팅 성화에 글을 작성합니다.
제가 하려던 실무 이미지를 그대로 갖다 쓸수는 없으니 인터넷에서 이러한조건에 맞는 이미지를 구했습니다.
아래의 이미지를 0부터 9까지 총 10장의 이미지로 구성되어있습니다.
이 10장의 이미지로 샘플을 테스트해보겠습니다.
먼저 1장의 이미지 뷰를 메모리에 올렸을 때의 상태입니다.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
위의 코드와 같이 0번의 이미지만 올려보았습니다.
1장의 이미지를 올렸을때 Native 메로리가 29.4MB를 차지하게 되네요.
이제 문제의 10장의 이미지가 겹쳐졌을 때 어떻게 되는지 확인해봅시다.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num4"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num6"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num7"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num8"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/num9"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
위와 같이 이미지 뷰를 10장을 겹쳐봅니다.
와우 Total 메로리가 300메가가 넘고 Navtive 메모리는 225.2MB에 Graphic 메모리도 65.5MB나 차지하네요.
폰의 할딱거림이 느껴지네요 (할딱할딱)
자 개선해봅시다.
제가 적용한 방법은
10장의 비트맵을 1장의 비트맵으로 merge 한 후에 customview에서 ondraw의 canvas에 그려주는 방식입니다.
코드는 아래와 같습니다.
package karrel.kr.co.imagememorytest
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
/**
* Created by Rell on 2019. 4. 1..
*/
class CustomImageView @JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
View(context, attrs, defStyleAttr) {
private val images = arrayOf(
R.drawable.num0,
R.drawable.num1,
R.drawable.num2,
R.drawable.num3,
R.drawable.num4,
R.drawable.num5,
R.drawable.num6,
R.drawable.num7,
R.drawable.num8,
R.drawable.num9
)
private var bitmap: Bitmap? = null
init {
images.forEach { imageRes ->
val b = BitmapFactory.decodeResource(context.resources, imageRes)
if (bitmap == null) {
bitmap = b
} else {
bitmap = mergeToPin(bitmap!!, b)
}
}
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val paint = Paint()
canvas?.drawBitmap(bitmap, 0f, 0f, paint)
}
private fun mergeToPin(back: Bitmap, front: Bitmap): Bitmap {
val result = Bitmap.createBitmap(back.width, back.height, back.config)
val canvas = Canvas(result)
val widthBack = back.width
val widthFront = front.width
val move = ((widthBack - widthFront) / 2).toFloat()
canvas.drawBitmap(back, 0f, 0f, null)
canvas.drawBitmap(front, move, move, null)
return result
}
}
위의 코드로 만들어진 custom view를 아래와 같이 작성해봅니다.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<karrel.kr.co.imagememorytest.CustomImageView
android:layout_width="400dp"
android:layout_height="400dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
오호 결과는 원하는 대로 나왔네요. 이제 메모리 부분을 살펴보죠.
네이티브 메모리는 48.9MB Graphics 메모리는 20.3MB가 되었네요.
이걸로 제가 만든 앱을 OOM에서 구해낼 수 있겠어요! 꺅!
주의할 사항은 뷰의 초기에 drawable에서 bitmap을 로딩하는 과정에서 시간이 소요되게 됩니다.
실무 적용 시 해당 부분에 대해서는 감안해 보아야 할 것 같습니다.
샘플 코드는 아래에 있습니다.
https://github.com/karrel84/ImageMemoryTest
포스팅 끝
'개발 > ANDROID' 카테고리의 다른 글
다크모드 (0) | 2019.06.24 |
---|---|
[트랜스퍼모드] xPermode 적용기 (0) | 2019.02.11 |
와이파이 다이렉트 (0) | 2018.11.08 |
안드로이드 투명 인디케이터 만들기 (0) | 2018.10.17 |
fontFamily (0) | 2018.10.15 |
- Total
- Today
- Yesterday