[Android]Kotlin에서 try-with-resources 패턴 구현하기: Bitmap 리소스 관리

gay0ung·2025년 8월 6일
0

Android

목록 보기
12/14

문제 상황

Android 앱 개발 중 Bitmap 리소스 관리에서 자주 마주치는 문제입니다:

try {
    val processedBitmap = processImage(originalBitmap)
    // 이미지 처리 로직
    saveToFile(processedBitmap)
} catch (e: Exception) {
    Log.e("TAG", "Error processing image", e)
} finally {
    originalBitmap.recycle()
    processedBitmap.recycle() // null일 수도 있어서 위험!
}

문제점:

  • finally 블록에서 수동으로 리소스 해제
  • 예외 발생 시 리소스 누수 위험
  • null 체크 누락 가능성

Kotlin의 해결책: .use 확장 함수

1. 기본 .use 함수

Kotlin은 Closeable 인터페이스를 구현한 클래스에 .use 확장 함수를 제공합니다:

FileInputStream("file.txt").use { input ->
    input.read()
} // 자동으로 close() 호출

하지만 BitmapCloseable을 구현하지 않아서 직접 사용할 수 없습니다.

2. Bitmap 전용 확장 함수 만들기

inline fun <T> Bitmap.use(block: (Bitmap) -> T): T {
    return try {
        block(this)
    } finally {
        if (!this.isRecycled) {
            this.recycle()
        }
    }
}

실전 적용 예시

Before: 수동 리소스 관리

fun processAndSaveImage(inputPath: String, outputPath: String) {
    var original: Bitmap? = null
    var processed: Bitmap? = null

    try {
        original = BitmapFactory.decodeFile(inputPath)
        processed = applyFilter(original)
        saveBitmap(processed, outputPath)
    } catch (e: Exception) {
        Log.e("ImageProcessor", "Failed to process image", e)
    } finally {
        original?.recycle()
        processed?.recycle()
    }
}

After: .use 확장 함수 활용

fun processAndSaveImage(inputPath: String, outputPath: String) {
    BitmapFactory.decodeFile(inputPath).use { original ->
        applyFilter(original).use { processed ->
            saveBitmap(processed, outputPath)
        }
    }
}

실용적인 활용 패턴

1. 여러 Bitmap 동시 처리

fun mergeImages(path1: String, path2: String): Bitmap {
    return BitmapFactory.decodeFile(path1).use { bitmap1 ->
        BitmapFactory.decodeFile(path2).use { bitmap2 ->
            mergeBitmaps(bitmap1, bitmap2)
        }
    }
}

2. 조건부 처리

fun processImage(bitmap: Bitmap, shouldResize: Boolean) {
    bitmap.use { original ->
        if (shouldResize) {
            resizeBitmap(original, 500, 500).use { resized ->
                uploadImage(resized)
            }
        } else {
            uploadImage(original)
        }
    }
}

3. 에러 처리 강화

inline fun <T> Bitmap.use(block: (Bitmap) -> T): T {
    return try {
        block(this)
    } finally {
        try {
            if (!this.isRecycled) {
                this.recycle()
            }
        } catch (e: Exception) {
            // recycle 실패는 무시
        }
    }
}

다른 리소스에도 적용하기

// Canvas 자동 관리
inline fun <T> Canvas.use(block: (Canvas) -> T): T {
    return try {
        block(this)
    } finally {
        this.restore()
    }
}

// 사용 예시
canvas.use { c ->
    c.save()
    c.rotate(45f)
    c.drawBitmap(bitmap, 0f, 0f, paint)
}

성능 고려사항

inline 키워드의 중요성

// inline을 사용하면 함수 호출 오버헤드가 없음
inline fun <T> Bitmap.use(block: (Bitmap) -> T): T {
    // 컴파일 시 호출 위치에 직접 삽입됨
}

결론

Kotlin의 확장 함수를 활용하면 Java의 try-with-resources와 동일한 안전성을 확보할 수 있습니다.

장점

  • 자동 리소스 관리로 메모리 누수 방지
  • 코드 가독성 향상
  • 예외 안전성 보장

권장사항

  1. 프로젝트 공통 유틸리티에 .use 확장 함수 정의
  2. 모든 Bitmap 처리 코드에 일관되게 적용
  3. 다른 리소스 타입에도 확장하여 활용

Android 앱의 안정성을 위해 꼭 적용해보세요!


참고 자료

0개의 댓글