8000 GitHub - murjune/Image-Cache-Sample: πŸ“š μš°μ•„ν•œ 글쓰기에 μ‚¬μš©λœ 이미지 μƒ˜ν”Œμ•±λ‹ˆλ‹€!
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

πŸ“š μš°μ•„ν•œ 글쓰기에 μ‚¬μš©λœ 이미지 μƒ˜ν”Œμ•±λ‹ˆλ‹€!

License

Notifications You must be signed in to change notification settings

murjune/Image-Cache-Sample

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

5 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

이미지 μΊμ‹œ μƒ˜ν”Œ μ•±

이미지 μΊμ‹œλ₯Ό μ μš©ν•˜μ—¬ 이미지 λ‘œλ”© 속도λ₯Ό κ°œμ„ ν•˜λŠ” μƒ˜ν”Œ μ•±μž…λ‹ˆλ‹€. 브랜치 λ³„λ‘œ λ‹€μ–‘ν•œ μΊμ‹œ μ „λž΅μ„ μ μš©ν•΄λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€!


μ†Œκ°œ

μ•ˆλ…•ν•˜μ„Έμš”! μ €λŠ” ν˜„μž¬ μš°ν…Œμ½”μ—μ„œ μ•ˆλ“œλ‘œμ΄λ“œ 개발자둜 "PokeRogueHelper" ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

μ•±μ—μ„œ 화면을 이동할 λ•Œλ§ˆλ‹€ "포켓λͺ¬ 데이터"λ₯Ό 뢈러였고 이미지λ₯Ό λ Œλ”λ§ν•˜λŠ” μž‘μ—…μ΄ λΉˆλ²ˆν•˜κ²Œ λ°œμƒν•˜λŠ”λ°, 이둜 인해 이미지 λ‘œλ”© 속도가 λŠλ €μ§€λŠ” λ¬Έμ œκ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.

저희 νŒ€μ€ μΊμ‹œ(Cache)λ₯Ό ν™œμš©ν•˜μ—¬ μ•± μ„±λŠ₯을 κ°œμ„ ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 포켓λͺ¬μ΄λΌλŠ” 도메인 νŠΉμ„±μƒ 데이터가 자주 λ³€κ²½λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ—, μΊμ‹œλ₯Ό μ μš©ν•˜μ—¬ 데이터 λ‘œλ”©κ³Ό 이미지 λžœλ”λ§ 속도λ₯Ό 크게 κ°œμ„ ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

이번 κΈ€μ—μ„œλŠ” λΉ λ₯΄κ³  효율적인 데이터 λ‘œλ“œλ₯Ό μœ„ν•΄ λ°˜λ“œμ‹œ μ•Œμ•„μ•Ό ν•  μΊμ‹œμ— λŒ€ν•΄ μ•Œμ•„λ³΄κ³ , 이미지 μΊμ‹œ μ‹€μŠ΅ 예제λ₯Ό 톡해 μΊμ‹œλ₯Ό 직접 κ΅¬ν˜„ν•˜λŠ” 방법을 μ†Œκ°œν•˜κ² μŠ΅λ‹ˆλ‹€.

1. μΊμ‹œ

μΊμ‹œλŠ” 데이터λ₯Ό μž„μ‹œλ‘œ μ €μž₯ν•˜λŠ” μž₯μ†Œλ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€.

자주 μ‚¬μš©ν•˜λŠ” λ°μ΄ν„°μ˜ 경우 맀번 λ„€νŠΈμ›Œν¬ 톡신을 톡해 데이터λ₯Ό λΆˆλŸ¬μ˜€λŠ” 것은 λΉ„νš¨μœ¨μ μž…λ‹ˆλ‹€. 졜초둜 데이터λ₯Ό 뢈러올 λ•Œ,μΊμ‹œμ— 데이터λ₯Ό μ €μž₯ν•˜μ—¬ λ„€ν¬μ›Œν¬ 톡신을 쀄이고, 데이터 λ‘œλ”© 속도λ₯Ό κ°œμ„ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

1-1) λ©”λͺ¨λ¦¬ μΊμ‹œ vs λ””μŠ€ν¬ μΊμ‹œ

