Glide에 대해 알아보기 - 기본 사용법, 다운 샘플링, Target

Glide
Glide란, Android에서 추천하는 이미지 로드 라이브러리이다.
보다 쉽게 이미지를 로드 하고 더 나아가서는 이미지 캐싱 까지 해주는 편리한 라이브러리이다.
기본 사용법
fun ImageView.setImage(url: String) {
Glide.with(this)
.load(url)
.into(this)
}
기본 사용법은 매우 간단하다.
with 에 Context 또는 View를 지정한다.
load 에 불러올 이미지를 지정한다.
마지막에 into 에 불러온 이미지를 넣을 ImageView를 지정하면 끝이다.
Glide에서 제공하는 함수들
Glide는 단순히 이미지를 로드할 뿐만 아니라 여러가지 처리를 위한 함수들을 제공한다.
fun ImageView.glide(url: String) {
Glide.with(this)
.load(url)
.override(500,500)
.thumbnail(0.1f)
.placeholder(android.R.drawable.btn_plus)
.error(android.R.drawable.stat_sys_warning)
.into(this)
}
- override : 불러오는 이미지가 너무 클 경우, 이미지 로딩 속도가 지나치게 느려지거나 심하면 *OOM으로 앱이 죽어버리는 상황이 발생할 수 있다. 이때, override를 통해 이미지의 크기를 조절해서 로드할 수 있다.
- thumbnail : 썸네일로 화질이 낮은 이미지를 먼저 보여주고 원본 이미지를 보여줄 수 있다. (0.1f = 10%화질)
- placeholder : 이미지가 로딩 되기 전 표시되는 이미지 지정한다.
- error : 이미지 로드에 실패했을 때 표시되는 이미지를 지정한다.
OOM이란? -> Out Of Memory로, 안드로이드는 앱 내에서 사용할 수 있는 힙 메모리가 정해져 있기 떄문에, 메모리 부족이 발생 할 수 있다.
Listener
Glide에는 이미지 요청에 대한 리스너도 제공한다.
val listener = object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
TODO("Not yet implemented")
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
TODO("Not yet implemented")
}
}
- onLoadFailed : 로딩 실패 시
- onResourceReady : 로딩 성공 시
- TODO 대신에 data에 대한 로그 등을 찍어 보는 작업을 할 수 있다.
다운 샘플링
상단의 설명처럼 퀄리티의 이미지 등을 로딩하거나 많은 이미지를 사용할 때 OOM 에러를 만날 수 있다.
Glide는 이를 해결하기 위해 다운 샘플링을 해준다.
다운샘플링이란 Bitmap을 실제 View가 요구하는 사이즈로 줄여주는 것을 의미한다.
예를 들어 2000 x 2000 사이즈 이미지가 있고, View 사이즈는 400 x 400이라면, Glide는 다운 샘플링하여 400 x 400으로 만들고 View에 보여준다.
Glide.with(context).load(url).into(imageView);
Glide는 ImageView를 파라미터로 받기 때문에 ImageView의 사이즈를 알 수 있다.
따라서 원본 이미지 전체를 메모리에 로딩하지 않고 다운샘플링을 한 후에 메모리에 로딩한다.
이를 통해 Bitmap은 메모리를 적게 차지하고, OOM 에러를 예방할 수 있다.
참고) Glide는 이미지를 최대한 빨리 로드해주는 데 최적화되어 있기 때문에 메모리 & 디스크에 이미지를 캐싱하는 정책을 기본적으로 하고 있다. (메모리 캐싱 글에서 자세히 다름)
다운 샘플링 사이즈 결정 과정

기본적으로 글라이드는 getSize() 메서드를 통해 타깃이 제공한 크기를 요청의 타깃 크기로 사용한다.
이렇게 하면 Glide가 적절한 URL을 선택하고, 다운샘플링하고, 자르고, 적절한 이미지 크기를 변환하여 메모리 사용량을 최소화하고 최대한 빠르게 로딩할 수 있다.
View Targets
View Target 은 뷰의 속성을 검사하거나 OnPreDrawListener를 사용하여 뷰가 렌더링되기 직전에 뷰를 측정하여
getSize()를 구현한다. 따라서 Glide는 대부분의 이미지가 표시될 뷰에 맞게 자동으로 크기를 조정할 수 있다.
더 작은 이미지를 로드하면 디스크에 캐시된 이미지를 더 빠르게 로드하고 메모리를 덜 사용하며 뷰 크기가 일관된 경우 Glide의 비트맵풀 적중률을 높일 수 있다.

