티스토리 뷰

업무 중에 이미지뷰가 화면에 겹쳐 올라가면서 많은 메모리를 잡아먹고 끝내는 OOM 외치며 죽어가는 앱을 보았습니다..

 

몇장의 이미지는 (대략 1200*900) 이미지 5~6장이 겹쳐져야 했습니다.

 

이런 부분에대해서 해결방안을 찾던중 저의 영원한 스승님 물깡이님께서 조언을 주셨고

조언을 내용을 바탕으로 테스트 코드를 만들어보았습니다.

 

노아님 포스팅 성화에 글을 작성합니다.

 

 

제가 하려던 실무 이미지를 그대로 갖다 쓸수는 없으니 인터넷에서 이러한조건에 맞는 이미지를 구했습니다.

 

아래의 이미지를 0부터 9까지 총 10장의 이미지로 구성되어있습니다.

이 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번의 이미지만 올려보았습니다.

 

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장을 겹쳐봅니다.

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>

 

CustomImageView를 이용해서 구현

 

오호 결과는 원하는 대로 나왔네요. 이제 메모리 부분을 살펴보죠.

 

 

CustomImageView 1장 올린결과

네이티브 메모리는 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
링크