μΊμ‹œλŠ” μ €μž₯ μœ„μΉ˜μ— 따라 λ©”λͺ¨λ¦¬ μΊμ‹œμ™€ λ””μŠ€ν¬ μΊμ‹œλ‘œ λ‚˜λ‰©λ‹ˆλ‹€.
λ‚΄κ°€ μ €μž₯ν•  λ°μ΄ν„°μ˜ νŠΉμ„±μ— 따라 μ μ ˆν•œ μΊμ‹œ 방법을 μ„ νƒν•΄μ•Όν•©λ‹ˆλ‹€.

λ©”λͺ¨λ¦¬ μΊμ‹œλŠ” RAM 에 μ €μž₯되기 λ•Œλ¬Έμ— λΉ λ₯΄κ²Œ 데이터λ₯Ό 뢈러올 수 μžˆμ§€λ§Œ, 앱이 μ’…λ£Œλ˜λ©΄ 데이터가 μ‚¬λΌμ§‘λ‹ˆλ‹€.

λ””μŠ€ν¬ μΊμ‹œλŠ” ν•˜λ“œ λ””μŠ€ν¬μ— μ €μž₯되기 λ•Œλ¬Έμ— 앱이 μ’…λ£Œλ˜μ–΄λ„ 데이터가 μœ μ§€λ©λ‹ˆλ‹€. ν•˜μ§€λ§Œ, λ©”λͺ¨λ¦¬ μΊμ‹œμ— λΉ„ν•΄ 느리게 데이터λ₯Ό 뢈러올 수 μžˆμŠ΅λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같은 κΈ°μ€€μœΌλ‘œ μ μ ˆν•œ μΊμ‹œ 방법을 선택할 수 μžˆμŠ΅λ‹ˆλ‹€.

  1. λΉ λ₯΄κ²Œ 데이터λ₯Ό λ‘œλ”©ν•΄μ•Όν•  경우: λ©”λͺ¨λ¦¬ μΊμ‹œ
  2. 데이터λ₯Ό μž₯κΈ°κ°„ μ €μž₯ν•˜κ±°λ‚˜ μ•± μž¬μ‹œμž‘ μ‹œ 데이터λ₯Ό μœ μ§€ν•΄μ•Όν•  경우: λ””μŠ€ν¬ μΊμ‹œ
  3. 데이터가 μž₯κΈ°κ°„ λ³€κ²½λ˜μ§€ μ•ŠλŠ” 경우: λ””μŠ€ν¬ μΊμ‹œ
  4. 데이터가 자주 λ³€κ²½λ˜λŠ” 경우: λ©”λͺ¨λ¦¬ μΊμ‹œ
  5. 데이터가 μ‹€μ‹œκ°„μœΌλ‘œ λ³€κ²½λ˜λŠ” 경우: μΊμ‹œ μ‚¬μš© X

1-2) μΊμ‹œ μ˜€λ²„ν”Œλ‘œμš°

μΊμ‹œλ˜λŠ” 데이터가 λ„ˆλ¬΄ λ§Žμ•„μ Έ μΊμ‹œμ˜ 크기λ₯Ό μ΄ˆκ³Όν•˜κ²Œ 되면 μ–΄λ–»κ²Œ λ κΉŒμš”? πŸ€”

μΊμ‹œμ— μ €μž₯된 데이터가 λ„ˆλ¬΄ λ§Žμ•„μ Έμ„œ μΊμ‹œμ˜ 크기λ₯Ό μ΄ˆκ³Όν•˜κ²Œ λ˜λŠ” 경우λ₯Ό μΊμ‹œ μ˜€λ²„ν”Œλ‘œμš°(Cache Overflow)라고 ν•©λ‹ˆλ‹€. μƒˆλ‘œμš΄ 데이터λ₯Ό μΊμ‹œμ— μ €μž₯ν•˜κΈ° μœ„ν•΄ μ €μž₯된 데이터 쀑 μΌλΆ€λŠ”μ‚­μ œλ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

μ΄λ•Œ μ–΄λ–€ 데이터λ₯Ό λ¨Όμ € μ‚­μ œν• μ§€ κ²°μ •ν•˜λŠ” 방법을 μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜μ΄λΌκ³  ν•©λ‹ˆλ‹€.

1-3) μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜

μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜ 쀑 κ°€μž₯ 많이 μ‚¬μš©λ˜λŠ” μ•Œκ³ λ¦¬μ¦˜μ€ LRU 와 LFU μž…λ‹ˆλ‹€.

  1. LFU(Least Frequently Used): κ°€μž₯ 적게 μ‚¬μš©λœ 데이터λ₯Ό μ‚­μ œν•˜λŠ” 방식
  2. LRU(Least Recently Used): κ°€μž₯ 였래 μ‚¬μš©λ˜μ§€ μ•Šμ€ 데이터λ₯Ό μ‚­μ œν•˜λŠ” 방식

LFU λŠ” λ°μ΄ν„°μ˜ μ°Έμ‘° λΉˆλ„μˆ˜μ— μ˜κ±°ν•œ μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜μž…λ‹ˆλ‹€. λ§Œμ•½ νŠΉμ • 데이터가 λ‹€λ₯Έ 데이터에 λΉ„ν•΄ 더 자주 μ‚¬μš©λ˜λŠ” κ²½μš°μ— LFU μ•Œκ³ λ¦¬μ¦˜μ΄ μ ν•©ν•©λ‹ˆλ‹€.

  • ex) 파이리 λΌλŠ” 포켓λͺ¬ 이미지가 λ‹€λ₯Έ 포켓λͺ¬μ— λΉ„ν•΄ 3 ~ 4 λ°° 많이 λžœλ”λ§λœλ‹€λ©΄, νŒŒμ΄λ¦¬λŠ” λ‹€λ₯Έ 포켓λͺ¬μ— λΉ„ν•΄ 더 자주 μ°Έμ‘°λ˜λŠ” λ°μ΄ν„°μž…λ‹ˆλ‹€.

LRU λŠ” μ‹œκ°„ 지역성에 μ˜κ±°ν•œ κ°€μž₯ 많이 μ‚¬μš©λ˜λŠ” μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜μž…λ‹ˆλ‹€. μ‹œκ°„ μ§€μ—­μ„±μ΄λž€ μ‚¬μš©μžκ°€ κ°€μž₯ μ΅œκ·Όμ— μ‚¬μš©ν•œ 데이터가 κ°€μž₯ 높은 ν™•λ₯ λ‘œ λ‹€μ‹œ μ‚¬μš©λ  κ²ƒμ΄λΌλŠ” κ°œλ…μž…λ‹ˆλ‹€.

  • ex) μ‚¬μš©μžκ°€ μ΅œκ·Όμ— ν”ΌμΉ΄μΈ„ 이미지λ₯Ό μ‚¬μš©ν–ˆλ‹€λ©΄, λ‹€μŒμ—λ„ ν”ΌμΉ΄μΈ„ 이미지λ₯Ό μ‚¬μš©ν•  ν™•λ₯ μ΄ λ†’λ‹€λŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€.

λ°μ΄ν„°μ˜ νŠΉμ„±μ— 따라 μ μ ˆν•œ μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜μ„ μ„ νƒν•˜μ—¬ μΊμ‹œλ₯Ό 관리해면 λ©λ‹ˆλ‹€! πŸ’ͺ

이제 κ°„λž΅ν•˜κ²Œ μΊμ‹œμ— λŒ€ν•΄ μ•Œμ•„λ³΄μ•˜μœΌλ‹ˆ, μ‹€μŠ΅μ„ 톡해 이미지 μΊμ‹œλ₯Ό κ΅¬ν˜„ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€!

3. μ‹€μŠ΅ - 이미지 μΊμ‹œ

μžμ„Έν•œ μ½”λ“œλŠ” μ‹€μŠ΅ κΉƒν—ˆλΈŒ μ£Όμ†Œμ—μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€!

img_1.png

이미지 URL 을 톡해 이미지λ₯Ό λΆˆλŸ¬μ™€ 화면에 λžœλ”λ§ν•˜λŠ” κ°„λ‹¨ν•œ μƒ˜ν”Œμ•±μ„ λ§Œλ“€μ–΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

Step1: λ„€νŠΈμ›Œν¬ 톡신

class ImageLoader(
    private val ImageService: PokemonImageService
) {
    suspend fun bitmaps(urls: List<String>): List<Bitmap> {
        return ImageService.bitmaps(urls)
    }
}

