앱을 만들면서 ImageView에 사진을 띄우고자 하는 경우가 많았다.

  1. 안드로이드 앱 안의 drawable 폴더의 리소스를 보여주는 경우
  2. 안드로이드 디바이스 안에 저장되어 있는 사진을 보여주는 경우(갤러리 혹은 기타 내부 사진)
  3. 이미지 URL을 로드해서 보여주는 경우

위의 1,2번 같은 경우는 안드로이드 기기 내부의 리소스를 불러오는 작업이므로 예외사항도 적고 실제 구현도 복잡하지 않다. 하지만 3번과 같은 경우는 http 클라이언트나 서버 통신을 이용해 ImageView에 보여주어야 하는 경우이다. 나는 이 같은 경우에서 Glide라는 라이브러리를 사용했는데, 사용할 때는 사용법이 쉬웠지만, 내부 동작이나 원리가 무엇인지 몰랐다.

그래서 Glide의 내부 동작 및 원리를 공부하고 이와 비슷한 Picasso와의 비교도 해보겠다.

기본 사용방법

기본적인 사용방법은 Picasso, Glide가 거의 똑같은 방식이다.

Picasso

1
Picasso.with(context).load("https://cdn.vox-cdn.com/thumbor/97LMDBshI0KBwq2N0_NnP5_0QSE=/0x0:2040x1360/1520x1013/filters:focal(857x517:1183x843):format(webp)/cdn.vox-cdn.com/uploads/chorus_image/image/57358643/jbareham_170504_1691_0020.0.0.jpg").into(imageView)

Glide

1
Glide.with(context).load("https://cdn.vox-cdn.com/thumbor/97LMDBshI0KBwq2N0_NnP5_0QSE=/0x0:2040x1360/1520x1013/filters:focal(857x517:1183x843):format(webp)/cdn.vox-cdn.com/uploads/chorus_image/image/57358643/jbareham_170504_1691_0020.0.0.jpg").into(imageView)

다만, with() 함수에서 Picasso는 Context만을 지원하는 반면에 Glide는 Context 뿐만 아니라 다른 객체들도 사용할 수 있다. 즉, Picasso에 비해서 Glide의 with() 함수가 더 많은 인자를 받아서 사용할 수 있어서 많은 곳에서 사용할 수 있다는 장점이 있다.

Picasso
Glide

기본 Bitmap 포맷

1920x1080의 이미지를 768x432 크기의 ImageView에서 각각 로드한 경우 보여지는 이미지는 아래와 같다. 그림을 잘 보면 Glide의 이미지 화질이 Picasso보다 좋지 않음을 확인할 수 있다.

이유는 Picasso는 Bitmap 포맷은 ARGB_8888로 사용하고 Glide는 Bitmap 포맷을 RGB_565를 사용한다. RGB_565는 ARGB_8888에 비해서 화질은 떨어지지만 메모리의 용량을 50% 적게 사용한다는 이점을 가지고 있다.

아래 그림은 Picasso와 Glide의 메모리 사용량을 비교한 그래프이다. (8MB 정도는 기본적으로 애플리케이션에서 사용하는 메모리 사용량이고 그 이상이 각각 이미지로딩 라이브러리에서 사용하는 메모리 사용량이다.)

만약 메모리 용량보다 이미지의 화질이 더 중요하다고 생각한다면 Picasso를 사용하거나 Glide의 Bitmap 포맷을 ARGB_8888로 변경하여 사용할 수 있다.

이는 GlideModule을 상속받는 클래스를 하나 지정해서 기본 포맷을 ARGB_8888로 설정하고 이 GlideModule을 매니페스트에 등록시켜주면 된다.

CustomGlideModule.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SelphoneGlideModule implements GlideModule{

@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 포맷을 ARGB_8888로 변경
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);

}

@Override
public void registerComponents(Context context, Glide glide) {
glide.register(SelphoneImage.class, InputStream.class, new SelphoneGlideUrlLoader.Factory());
}

}

AndroidManifest.xml

1
2
3
4
<!-- Glide모듈을 Custom하게 변경 -->
<meta-data
android:name="kr.co.selphone.library.glide.SelphoneGlideModule"
android:value="GlideModule" />

만약 Glide의 기본 Bitmap 포맷을 ARGB_8888로 변경했다면 Glide는 이전에 비해 2배 정도 더 많은 메모리를 사용하는 것을 확인할 수 있다. 이제는 같은 Bitmap 포맷을 사용하는데도 Picasso가 Glide보다 훨씬 많은 메모리 사용량을 보이고 있다.[왜 그럴까…?]

그 이유는 Glide와 Picasso의 캐시 정책이 다르기 때문이다. Picasso는 1920x1080 크기의 원본 이미지를 메모리로 가져와서 GPU에서 실시간으로 리사이징해서 768x432의 ImageView에 할당한다.
하지만 Glide는 바로 768x432크기로 이미지를 메모리로 가져와서 ImageView에 할당시키기 때문에 메모리 사용량이 적은 것이다.

만약, Picasso에서도 Glide와 같은 방식을 취하고 싶다면 resize() 함수를 이용하면 된다.

1
2
3
4
5
Picasso
.with(context)
.load("https://cdn.vox-cdn.com/thumbor/97LMDBshI0KBwq2N0_NnP5_0QSE=/0x0:2040x1360/1520x1013/filters:focal(857x517:1183x843):format(webp)/cdn.vox-cdn.com/uploads/chorus_image/image/57358643/jbareham_170504_1691_0020.0.0.jpg")
.resize(768,432)
.into(imageView)

