IT_Programming/Android_Java

Volley 잘 쓰기

JJun ™ 2013. 8. 3. 12:52



 출처: http://blog.gae9.com/post/59371078763/volley-잘-쓰기




안녕하세요, 개9입니다. 오랜만에 기술 블로그에 글을 쓰게 되었습니다. 이번엔 안드로이드 개발에 관한 내용입니다.

개9 안드로이드 앱은 1.1.1 버전부터 HTTP 라이브러리로 Volley를 사용합니다. Volley는 안드로이드 오픈 소스 프로젝트(AOSP)의
일부로, 구글의 플레이 스토어 앱에 사용된 것으로 알려져 있습니다.Google I/O 2013 행사에서 공개됐지만 아직 참고할만한 문서가
거의 없어 개9 앱에 적용하기 위해 여러 시행착오를 겪었습니다. 그 내용을 이 글에 정리해보려 합니다.



설치

Git 저장소(https://android.googlesource.com/platform/frameworks/volley)를 클론해서 JAR 파일로 컴파일한 뒤 프로젝트에
추가합니다. 처음엔 라이브러리 프로젝트로 포함해서 썼는데, 리소스가 따로 들어있지 않으므로 JAR로 넣어도 무방합니다.



Request Queue

Volley에서 가장 중심이 되는 것이 RequestQueue 객체입니다.
간단하게는Volley.newRequestQueue(Context) 메소드로 만들 수 있는데, 그 경우 다음과 같은 기본값으로 설정됩니다.

  • 안드로이드 운영체제 버전에 맞게 HTTP 스택을 결정합니다.
    API level 9 이상에서는 HttpURLConnection을, 그 이전 버전에서는 Apache HttpClient를 사용합니다.

  • DiskBasedCache 객체를 새로 만듭니다. 내장 캐시 디렉토리 밑의 volley 디렉토리에 캐시를 저장하게 되며,
    최대 용량은 5메가로 잡힙니다.

  • 네트워크 스레드를 4개 사용합니다.


개9 앱에서는 이 기본값을 그대로 사용하지 않고 다음과 같이 직접 구성했습니다.
(Volley 클래스의 소스 코드를 보시면 어렵지 않게 수정할 수 있습니다.)

  • RequestQueue마다 별도의 캐시 객체를 가지는 것이 아니라 공통 캐시를 사용하도록 했습니다.
  • 외장 메모리가 있다면 캐시를 내장 메모리 대신 외장 메모리에 저장합니다.
  • 기본 제공된 DiskBasedCache 대신 일부 문제를 수정한 캐시를 사용했습니다. (다음 부분에서 설명할 내용)
  • 굳이 캐시가 필요 없는 API 요청의 경우 캐시를 거치지 않게 하였습니다.
  • 동시 요청 처리가 필요 없는 경우 네트워크 스레드를 1개만 만들도록 하여 메모리를 절약합니다.


또한, 중요한 요청이 아닌 경우 액티비티/프래그먼트마다 큐를 만들지 않고 공용 큐를 사용하여 메모리를 아꼈습니다. 
RequestQueue가 제대로 해제되지 않을 경우 스레드 누수가 발생하므로, DDMS를 통해서 스레드가 잘 사라지고 있는지
확인해 볼 필요가 있습니다.


개선된 DiskBasedCache

Volley에 기본 제공되는 DiskBasedCache는 비교적 간단한 구현체지만 약간 문제가 있습니다.
캐시된 오브젝트가 많아질수록 초기화하는 데 시간이 오래 걸리고, 캐시가 초기화되는 동안 요청이 하나도 처리되지 않는 문제입니다.

initialize() 메소드에서 캐시 초기화를 할 때, 캐시 디렉토리에 있는 모든 파일의 헤더를 미리 읽어오게 되어 있는데, 이 과정이 상당히
오래 걸려서 그동안 캐시 요청이 처리되지 못합니다. 또한, 캐시 객체를 여러 RequestQueue에서 공유하면 큐를 만들 때마다
initialize 메소드가 호출되고, 캐시 초기화 과정이 불필요하게 다시 실행됩니다. 게다가 initialize 메소드는 synchronized로 표시가
되어 있어 다른 캐시 요청이 처리되지 못하고 블럭되어 버립니다.

따라서 저희는 초기화 과정을 별도의 스레드에서 처리하도록 수정해서 사용하게 되었습니다.
해당 코드는 Gist에서 내려받으실 수 있습니다. (기존 코드와의 diff 보기)

Improved Volley DiskBasedCache.zip

현재 2,000명 이상의 사용자에게 배포되었으며 특별한 오류는 보고되지 않았으나, 
혹시 버그가 있을 수도 있으니 참고하고 사용하시기 바랍니다 :)

현재 AOSP에 관련 수정 사항이 리뷰 중이긴 하지만, 나중에는 DiskLruCache처럼 제대로 된 캐시 솔루션을 적용하면 좋을 것 같습니다.



이미지 로딩

레이아웃에서 ImageView 대신 NetworkImageView를 사용하고, 액티비티나 리스트 어댑터 초기화 시 ImageLoader를 만들어서 씁니다.
(자세한 내용은 Google I/O 2013 발표 자료에 잘 나와 있습니다.)

ImageLoader에는 ImageCache 객체를 넘기게 되어있습니다. 이미지 캐시는 이미 한번 불러온 이미지의 Bitmap을
캐싱해서 다시 불러오게 될 때 디코딩 시간을 절약해줍니다. Volley에서는 기본 이미지 캐시 구현을 제공하지 않아서
직접 만들어 사용하였습니다.

이미 원본 이미지는 HTTP 캐시를 통해 디스크에 저장되고 있기 때문에, 안드로이드 호환성 라이브러리에 있는 LruCache를 사용하여
메모리에만 캐시하도록 하였습니다. (BitmapCache 코드 보기)

LruCache-based BitmapCache for Volley.zip

그리고 이미지 로더는 기본적으로 응답을 100ms마다 몰아서 처리하게 되어있습니다.
이러한 배치 처리 기능을 끄면 리스트 뷰 스크롤 성능은 떨어지지만, 이미지가 빨리 불러와지는 것처럼 보이게 할 수 있습니다. 
setBatchedResponseDelay(0)을 호출하여 끕니다. (애플리케이션 특성에 맞게 적용하시기 바랍니다.)


팁: 디버깅 로그 보기

VolleyLog.DEBUG 값을 수정하면 디버깅 로그를 볼 수 있게 되어 있습니다.
어떤 과정에서 시간이 얼마나 걸렸는지 알 수 있어서 유용합니다. 다음과 같이 static initializer에서 수정할 수 있습니다.

class App extends Application {
    static {
        com.android.volley.VolleyLog.DEBUG = true;
    }
    ...
}



결론

비록 몇가지 문제점이 있고 문서가 부실하긴 하지만, Volley는 깨끗한 코드와 잘 만들어진 API를 가진 좋은 라이브러리입니다.
AOSP의 일부이기 때문에 앞으로도 발전 가능성이 높고 계속 관심을 가져볼만 하다고 생각합니다.



참고 자료





Improved Volley DiskBasedCache.zip
0.0MB
LruCache-based BitmapCache for Volley.zip
0.0MB