ImageLoader μ—μ„œ ImageService λ₯Ό 톡해 λ„€νŠΈμ›Œν¬ ν†΅μ‹ ν•˜μ—¬ 포켓λͺ¬ 이미지λ₯Ό λΆˆλŸ¬μ˜€κ² μŠ΅λ‹ˆλ‹€.

이미지 λ‘œλ”©μ΄ λ„ˆλ¬΄ μ˜€λž˜κ±Έλ¦¬λ„€μš”. λ©”λͺ¨λ¦¬ μΊμ‹œλ₯Ό μ μš©ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€!

Step2: λ©”λͺ¨λ¦¬ μΊμ‹œ 적용

class ImageLoader(
    private val imageService: ImageService
) {
    private val cachedImages: MutableMap<String, Bitmap> = mutableMapOf<String, Bitmap>()

    suspend fun bitmaps(urls: List<String>): List<Bitmap> {
        if (cachedImages.keys.containsAll(urls.toSet())) {
            return urls.map { requireNotNull(cachedImages[it]) }
        }
        return imageService.bitmaps(urls).also { cacheImages(urls, it) }
    }

    fun clearCache() {
        cachedImages.clear()
    }

    private fun cacheImages(keys: List<String>, images: List<Bitmap>) {
        keys.forEachIndexed { index, key ->
            cachedImages[key] = images[index]
        }
    }
}

Map 자료ꡬ쑰λ₯Ό ν™œμš©ν•˜μ—¬ λ©”λͺ¨λ¦¬ μΊμ‹œλ₯Ό κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

  1. cachedImages에 이미지가 μΊμ‹œλ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•œ ν›„, μΊμ‹œλ˜μ–΄ 있으면 μΊμ‹œλœ 이미지λ₯Ό λ°˜ν™˜
  2. κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ λ„€νŠΈμ›Œν¬ 톡신을 톡해 이미지λ₯Ό 뢈러온 ν›„, μΊμ‹œμ— μ €μž₯

μœ„μ™€ 같은 λ°©μ‹μœΌλ‘œ 이미지λ₯Ό μΊμ‹œν•˜λ©΄, λ™μΌν•œ 이미지 URL 의 경우 λΉ λ₯΄κ²Œ λ‘œλ“œν•  수 μžˆμŠ΅λ‹ˆλ‹€!

데이터λ₯Ό λ¦¬ν”„λ ˆμ‰¬ν•˜λ”λΌλ„ λ‘œλ”© 화면이 보이지 μ•Šμ„ μ •λ„λ‘œ 이미지 λ‘œλ”©μ΄ λΉ¨λΌμ‘ŒμŠ΅λ‹ˆλ‹€! κ·ΈλŸ¬λ‚˜, 앱을 μž¬μ‹œμž‘ν•˜λ©΄ μ–΄λ–¨κΉŒμš”??

λ©”λͺ¨λ¦¬ μΊμ‹œ 방식은 RAM에 μΊμ‹œν–ˆκΈ° λ•Œλ¬Έμ— ν”„λ‘œμ„ΈμŠ€κ°€ μ’…λ£Œλ˜λ©΄ μΊμ‹œκ°€ λΉ„μ›Œμ§€κ²Œλ©λ‹ˆλ‹€.
λ”°λΌμ„œ, λ„€νŠΈμ›Œν¬ 톡신과 λ™μΌν•˜κ²Œ 이미지λ₯Ό λ‹€μ‹œ λΆˆλŸ¬μ™€μ•Όν•©λ‹ˆλ‹€.

이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ””μŠ€ν¬ μΊμ‹œλ₯Ό μ μš©ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€!

Step3: λ””μŠ€ν¬ μΊμ‹œ 적용

μ•ˆλ“œλ‘œμ΄λ“œ λ‚΄λΆ€ μ €μž₯μ†Œλ₯Ό ν™œμš©ν•˜μ—¬ PokemonImageSaver ν΄λž˜μŠ€μ— 이미지λ₯Ό μ €μž₯ν•˜κ³  λΆˆλŸ¬μ˜€λŠ” κΈ°λŠ₯을 μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

class ImageSaver(context: Context) {
    private val cacheFolder: File = File(context.cacheDir, "pokemon")
        get() {...}

