brunch

You can make anything
by writing

C.S.Lewis

by 서준수 Dec 21. 2021

안드로이드 Glide

Glide란?

Bump Technologies에서 개발한 안드로이드용 이미지 로딩 라이브러리이다. 현재는 구글이 개발사를 인수하였다. 사용법이 간단하고 성능이 좋다는 것이 큰 장점이다.

Version : glide:4.12.0, glide:compiler:4.12.0
OS : Windows 11
IDE : Android Studio
Target : Android
Code : GitHub


왜 사용해야 하나?

안드로이드에서 이미지를 보여주기 위해서 ImageView를 사용한다. 여기에 보여줄 이미지 리소스는 다양한 방식으로 획득할 수 있다. 크게 리소스 위치 관점으로 나뉘어 보면 다음과 같다.


[로컬]

- 앱 자체적으로 보유한 이미지 로드 (drawable 폴더 내 이미지 파일)

- 사용자가 다운로드한 이미지나 촬영한 사진 파일 로드 (단말기에 저장되어 있는 이미지 파일)

[네트워크]

- 서버에 있는 이미지 로드 (이미지 URL)


로컬에 있는 이미지를 가져오는 것은 간단하지만 네트워크에서 가져올 때는 그렇지 않다. 보통 HTTP 통신을 통해서 이미지를 가져오는데 로컬에 비하면 당연히 불안정하다. 그로 인해 이미지를 가져오지 못했을 때 어떻게 처리할 것인지 재시도 정책은 어떻게 수립할 것인지 등 고려할 점이 늘어난다. 이미지를 제대로 가져왔다면 자원 낭비를 막기 위한 재사용과 이미지 캐싱 정책, 디코드, 메모리 관리 등이 필요하다.


이러한 사항 외에도 고려할 문제가 많고 그것을 모두 구현하는 것은 쉽지 않은 일이다. 그 어렵고 복잡한 일을 대신 고민해서 구현해 준 것이 바로 Glide이다. (이것은 여타 다른 이미지 로딩 라이브러리도 마찬가지일 것이다.) 심지어 Glide는 사용법이 간단하고 성능이 뛰어나며 다양한 기능을 지원한다. 따라서 사용을 안 할 수가 없다.


어떻게 사용하나?

1. Gradle에 dependency 추가

implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'

2. 네트워크 이미지를 사용한다면 인터넷 권한 추가

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

3. 기본 사용법

Glide.with(context)
          .load(가져올 이미지)
          .into(이미지를 보여줄 View)

with, load, into는 Glide의 가장 기본이 되는 함수이다. 이 함수들만 있으면 이미지 로딩이 가능하다.


with의 파라미터에는 View, Context, Activity, FragmentActivity를 넘길 수 있다. load의 파라미터에는 가져올 이미지 타입을 지정한다. 네트워크 이미지를 가져오기 위해서 이미지 URL을 지정할 수 있다. 또한 Drawable이나 Bitmap, File 등을 지정할 수도 있다. 그 외에도 여러 타입을 지원한다. into의 파라미터는 load에서 가져온 이미지를 보여줄 View를 지정하면 된다.


3. 주요 함수

Glide.with(context)
          .load(가져올 이미지)
          .placeholder(미리 보여줄 이미지)
          .fallback(이미지 URL null인 경우 보여줄 이미지)
          .error(에러 시 보여줄 이미지)
          .thumbnail(비율 또는 저해상도 이미지 강제 로드)
          .into(이미지를 보여줄 View)

- placeholder() : 이미지를 가져오는 동안 보여줄 이미지를 지정한다. 요청한 이미지를 정상적으로 가져오면 해당 이미지로 대체되고 그렇지 않으면 여기서 설정한 이미지가 계속 표시된다.

- fallback() : 이미지 URL 또는 모델이 null인 경우 표시할 대체 이미지를 지정한다. 어떤 등록된 이미지가 없을 때 기본값을 설정해 주는 용도로 사용 가능하다. 이 처리를 하지 않으면 error()에서 처리한다.

- error() : 이미지 가져오기 실패 시 보여줄 이미지를 지정한다. fallback()을 설정하지 않았을 경우에도 여기서 처리한 이미지가 표시된다.

- thumbnail() : 지정한 비율만큼만 이미지를 미리 가져와서 보여주거나 저해상도 이미지를 강제로 표시한다. 파라미터에 0.3f로 지정하면 30% 비율로 가져와서 흐릿하게 보여준다. 기본 요청과 병렬적으로 요청이 된다. 만약 기본 요청이 섬네일 요청보다 빨리 처리되면 당연하게도 네일 이미지는 보이지 않는다.


placeholder(), fallback(), error()는 결론적으로는 모두 대체 이미지를 표시해 주는 역할을 한다. 다만 상황에 맞게 좀 더 세부적으로 선택하여 사용할 수 있다.


Glide.with(context)
          .asGif()
          .load(가져올 이미지)
          .into(이미지를 보여줄 View)

- asGif() : 리소스를 항상 GifDrawable 타입으로 가져오도록 한다. 일반적으로 asDrawable()로만으로 충분하다. asDrawable()가 gif 여부와 관계없이 적절한 Drawable을 자동으로 반환해 주기 때문이다. 그리고 load()를 할 때 asDrawable().load()를 반환하기 때문에 asDrawable()를 별도로 선언하려고 신경 쓰지 않아도 된다.