ImageView의 크기를 매번 적어주기 번거롭거나 알아오기 귀찮다면 fit() 함수를 사용하면 같은 효과를 볼 수 있다.

1
2
3
4
5
Picasso
.with(context)
.load("https://cdn.vox-cdn.com/thumbor/97LMDBshI0KBwq2N0_NnP5_0QSE=/0x0:2040x1360/1520x1013/filters:focal(857x517:1183x843):format(webp)/cdn.vox-cdn.com/uploads/chorus_image/image/57358643/jbareham_170504_1691_0020.0.0.jpg")
.fit()
.into(imageView)

위 방법을 사용할 경우 Picasso, Glide 모두 같은 메모리 사용량을 보일 것이다.

이미지 캐시

그 전에 캐싱은 명령어들과 데이터들을 기억장치 또는 캐시를 저장하는 디스크에 일시적으로 저장하는 기술을 말한다.

이미지를 캐시하는 방식에서도 기본적인 정책은 Glide와 Picasso가 다르다. 위의 예시처럼 1920x1080 이미지를 768x432 크기의 ImageView에 로드하는 경우 Glide는 768x432 크기의 이미를 캐시하는 반면, Picasso는 1920x1080의 원본 이미지를 캐시하게 된다.

만약 1920x1080 이미지를 다시 384x216 크기의 ImageVIew로 로드한다고 할 경우 Picasso는 이미 원본 이미지를 캐시해서 그대로 가지고 있지만 Glide는 또 하나의 384x216 크기의 이미지 파일을 캐시하게 된다.

Glide는 같은 이미지를 다른 크기의 ImageView에 로드한다는 이유로 2번의 이미지 다운로드와 리사이징 작업이 필요하게 된다.(내 생각에는 비효율적이라고 생각한다.)

만약 Glide에서 원본 크기의 이미지를 캐시하도록 설정하고 싶은 경우 아래와 같이 캐시 정책을 추가해주면 된다.

1
2
3
4
5
Glide
.with(context)
.load("https://cdn.vox-cdn.com/thumbor/97LMDBshI0KBwq2N0_NnP5_0QSE=/0x0:2040x1360/1520x1013/filters:focal(857x517:1183x843):format(webp)/cdn.vox-cdn.com/uploads/chorus_image/image/57358643/jbareham_170504_1691_0020.0.0.jpg")
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView)

위의 정책을 적용하고 난 뒤부터는 모든 Glide를 이용하여 로드할 때 원본 이미지를 가져와서 캐시하게 된다.

Glide는 이미지를 최대한 빨리 로드해주는데에 최적화 되어있기 때문에 이러한 정책을 기본적으로 적용하고 있다. 원본 이미지를 가져오는 정책과 이를 로드하는 속도는 반비례 관계에 있기 때문에 이미지 로딩 속도와 캐시에 관련해서는 원하는 방향에 맞게 설정해주면 좋다.

추가 함수 사용방법 비교

Resize

1
2
3
4
5
// Picasso
.resize(300,200)

// Glide
.override(300,200)

Center Crop

1
2
3
4
5
// Picasso
.centerCrop()

// Glide
.centerCrop()

PlaceHolder와 Error이미지

1
2
3
4
5
6
7
// Picasso
.placeHolder(R.drawable.placeholder)
.error(R.drawable.imageNot)

// Glide
.placeHolder(R.drawable.placeholder)
.error(R.drawable.imageNot)

한글이 포함된 URL처리

Picasso에서는 URL에 한글이 포함되어 있으면 ANR(Application Not Responsding)을 발생하며 앱을 종료시킨다. 그러나 Gliude는 URL에 한글이 포함되어도 아무 문제가 없다.

Glide에서만 가능한 기능

  1. GIF
    Glide에서는 ImageView에 GIF 파일을 로드할 수 있다. 하지만 GIF를 빈번하게 사용하면 많은 메모리 사용량을 보이기 때문에 적절하게 GIF를 사용해야 한다.

  2. Thumbnail 기능
    용량이 큰 이미지의 경우 thumbnail() 함수를 이용하여 우선 10% 만큼만 가져와서 흐릿하게나마 먼저 보여주고 그 다음 전체 이미지를 로드해서 사용자에게 좀더 쾌적한 UI를 제공해줄 수 있다. [%는 사용자가 지정할 수 있다.]

  3. Custom Animation
    Picasso에서는 단순이 fade in 기능의 애니메이션만을 지원하는 반면, Glide에서는 .animate(ViewPropertyAnimation.Animator)을 이용하여 여러 애니메이션을 보여줄 수 있다.

  4. Custom load
    기본적으로 Image Resource, URL, 휴대폰 내장 이미지를 로드하는 기능 외에도 Custom Model을 만들어서 이를 로드하는 기능도 지원하고 있다.

더 복잡한 내용

안드로이드에서 비트 맵 캐싱에 대한 부분은 까다롭다. 그래서 우리는 이미지의 비트맵 포맷을 편리하게 캐싱해주는 Glide를 사용한다. Glide도 비트 맵 캐싱에 관한 작업을 수행하므로 이와 관련있다.

Glide는 디스크 캐시와 메모리 캐시를 사용한다.

잠깐!

이미지 로드 라이브러리 중에서 Fresco에 대해서 간단하게 알고 넘어가자.

  • facebook에서 공개한 이미지 라이브러리.
  • 널리 알려져 있는 UIL, Glide, Picasso 와 비교하여 가장 최근에 공개되었다.
  • 다만, 아직 최근에 공개된 라이브러라서 레퍼런스가 많지 않고 Fresco의 ImageView를 사용해야한다는 것이 단점이다.

참고