    suspend fun bitmaps(urls: List<String>): List<Bitmap> = withContext(Dispatchers.IO) {
        urls.mapNotNull { url ->
            val file = photoCacheFile(url)
            if (file.exists()) {
                BitmapFactory.decodeFile(file.absolutePath)
            } else {
                null
            }
        }
    }
    suspend fun saveImage(url: String, bitmap: Bitmap) = withContext(Dispatchers.IO) {
        photoCacheFile(url).outputStream().use { output ->
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, output)
        }
    }
  • saveImage ν•¨μˆ˜μ—μ„œλŠ” Bitmap 을 PNG ν˜•μ‹μœΌλ‘œ μ••μΆ•ν•˜μ—¬ λ‚΄λΆ€ μ €μž₯μ†Œμ— μ €μž₯ν•©λ‹ˆλ‹€.
  • bitmaps ν•¨μˆ˜μ—μ„œλŠ” urls 에 ν•΄λ‹Ήν•˜λŠ” 이미지 파일이 μ‘΄μž¬ν•˜λ©΄ Bitmap 으둜 λ””μ½”λ”©ν•˜μ—¬ λ°˜ν™˜ν•©λ‹ˆλ‹€.
class ImageLoader(
    private val imageService: ImageService,
    private val imageSaver: ImageSaver
) {
    private val cachedImages: MutableMap<String, Bitmap> = mutableMapOf<String, Bitmap>()

    suspend fun bitmaps(urls: List<String>): List<Bitmap> {
        if (isMemoryCached(urls)) {
            return urls.map { requireNotNull(cachedImages[it]) }
        }
        if (isDiskCached(urls)) {
            return imageSaver.bitmaps(urls).also { cacheImages(urls, it) }
        }

        return imageService.bitmaps(urls)
            .also { bitmap ->
                urls.zip(bitmap).forEach { (url, bitmap) ->
                    imageSaver.saveImage(url, bitmap)
                }
            }
            .also { bitmap ->
                cacheImages(urls, bitmap)
            }
    }
    ...

이제 PokemonImageLoader μ—μ„œ λ©”λͺ¨λ¦¬ μΊμ‹œμ™€ λ””μŠ€ν¬ μΊμ‹œλ₯Ό λͺ¨λ‘ ν™œμš©ν•˜μ—¬ 이미지λ₯Ό λΆˆλŸ¬μ˜΅λ‹ˆλ‹€.

  1. λ©”λͺ¨λ¦¬ μΊμ‹œμ— 이미지가 μ‘΄μž¬ν•˜λŠ”μ§€ 확인
  2. λ””μŠ€ν¬ μΊμ‹œμ— 이미지가 μ‘΄μž¬ν•˜λŠ”μ§€ 확인
  3. λ„€νŠΈμ›Œν¬ 톡신을 톡해 이미지λ₯Ό 뢈러온 ν›„, λ””μŠ€ν¬ μΊμ‹œ, λ©”λͺ¨λ¦¬ μΊμ‹œμ— μ €μž₯

이제 앱을 μž¬μ‹œμž‘ν•΄λ„ λ””μŠ€ν¬λœ 이미지λ₯Ό λΆˆλŸ¬μ™€ λΉ λ₯΄κ²Œ 화면을 λ‘œλ“œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Step4: LRU μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜ 적용

λ‚΄λΆ€ μ €μž₯μ†Œλ‚˜ RAM 곡간이 λΆ€μ‘±ν•΄μ§ˆ 경우, μΊμ‹œλœ 이미지 쀑 일뢀λ₯Ό μ‚­μ œν•œ ν›„ μƒˆλ‘œμš΄ 이미지λ₯Ό μ €μž₯ν•΄μ•Όν•©λ‹ˆλ‹€.

Step4-1) λ©”λͺ¨λ¦¬ μΊμ‹œ LRU 적용

λ©”λͺ¨λ¦¬ μΊμ‹œμ˜ 경우 μ•ˆλ“œλ‘œμ΄λ“œμ—μ„œ μ œκ³΅ν•˜λŠ” LRUCacheλ₯Ό ν™œμš©ν•˜μ—¬ LRU μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜μ„ μ‰½κ²Œ μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

mutableMap 을 LRUCache 둜 λ³€κ²½ν•΄μ£Όκ² μŠ΅λ‹ˆλ‹€!

