앱에서 이미지로더로 picasso 를 사용하고 있다. 얼마 전 새로운 요구사항이 추가되었다. 모델에서 이미지의 source를 로컬 리소스를 사용할 건지, 웹 url을 사용할 건지 분기를 하여 처리해야 한다. 이 부분은 간단히 만들 수 있다. 대강 이렇게 구현하면 된다.
sealed class ImageSource {
class RemoteImage(val url:String) : ImageSource()
class LocalResourceImage(@DrawableRes val resId: Int) : ImageSource()
}
when( source) {
is RemoteImage -> picasso.load(source.url)
is LocalResourceImage -> picasso.load(source.resId)
}
.into( imageView)
다 된 줄 알았는데 VectorDrawble이 제대로 보이지 않는다는 제보가 들어왔다. 하지만 Jake Wharton 형님은 안해주신다고 하였다.
Comment for #1109
No plans. Vectors, by definition, don't really need an image processing pipeline to change their size. Why are you wanting to use Picasso for this?
궁금해서 왜 vector drawable일 경우 실패하나 찾아보니 ResourceRequestHandler
에서 BitmapFactory.decodeResource
를 통해 bitmap 을 가져오는데, vector drawable일 경우 여기서 null이 떨어져버린다.
private static Bitmap decodeResource(Resources resources, int id, Request data) {
final BitmapFactory.Options options = createBitmapOptions(data);
if (requiresInSampleSize(options)) {
BitmapFactory.decodeResource(resources, id, options);
calculateInSampleSize(data.targetWidth, data.targetHeight, options, data);
}
return BitmapFactory.decodeResource(resources, id, options);
}
여기까지 확인해서 picasso 의 load()
로는 vector drawable을 읽어들일 수 없다고 결론을 내렸다.
이 경우 취할 수 있는 판단은 아래 2가지 일 것이다.
- 어떻게든 picasso 맥락 안에서 해결한다.
- picasso 에서 해결할 수 없는 건 picasso 맥락을 벗어나 해결한다.
이 경우 1번으로 할 여지가 거의 없어 보인다. 막 억지로 transform 이나 callback을 집어넣으면 유지보수 측면에선 오히려 더 해롭다. 그래서 picasso 맥락 안에서 해결하는 건 포기하고 아래와 같이 수정했다. 내 경우 local resource일 경우 transform 등의 후처리가 전혀 필요하지 않았기 때문에 간단했다.
when( source) {
is RemoteImage -> { //원격 이미지는 picasso로 처리
picasso.load(source.url)
.into( imageView)
}
is LocalResourceImage -> { //로컬 이미지는 그냥 setImageDrawble로 처리
imageView.setDrawable( context.resource.getDrawble( source.resId) )
}
}
잘 동작하는 것 같다. 하지만 RecyclerView
와 같이 뷰를 재사용하는 경우 문제가 된다. 왜 문제가 되는지 3초 동안 생각해보시오.
...
RecyclerView
를 빠르게 스크롤 한 경우, 원격 이미지를 picasso 가 비동기로 불러오는 와중에 로컬 리소스가 setDrawble()
된 경우가 발생할 수 있다. 이러면 로컬 리소스 이미지가 보여야 할 자리에 뜬금없는 원격 이미지가 뜰 수 있다. picasso 맥락 안에서 해결했으면 이런 일이 없었을 것이다. 이 경우 다행히 picasso 는 cancelRequest()
라는 메서드를 제공하기 때문에, ImageView
를 대상으로 동작중인 비동기 작업을 취소하면 된다.
when( source) {
is RemoteImage -> { //원격 이미지는 picasso로 처리
picasso.load(source.url)
.into( imageView)
}
is LocalResourceImage -> { //로컬 이미지는 그냥 setImageDrawble로 처리
picasso.cancelRequest(imageView)
imageView.setDrawable( context.resource.getDrawble( source.resId) )
}
}
이런 이슈는 비단 picasso 뿐 아니라 다른 프레임워크를 사용할 때도 쉽게 접할 수 있는 문제이다. 따라서 프레임워크 맥락에서 해결할 수 있는 문제는 어지간하면 프레임워크 안에서 해결하는게 대게는 좋은 선택지라고 생각한다.
Top comments (0)