코딩하는 개굴이

[안드로이드] Coil 라이브러리로 이미지 로딩하기 본문

안드로이드/KOTLIN

[안드로이드] Coil 라이브러리로 이미지 로딩하기

개굴이모자 2023. 1. 8. 22:35
반응형

Picasso 를 사용하다가 Coil 라이브러리로 마이그레이션을 하기로 함에 따라

Coil 의 사용법 및 에러 핸들링 관련하여 포스팅을 해보려한다.

 

 

 

Coil 이란?

coil 은 Kotlin Coroutin 으로 만들어진 Android 백엔드 이미지 로딩 라이브러리이다.

coil 외에 url 및 File path 등의 이미지를 로딩하기 위한 라이브러리는 Picasso, Glide 등 다수 존재하지만, 본인은 Coil 을 적용해보려한다.

Migration 및 적용하기

implementation("io.coil-kt:coil:2.2.2")
implementation("io.coil-kt:coil-gif:2.2.2") //coil 에서 gif 적용을 위한 implementation 으로 선택사항

기존의 31 이었던 targetSdkVersion 을 32 이상으로 올려야했다.

이렇게 세팅했다면, 적용하는 부분은 아래와 같이 간단하다.

// URL
imageView.load("<https://www.example.com/image.jpg>")

// File
imageView.load(File("/path/to/image.jpg"))

그러나, 경우에 따라 place holder 를 필요로 하거나, bitmap 을 불러와야하는 경우 등 load() 만으로 구현이 되지 않는 경우들이 존재할 수 있다.

그럴 경우, 아래와 같이 imageLoader 와 imageRequest 를 사용해 구현되어야한다.

//Picasso
//bitmap 을 가져오는 형태
sampleBitmap = Picasso.get()
                        .load(Uri.parse(uriString))
                        .resize(DEFINED_SIGNATURE_WIDTH, DEFINED_SIGNATURE_HEIGHT)
                        .get()

//Coil
//bitmap 을 가져오는 형태
//imageLoader 는 singleton 으로 선언되어있다
val imageRequest = ImageRequest.Builder(getApplication<Application>().applicationContext)
                            .data(uriString)
                            .allowHardware(false) //하위 에러 핸들링을 위한 옵셔널 세팅
                            .build()
val bitmap = imageLoader.execute(imageRequest).drawable?.toBitmap()

ImageLoader

single {
        ImageLoader.Builder(androidApplication())
            .components { //gif 디코더 등록을 위한 코드로 생략 가능
                if (Build.VERSION.SDK_INT >= 28) {
                    add(ImageDecoderDecoder.Factory())
                } else {
                    add(GifDecoder.Factory())
                }
            }
            .diskCache { //cache 처리를 위한 코드로 생략 가능
                DiskCache.Builder()
                    .directory(androidApplication().cacheDir.resolve("image_cache"))
                    .build()
            }
            .build()
    }

ImageLoader 는 coil 의 가이드 상, singleton 으로 관리될 때 최적의 효율을 보장하므로 이를 권장하고 있다.

 

 

Coil 을 이용한 Gif 불러오기 (feat. placeholder)

gif 를 placeholder 로 사용하고자 coil 을 이용해 gif 파일을 불러보려한다.

우선, 대상이 되는 gif 파일을 res 하위의 Android Resource Directory 중, raw 파일 하위에 추가한다. (없다면 추가해야한다.)

그리고, 아래와 같이 imageRequest 의 data 에 raw 리소스를 단순히 불러오기만 하면 간단히 구현 가능하다.

viewModelScope.launch {
            val imgRequest = ImageRequest.Builder(getApplication<Application>().applicationContext)
                .data(R.raw.loading)
                .listener { _, result -> //결과에 다른 처리
                    loadingDrawable = result.drawable
                }
                .build()

            imageLoader.execute(imgRequest)
        }
....

val imgRequest = ImageRequest.Builder(getApplication<Application>().applicationContext)
            .data(url)
            .diskCacheKey(randomKey.toString())
            .target(imageView)
            .placeholder(loadingDrawable) //불러온 gif 를 placeholder 로 지정
            .listener { _, result ->
                imageView.setImageDrawable(result.drawable)
            }
            .build()
        imageLoader.enqueue(imgRequest)

그리고 불러온 gif 를 Placeholder 로 지정한다.

 

 

에러 핸들링

Software Rendering dosn’t support hardware bitmaps

이제 다 되었다 라고 생각할 때 즈음, 런타임 에러가 발생하였다.

내용은 아래와 같았는데, Software rendering 이 hardware rendering 을 지원하지 않는다는 것으로, 에러는 imageview 에 setImageBitmap 하는 부분에서 발생하였다.

FATAL EXCEPTION: main
Process: com.peopleofandroido.readpictures, PID: 16644
java.lang.IllegalArgumentException: Software rendering doesn't support hardware bitmaps
at android.graphics.BaseCanvas.onHwBitmapInSwMode(BaseCanvas.java:742)
at android.graphics.BaseCanvas.throwIfHwBitmapInSwMode(BaseCanvas.java:749)
at android.graphics.BaseCanvas.throwIfCannotDraw(BaseCanvas.java:94)
at android.graphics.BaseCanvas.drawBitmap(BaseCanvas.java:134)
at android.graphics.Canvas.drawBitmap(Canvas.java:1557)
at com.peopleofandroido.readpictures.presentation.viewmodel.PictureEditorViewModel.processFinalBitmap(PictureEditorViewModel.kt:385)
at com.peopleofandroido.readpictures.presentation.viewmodel.PictureEditorViewModel$setImageViewBitmap$1$1.onBitmapReady(PictureEditorViewModel.kt:357)
at ja.burhanrashid52.photoeditor.PhotoSaverTask.handleBitmapCallback(PhotoSaverTask.java:145)
at ja.burhanrashid52.photoeditor.PhotoSaverTask.onPostExecute(PhotoSaverTask.java:112)
at ja.burhanrashid52.photoeditor.PhotoSaverTask.onPostExecute(PhotoSaverTask.java:23)
at android.os.AsyncTask.finish(AsyncTask.java:771)
at android.os.AsyncTask.access$900(AsyncTask.java:199)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8855)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)

발생한 원인은 Coil 에서 Bitmap 을 가져오는 과정에서 hardware rendering 이 된 것으로 보였다.

따라서, ImageRequestBuilder 의 옵션에 allowHardware 를 false 로 세팅하였으며, 이후 발생하지 않았다.

val imageLoader =ImageLoader(getApplication<Application>().applicationContext)
val imageRequest =
    ImageRequest.Builder(getApplication<Application>().applicationContext)
        .data(dataSignatureImageUriString)
        .allowHardware(false) //false 로 세팅
        .build()
val bitmap = imageLoader.execute(imageRequest).drawable?.toBitmap()

 

 

참고 링크

https://coil-kt.github.io/coil/recipes/#shared-element-transitions

https://coil-kt.github.io/coil/image_requests/

https://coil-kt.github.io/coil/gifs/

https://notepad96.tistory.com/177

https://eso0609.tistory.com/66

반응형
Comments