class ImageLoader(
    ...
) {
    private val cachedImages: LruCache<String, Bitmap> =
        lruCache(cacheSize(), sizeOf = { _, value -> value.byteCount / 1024 })

    private fun cacheSize(): Int {
        val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
        return maxMemory / 8
    }
}

lruCache() νŒ©ν† λ¦¬ ν•¨μˆ˜λ₯Ό 톡해 LRUCache λ₯Ό μƒμ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

  • cacheSize() ν•¨μˆ˜λ₯Ό 톡해 μΊμ‹œμ˜ μ΅œλŒ€ 크기 λ©”λͺ¨λ¦¬μ˜ 1/8 둜 μ„€μ •ν•˜μ˜€μŠ΅λ‹ˆλ‹€.
  • sizeOf λŠ” μΊμ‹œμ— μ €μž₯될 데이터 ν•˜λ‚˜μ˜ 크기λ₯Ό κ³„μ‚°ν•˜λŠ” λžŒλ‹€μž…λ‹ˆλ‹€.

λ§Œμ•½, μΊμ‹œμ— μΆ”κ°€λœ Bitmap 의 byteCount κ°€ 1024 보닀 크닀면, 1KB λ‹¨μœ„λ‘œ μΊμ‹œμ— μ €μž₯λ©λ‹ˆλ‹€. lruCache() 의 μ‚¬μš©λ²•μ΄ κΆκΈˆν•˜μ‹œλ‹€λ©΄ lruCache ν•™μŠ΅ν…ŒμŠ€νŠΈ λ₯Ό μ°Έκ³ ν•΄μ£Όμ„Έμš”!

μ™œ maxMemory / 8 둜 μ„€μ •ν–ˆμ„κΉŒμš”? πŸ€”

일반/hdpi 기기의 경우 μ΅œμ†Œ 32MB 의 λ©”λͺ¨λ¦¬λ₯Ό μ œκ³΅ν•˜μ—¬, maxMemory / 8 μ •λ„λ‘œ μΊμ‹œλ₯Ό μ„€μ •ν•˜λŠ” 것을 ꢌμž₯ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

λ§Œμ•½, 800x480 ν•΄μƒλ„μ˜ κΈ°κΈ°μ—μ„œ μ΄λ―Έμ§€λ‘œλ§Œ κ΅¬μ„±λœ GridView κ°€ 화면에 꽉 μ±„μšΈ 경우, μ•½ 1.5MB 의 λ©”λͺ¨λ¦¬κ°€ ν•„μš”ν•˜λ‹€κ³  ν•©λ‹ˆλ‹€. λ”°λΌμ„œ, 32MB / 8 = 4MB μ •λ„λ‘œ μ„€μ •ν•˜λ©΄ μ•½ 2.5 Page 에 ν•΄λ‹Ήν•˜λŠ” 이미지λ₯Ό μΊμ‹œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λΉ„νŠΈλ§΅ 캐싱 μ•ˆλ“œλ‘œμ΄λ“œ κ³΅μ‹λ¬Έμ„œ

Step4-2) λ””μŠ€ν¬ μΊμ‹œ LRU 적용

λ””μŠ€ν¬ μΊμ‹œμ˜ 경우, μ•ˆλ“œλ‘œμ΄λ“œμ—μ„œ κ³΅μ‹μ μœΌλ‘œ μ§€μ›ν•˜λŠ” DiskLruCache λŠ” μ—†μŠ΅λ‹ˆλ‹€. 😒
λ”°λΌμ„œ, java 의 File I/O λ₯Ό ν™œμš©ν•˜μ—¬ LRU μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜μ„ 직접 κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

μ΄λŠ” κ°€μž₯ μ˜€λž˜μ „μ— μ ‘κ·Όν•œ νŒŒμΌμ„ μ°Ύμ•„ κ΅μ²΄ν•˜κΈ° μœ„ν•΄ 파일의 lastModified λ₯Ό ν˜„μž¬ μ‹œκ°„μœΌλ‘œ μ—…λ°μ΄νŠΈν•˜λŠ” updateFileAccessTime ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

private fun updateFileAccessTime(file: File) {
    file.setLastModified(System.currentTimeMillis())
}

그리고, λ””μŠ€ν¬ μΊμ‹œμ˜ 크기가 MAX_DISK_CACHE_SIZE λ₯Ό μ΄ˆκ³Όν•˜λ©΄, κ°€μž₯ μ˜€λž˜μ „μ— μ ‘κ·Όν•œ νŒŒμΌλΆ€ν„° μ‚­μ œν•˜λŠ” manageDiskCacheSize() ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

private fun manageDiskCacheSize() {
    val files = cacheFolder.listFiles() ?: return
    var totalSize = files.sumOf { it.length() }
    val maxSize = MAX_DISK_CACHE_SIZE

    if (totalSize > maxSize) {
        val sortedFiles = files.sortedBy { it.lastModified() }
        for (file in sortedFiles) {
            if (totalSize <= maxSize) break
            totalSize -= file.length()
            file.delete()
        }
    }
}

νŒŒμΌμ„ 읽을 λ•Œλ§ˆλ‹€ updateFileAccessTime() ν•¨μˆ˜λ₯Ό 톡해 파일의 lastModified λ₯Ό μ—…λ°μ΄νŠΈν•˜κ³ , manageDiskCacheSize() ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬ μΊμ‹œμ˜ 크기λ₯Ό κ΄€λ¦¬ν•˜λ„λ‘ ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

이제 λ©”λͺ¨λ¦¬ κ³Ό λ””μŠ€ν¬ μΊμ‹œ λͺ¨λ‘ LRU μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜μ„ μ μš©ν•˜μ—¬ μΊμ‹œ μ˜€λ²„ν”Œλ‘œμš°λ₯Ό λ°©μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€!

마무리

μ•ˆλ“œλ‘œμ΄λ“œμ—μ„œ 캐싱을 톡해 데이터 λ‘œλ”© μ‹œκ°„μ„ 쀄이고, λ„€νŠΈμ›Œν¬ μš”μ²­μ„ 쀄여 μ„±λŠ₯을 ν–₯μƒμ‹œμΌœ μ‚¬μš©μž κ²½ν—˜ ν–₯상에 μ€‘μš”ν•œ 역할을 ν•©λ‹ˆλ‹€.

이번 κΈ€μ—μ„œλŠ” λ©”λͺ¨λ¦¬ μΊμ‹œμ™€ λ””μŠ€ν¬ μΊμ‹œλ₯Ό ν™œμš©ν•˜μ—¬ 이미지 μΊμ‹œλ₯Ό κ΅¬ν˜„ν•˜λŠ” 방법과 μΊμ‹œ ꡐ체 μ•Œκ³ λ¦¬μ¦˜μ„ μ μš©ν•˜μ—¬ μΊμ‹œ μ˜€λ²„ν”Œλ‘œμš°λ₯Ό λ°©μ§€ν•˜λŠ” 방법을 μ•Œμ•„λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

μ•ˆλ“œλ‘œμ΄λ“œ 개발자라면 Glide λ‚˜ Picasso 와 같은 이미지 λ‘œλ“œ 라이브러리λ₯Ό μ‚¬μš©ν•˜λ©΄ λ˜λŠ”λ° 직접 κ΅¬ν˜„ν•  일이 없을 것이라고 생각할 수 μžˆμŠ΅λ‹ˆλ‹€.

ν•˜μ§€λ§Œ, 이번 글을 톡해 μΊμ‹œλ₯Ό 직접 κ΅¬ν˜„ν•΄λ³΄λ©΄μ„œ μΊμ‹œμ˜ λ™μž‘ 원리λ₯Ό μ΄ν•΄ν•˜μ‹œλŠ” 것이 μΆ”ν›„ 우리의 μ„œλΉ„μŠ€μ— 효율적인 μΊμ‹œ μ „λž΅μ„ μ„ νƒν•˜κ³  μ μš©ν•˜λŠ” 데 도움이 될 κ²ƒμž…λ‹ˆλ‹€! πŸš€

κ°μ‚¬ν•©λ‹ˆλ‹€! 🌹

μ°Έκ³  λ¬Έν—Œ

https://developer.android.com/topic/performance/graphics/cache-bitmap

https://github.com/JakeWharton/DiskLruCache

About

πŸ“š μš°μ•„ν•œ 글쓰기에 μ‚¬μš©λœ 이미지 μƒ˜ν”Œμ•±λ‹ˆλ‹€!

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published
0