Android

[안드로이드]Glide에 대하여

나맘임 2023. 10. 8. 18:04

Glide

bumptech/glide: An image loading and caching library for Android focused on smooth scrolling (github.com)

 

GitHub - bumptech/glide: An image loading and caching library for Android focused on smooth scrolling

An image loading and caching library for Android focused on smooth scrolling - GitHub - bumptech/glide: An image loading and caching library for Android focused on smooth scrolling

github.com

안드로이드 이미지 로딩 라이브러리으로 이미 업계에서 성능과 신뢰성이 검증되고 확실하게 자리를 잡은 라이브러리라고 알려져있습니다.

유명한 만큼 레퍼런스 또한 많기 때문에 공부를 하기에 좋은 접근성을 가지고 있습니다.

또 다른 이미지 로딩 라이브러리로는 Coil, Fresco, Picasso 등이 있습니다.

Glide의 특징이 뭘까요?

1. Out of Memory 문제를 해결하기 위해 자동으로 다운 샘플링을 하여 디스크에 캐싱

인터넷을 통해 외부 이미지를 불러 올 때 다운 샘플링, 즉 이미지의 사이즈를 줄여서 저장합니다.

 

이 때문에 캐시에서 메모리 사용 측면에 이점이 생깁니다.

(원본 크기를 그대로 가져오지 않으므로)

 

하지만 다운 샘플링을 진행하는 과정이 존재하기 때문에

 

역으로 인터넷에서 불러올 때의 속도가 느리고,

 

이미지의 비트 수가 낮아지기 때문에 이미지 품질이 떨어진다는 특징이 존재합니다.

 

2. 빠른 이미지 로딩 속도

Glide는 기본적으로 이미지를 최대한 빨리 로드해주는 데에 집중하여 설계되어 있습니다.

 

ImageView의 크기만큼을 메모리에 캐싱을 진행하기 때문에

 

동일한 이미지를 여러 번 같은 크기로 보여줄 땐 정말 빠른 속도를 보여줍니다.

 

이에 반대로 동일한 이미지를 다양한 크기로 보여줄 땐 속도가 느리다는 특징이 있습니다.

 

또한 추가로 Activity나 Fragment의 생명주기를 알고 있기 때문에 사용자에게 보이는 Image만 빠르게 캐싱된 메모리부터 가져오게 됩니다.

 

반복되는 이미지가 많아서 캐시 데이터가 많이 필요할 때 큰 이점을 가지게 됩니다.

 

3. GIF 지원

GIF를 지원합니다.

 

Glide 캐싱 동작 과정

출처 : Best strategy to load images using Glide — Image loading library for Android | by Salmaan Ahmed | AndroidPub | Medium

 

메모리 & 디스크 정책을 사용하여 이미지를 불러오는 것을 확인할 수 있습니다.

 

Glide 기본 사용법

1. builde.gradle의 dependency 설정

implementation 'com.github.bumptech.glide:glide:4.16.0'

2. (네트워크 사용 시) AndroidManifest 설정

<uses-permission android:name="android.permission.INTERNET"/>

 

    <application ....
        android:usesCleartextTraffic="true"
    </application>

인터넷 권한과 API 28 이후로 생긴 HTTP 접근 방지를 해제시켜주는 설정입니다.

3. 사용법

기본 뼈대 메소드

Gilde.with(context)
     .load(이미지 url, 경로 등..)
     .into(표시할 뷰, 픽셀 값을 담을 객체 등...)

기본 요소로는 with, load, into가 존재합니다.

 

with은 Activity나 Fragment의 Context를 넣어주어야 하고

load는 이미지의 출처를 넣어주면 됩니다. Drawable, url, Bitmap, ByteArray 등 정말 많습니다.

into는 이미지를 보여줄 View나 픽셀 값을 담을 객체를 넣어주면 됩니다.

 

로딩 관련 처리

Gilde.with(context)
     .load(이미지 url, 경로 등..)
     .placeholder(로딩 전에 보여줄 이미지)
     .error(리소스 로딩 실패 시 보여줄 이미지)
     .fallback(이미지 값이 null 일 때 보여줄 이미지)
     .into(표시할 뷰, 픽셀 값을 담을 객체 등...)

placeholder는 로딩 전에 보여줄 이미지를 

error는 리소스 로딩 실패 시에 보여줄 이미지를

fallback은 리소스의 값이 null일 때 보여줄 이미지를 표시할 수 있도록 하는 메소드입니다.

이미지 테두리를 둥글게 하기 

Gilde.with(context)
     .load(이미지 url, 경로 등..)
     .apply(
     	RequestOptions().transform(RoundedCorners(30)) // 단위는 dp
     )
     .into(표시할 뷰, 픽셀 값을 담을 객체 등...)

GIF 이미지 로딩

Gilde.with(context)
     .load(이미지 url, 경로 등..)
     .asGif()
     .into(표시할 뷰, 픽셀 값을 담을 객체 등...)

단순히 asGif()를 추가하면 GIF 이미지를 로딩할 수 있습니다.

GIF 이미지 반복 1번만 하게 하기

        Glide.with(applicationContext)
            .asGif()
            .load(이미지 경로)
            .listener(object : RequestListener<GifDrawable>{
                override fun onLoadFailed(
                    e: GlideException?,
                    model: Any?,
                    target: Target<GifDrawable>,
                    isFirstResource: Boolean
                ): Boolean {
                    return false
                }

                override fun onResourceReady(
                    resource: GifDrawable,
                    model: Any,
                    target: Target<GifDrawable>?,
                    dataSource: DataSource,
                    isFirstResource: Boolean
                ): Boolean {
                    resource?.setLoopCount(1) //루프 카운트를 1로 설정
                    return true
                }

            })
            .into(뷰)

리사이클러뷰에 Glide 적용 예시

class ImageDataAdapter(private val context: Context) : RecyclerView.Adapter<ImageDataAdapter.ViewHolder>() {

    private val imageDataList = mutableListOf<ImageData>()

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val image: ImageView = itemView.findViewById(R.id.image)
        val title: TextView = itemView.findViewById(R.id.title)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val convertView =
            LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
        return ViewHolder(convertView)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val imageData = imageDataList[position]

        holder.apply {
            Glide.with(context).load(imageData.imageUrl)
                .override(150, 150)
                .into(image)
            title.text = imageData.imageTitle
        }
    }

    override fun getItemCount() = imageDataList.size

    fun add(imageData: ImageData) {
        imageDataList.add(imageData)

        GlobalScope.launch(Main) {
            notifyItemInserted(imageDataList.size)
        }
    }
}

리사이클러뷰에 Glide를 적용한 예시입니다

 

데이터를 바인딩하는 onBindViewHolder() 메소드에서 glide를 진행하면 됩니다.

 

리사이클러뷰에 Glide Preload 적용 예시

하지만 위와 같이 적용 시에 리스트를 밑으로 내리면 이미지 로딩이 걸려 안보이는 현상이 생깁니다.

 

이는 bindViewHolder()가 새로운 item을 불러오는데에는 네트워크 접속이 필요하기 때문에 시간이 걸려 발생합니다.

 

그렇기 때문에 이미지 로딩 딜레이를 줄이는 방법 중에 하나가 cache를 이용하는 것입니다.

 

다양한 방법이 존재하겠지만

 

Glide에선 이를 해결할 수 있도록 preload 를 지원합니다.

fun preload(context: Context,  url : String) {
    Glide.with(context).load(url)
        .preload(150, 150)
}

위와 같이 preload 메소드를 미리 만들어놓고

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val imageData = imageDataList[position]

    holder.apply {
        Glide.with(context).load(imageData.imageUrl)
            .override(150, 150)
            .into(image)
        title.text = imageData.imageTitle
    }

    //Preload
    if (position <= imageDataList.size) {
        val endPosition = if (position + 6 > imageDataList.size) {
            imageDataList.size
        } else {
            position + 6
        }
        imageDataList.subList(position, endPosition ).map { it.imageUrl }.forEach {
            preload(context, it)
        }
    }
}

위와 같은 코드로 화면을 내릴 때 마다 다음 아이템의 미리 데이터를 로드합니다.