Glide로 Recyclerview 버벅임 개선하기

[Vote22] 2022/02/13 Update

RecyclerView 버벅임

  • Vote22 앱에서 유튜브 영상의 썸네일 / 타이틀 / 영상 길이 등을 RecyclerView로 보여주는 Fragment에서 스크롤 시 굉장히 많은 버벅임이 있었습니다.

  • RecyclerView에서 스크롤 시 버벅임이 발생했던 이유:

    • RecyclerView의 onBindView에서 url을 통해 이미지 bitmap을 받아오는 방식을 취했는데, 이 과정이 오래 걸리다 보니 결국 view의 생성까지 걸리는 시간이 늘었던 것이고, 버벅임이 발생했던 것으로 보입니다.
  • 임시 해결안 :

    • 15개의 영상에 대한 정보를 먼저 불러오고, 정보를 불러오는 과정에서 bitmap을 미리 다운받아 ArrayList<>에 넣어주었습니다.
    • 이를 통해서 스크롤 시의 버벅임은 해결할 수 있었지만... 이 방식은 초기 로딩에서 시간이 너무 오래 걸리고, 메모리 측면에서 단점이 너무 극명한 방식이었습니다.

Glide를 통한 로딩

  • 먼저 기존 코드(임시 해결안)는 다음과 같았습니다.
//In Adapter class
public void addItem(VideoData item) {
	  items.add(item);
	  if(item == null) return;
		Thread mThread = new Thread() {
	      @Override
	      public void run() {
	          try {
	              URL url = new URL(item.getThumbnail());
	              HttpURLConnection conn = (HttpURLConnection) url.openConnection();
	              conn.setDoInput(true);
	              conn.connect();
	              InputStream is = conn.getInputStream();
	              Bitmap bitmap = BitmapFactory.decodeStream(is);
	              bitmaps.add(bitmap);
	          } catch (MalformedURLException e) {
	              e.printStackTrace();
	              bitmaps.add(null);
	          } catch (IOException e) {
	              e.printStackTrace();
	              bitmaps.add(null);
	          }
	      }
	  };
		//thread 실행, 그 후 종료될 때 까지 메인 Thread는 기다린다
	    mThread.start();
	    try {
	        mThread.join();
	    } catch (InterruptedException e) {
	        e.printStackTrace();
	    }

addItem 발생 시에 스레드에서 이미지 url을 통해 bitmap을 받아왔고, 이를 bitmaps에 넣어주었습니다.

  • 여기서는 메인 Thread가 기다리는 방식을 취했는데, 그렇지 않으면 arraylist에 bitmap이 들어가는 순서가 뒤섞이기도 하고, 이미지가 로딩이 완료된 것을 main thread에 알리는 등 더 복잡한 방식을 취해야 했습니다. 여기서는 임시방편이었기에 그 단계는 거치지 않았습니다.
  • 대신 해결책을 찾던 중, 더 쉽게 위에서 서술한 방식을 사용할 수 있는 Glide를 찾았습니다.

Glide 사용 코드

참고 : https://bumptech.github.io/glide/int/recyclerview.html

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    VideoData item = items.get(position);
    if(getItemViewType(position) == VIEW_TYPE_ITEM) {
        VideoViewHolder viewHolder = (VideoViewHolder) holder;
        Glide.with(viewHolder.itemView)
                .load(item.getThumbnail())
                .placeholder(R.color.light_gray)
                .fallback(R.color.black)
                .error(R.color.black)
                .into(viewHolder.thumbNail);

        viewHolder.setItem(item);
}
  • load : 아이템의 썸네일 url로부터 이미지를 받아옵니다
  • placeholder : 로딩되기 전 표시될 이미지입니다. 저는 색깔로 했습니다.
  • fallback : 가져온 이미지가 null 등 알맞은 형태가 아닐 때의 예외처리입니다.
  • error : 네트워크 오류 등 이미지를 불러오는 중에 발생하는 오류에 대한 예외처리입니다.
  • into : 가져온 이미지를 넣어줄 view를 정해줍니다.

이처럼 Glide를 통해 정말 간편하게 recyclerview에 이미지 로딩을 적용할 수 있었습니다.

About Glide

참조 :

Glide에 대해 더 찾아보던 중에 관련 유튜브 영상을 찾게 되어, 공부한 내용을 간단하게 정리해 보았습니다. 아래 Caching에 대한 내용은 첫번째 링크 영상의 내용이며, 두번째 링크의 영상은 glide 라이브러리 코드의 동작 과정을 빠르게 짚어주는데, 아직 저는 이해에 어려움을 겪고 있어서 추후에 정리하려고 합니다.

Caching

  • Glide cache는 memory cache + disk cache로 구성

    • memory cache : weak references + LRU cache
  • Glide는 디폴트로 뷰에 맞게 이미지를 compress, convert 합니다.

  • Memory cache는 화면 회전 등의 상황에서 불필요하게 메모리의 데이터를 읽어오는 것을 방지합니다. cache를 통해 빠르게 대응하는 것이죠.

  • Disk Cache의 경우, 네트워크 등에서 이미 다운받아온 데이터를 불필요하게 다시 요청하는 것을 방지합니다.

  • Glide의 Memory cache에 사용되는 key는 굉장히 복잡한 방식으로 계산됩니다.

출처 : Glide github - Engine.java 파일

width, height, transformations등이 모두 key 계산에 반영됩니다. 유튜브 영상에 따르면, 같은 이미지를 2개의 이미지뷰에 로딩할 시, 그 key가 각각 다르다고 합니다.

  • What is weak reference? :

    Weakly referenced objects have a shorter lifecycle because when the JVM performs garbage collection, once a weakly referenced object is found, it will be recycled (regardless of whether the memory is sufficient)

    즉, weakly reference된 것들은 GC가 실행되면 무조건 수거된다는 것(메모리 용량이 남았는지 여부에 관계 없이) 반대로 LRU cache는 strongly referenced되어 있어, GC 실행 시에 무조건 수거되지는 않습니다. 메모리 용량이 부족할 시에만 수거되는 것이죠.

The pictures that are in use are cached by weak references, and the pictures that are not in use are cached by LRU Cache

사용중인 이미지는 weak reference로, 사용하지 않는 이미지는 LRU로 처리를 한다는 것.

  • Glide의 user는 weakly referenced cache는 끌 수 없지만, LRU cache는 끌 수 있습니다.

    • skipMemoryCache()로 가능합니다.
  • Reading the Memory Cache

    1. reads from the memory cache of the LRU Cache Algorithm mechanism
    2. reads from the memory cache of the weak reference mechanism
  • Writing to Memory Cache

    1. writes to the memory cache of the weak reference mechanism

    2. writes to the memory cache of the LRU Cache Algorithm mechanism

      when image is no longer used

  • 전체 Step

    1. Reading

      Weak reference → Lru Cache → Disk Lru Cache → Network

    2. Writing

      reading의 반대

좋은 웹페이지 즐겨찾기