뷰 타겟의 로직은 4단계의 과정을 거친다. 예시 코드와 함께 설명해보겠다.
예시코드는 이해를 위해 구성한 것이므로, 실제 코드와 다를 수 있다.
1단계: 레이아웃 파라미터 확인
- XML에서 직접 지정한 크기(예: android:layout_width="200dp")가 있는지 확인
- 지정된 크기가 패딩보다 큰지 확인
- 조건 만족시 이 크기를 사용
// XML에서 설정한 layout_width, layout_height 값 확인
if (layoutParams.width > 0 && layoutParams.width > view.paddingLeft + view.paddingRight &&
layoutParams.height > 0 && layoutParams.height > view.paddingTop + view.paddingBottom) {
// 레이아웃 파라미터 크기 사용
return Size(layoutParams.width, layoutParams.height)
}
2단계: View 실제 크기 확인
if (view.width > 0 && view.width > view.paddingLeft + view.paddingRight &&
view.height > 0 && view.height > view.paddingTop + view.paddingBottom) {
// 실제 View 크기 사용
return Size(view.width, view.height)
}
- View가 이미 측정되어 실제 크기가 있는지 확인
- 측정된 크기가 패딩보다 큰지 확인
- 조건 만족시 이 크기 사용
3단계: wrap_content 처리
if (layoutParams.width == WRAP_CONTENT && layoutParams.height == WRAP_CONTENT &&
hasLayoutOccurred) {
// 경고 로그 출력
Log.w(TAG, "Wrap content is not recommended, use fixed size or SIZE_ORIGINAL")
// 화면 크기 사용
return Size(screenWidth, screenHeight)
}
- layout_width나 layout_height가 wrap_content로 설정됐는지 확인
- 최소 1회 이상의 레이아웃 측정이 있었는지 확인
- wrap_content는 권장하지 않으므로 경고 로그 출력
- 대신 Target.SIZE_ORIGINAL이나 override() 사용 권장
- 임시방편으로 Screen크기 사용
4단계: 레이아웃 대기
// match_parent, 0, wrap_content이고 아직 레이아웃 측정 안된 경우
viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
override fun onPreDraw(): Boolean {
// 레이아웃 측정 완료되면 1단계부터 다시 시작
if (view.width > 0 || view.height > 0) {
viewTreeObserver.removeOnPreDrawListener(this)
checkSize() // 1단계부터 재시작
}
return true
}
})
- match_parent나 0으로 설정된 경우
- 또는 wrap_content인데 아직 레이아웃 측정이 안된 경우
- OnPreDrawListener를 등록하여 레이아웃 측정 대기
- 측정이 완료되면 1단계부터 다시 시작
getSize()의 내부 코드를 살펴보면 다음과 같이 되어있다.

RecyclerView에서 Glide 사용 시 주의할 점

때때로 RecyclerView를 사용할 때 뷰가 재사용되어 현재 위치에 변경될 이전 위치의 크기를 유지할 수 있다.
이러한 경우를 처리하기 위해 새 ViewTarget을 생성하고 waitForLayout에 true을 전달할 수 있다:
waitForLayout은 레이아웃이 완료될 때까지 크기를 기다리고, 다시 측정하여 적절한 크기의 이미지를 로드한다.
@Override
public void onBindViewHolder(VH holder, int position) {
Glide.with(fragment)
.load(urls.get(position))
.into(new DrawableImageViewTarget(holder.imageView, /*waitForLayout=*/ true));
성능 좋은 뷰 크기

일반적으로 글라이드는 로드하는 뷰에 명시적인 dp 크기를 설정할 때 가장 빠르고 예측 가능한 결과를 제공한다.
하지만 그렇게 할 수 없는 경우 글라이드는 레이아웃 가중치, match_parent 및 OnPreDrawListeners를 사용하는
기타 상대적 크기에 대한 강력한 지원도 제공한다. 마지막으로, 다른 방법이 없다면 Glide는 wrap_content에 대해서도 합리적인 동작을 제공하려 한다.
대안

어떤 case던지 Glide가 뷰 크기를 잘못 가져오는 것 같으면 ViewTarget을 확장하고 자체 로직을 구현하거나 요청 옵션에서 override() 메서드를 사용하여 언제든지 수동으로 크기를 재정의할 수 있다.
https://bumptech.github.io/glide/doc/targets.html
Glide v4 : Targets
About Targets in Glide act as mediators between requests and requestors. Targets are responsible for displaying placeholders, loaded resources, and determining the appropriate dimensions for each request. The most frequently used Targets are ImageViewTarge
bumptech.github.io
https://velog.io/@tltty123/Android-Glide%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC
Android Glide에 대하여
Glide Glide란, Android에서 추천하는 이미지 로드 라이브러리 입니다. 이미지 로드 라이브러리는 보다 쉽게 이미지를 로드 하고 더 나아가서는 이미지 캐싱 까지 해주는 편리한 라이브러리 입니다.
velog.io