Glide.with(context)
          .load(getDrawable(R.drawable.gifFile))
          .into(이미지를 보여줄 View)

위와 같이 해도 gifFile이 잘 동작한다는 의미이다.


이것만으로 모든 고려 사항이 해결되는 것은 아니지만 많은 것들을 고려하지 않아도 된다.


예제를 보자!

예제는 Retrofit2에서 사용한 코드를 기반으로 한다. 해당 예제에 Databinding를 참고하여 Databinding을 살짝 끼얹어준다.


이미지를 가져오는 부분은 loadData() 함수이므로 해당 함수만 살펴본다. 아래는 기존 예제에서 사용한 코드이다.

Databinding이 적용되어 있지 않기 때문에 Handler를 통해서 TextView와 ImageView를 갱신하고 있다. Glide를 사용하면 into() 함수로 ImageView에 바로 반영할 수 있다. 이것보다 더 큰 문제는 이미지를 가져와서 디코딩하는 부분이다. 해당 작업은 Thread에서 진행되는데 이미지 URL에 연결하여 받아온 데이터를 Bitmap으로 디코딩한다. 아주 단순하게 구현된 코드라 자칫 Bitmap이 null인 경우 무한루프에 빠질 수 있는 위험한 상태이다. 하지만 Glide를 쓰면 이런 문제들에 대한 고민을 덜어준다.


다음은 Databinding과 Glide를 적용한 코드이다.

Handler 대신에 binding.invalidateAll()을 통해서 연결된 TextView에 데이터의 변경을 알려주어 갱신하도록 했다. Glide를 사용하여 불안정했던 Thread가 제거되었다. 디코딩도 Glide가 알아서 해주기 때문에 별도의 코드가 필요 없고 ImageView에 Bitmap을 직접 할당하는 부분도 into()로 간결하게 정리되었다.


주요 함수들도 다음과 같이 추가해서 사용할 수 있다.

Glide의 목적이 이미지를 가능한 빠르고 부드럽게 가져오는 것이다. 주요 함수들이 이에 부합하는 역할을 한다. thumbnail로 일부를 빠르게 가져오는 것도 그렇고 그에 앞서 placeholder로 빈 화면이 아닌 특정 이미지를 보여주기도 한다. 이렇게 하면 전체적으로 심리스 하게 보이는 효과가 있을 것으로 기대된다.


참고로 error 상황을 보고 싶다면 위 예제에 .asGif()를 추가하면 된다. 받아올 파일이 gif 형식이 아니라서 에러가 발생하여 error()에서 지정한 이미지가 표시되는 것을 확인할 수 있다.


캐싱 정책

diskCacheStrategy()를 사용하여 캐싱 정책을 지정할 수 있다. 기본값은 AUTOMATIC이다.

Glide.with(context)
          .load(가져올 이미지)
          .diskCacheStrategy(DiskCacheStrategy.ALL)
          .into(이미지를 보여줄 View)

DiskCacheStrategy.ALL : 원본 데이터와 디코딩 한 리소스 모두 캐시

DiskCacheStrategy.AUTOMATIC : 로컬 또는 네트워크 이미지에 따라 최적화된 전략 사용

DiskCacheStrategy.DATA : 원본 데이터를 디코딩하지 않고 캐시

DiskCacheStrategy.NONE : 디스크 캐시 사용하지 않음

DiskCacheStrategy.RESOURCE : 원본 데이터를 디코딩 후 캐시


기본값을 사용할 때는 좀 더 효율적인 방식을 선택하여 동작한다. 예를 들어 네트워크 이미지라면 URL을 통해서 이미지를 가져오는 비용이 이미 디스크에 있는 데이터 크기를 조절하는 것보다 크기 때문에 원본 데이터만 저장한다. 그러면 필요한 크기를 다시 받아오지 않고 원본 데이터를 기반으로 만들어서 쓰면 되기 때문이다. 로컬 이미지라면 반대로 원본 데이터를 검색하는 비용이 크기를 조절하는 것보다 저렴하기 때문에 크기 조절된 이미지(섬네일)만 저장한다.


재사용

Glide에서는 참조 횟수를 카운팅 하여 리소스 재사용 유무를 결정한다. 어떤 리소스를 into() 함수를 통해서 가져오면 참조 횟수는 1씩 증가한다. 만약 같은 리소스가 into(imageView1), into(imageView2)와 같이 두 번 사용되면 참조 회수는 2가 된다. 참조 횟수가 감소하는 경우는 해당 리소스를 보여주고 있는 View에서 clear()를 호출하거나 새로운 리소스가 그 View에 불려지는 경우이다.


참조 횟수가 0이 되면 리소스가 해제되고 재사용을 위해서 Glide로 반환된다. 이렇게 반환된 경우는 해당 리소스를 재사용하는 것이 안전하지 않다. 예를 들면 imageView.drawable를 가져와 다른 View에 표시하려고 할 때 imageView에서 보여주던 리소스가 이미 해제된 경우 원하던 결과를 기대할 수 없다. Glide로 반환된 리소스는 Pool에 저장되지만 새로운 이미지를 위한 Pool이 필요할 경우 제거되기 때문이다.


예제 실행 결과는 외적으로는 retrofit2와 다를 게 없다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari