저작권 안내: 저작권자표시 Yes 상업적이용 No 컨텐츠변경 No

스프링5 입문

JSP 2.3

JPA 입문

DDD Start

인프런 객체 지향 입문 강의

'이미지 URL'에 해당되는 글 1건

  1. 2013.01.31 안드로이드에서 URL 이미지를 ImageView에 보여주기 (2)

특정 URL 이미지를 가져와 ImageView에 보여주는 기능을 구현해보자. 먼저 이 기능을 구현하는데에는 아래 글에서 구현했던 기능들이 재사용된다. 그러니, 코드 중간에 설명되지 않는 내용들은 아래 글들을 읽어보면 이해가 될 것이다.

클래스 구성


URL로부터 이미지를 다운받아 ImaveView에 출력하는 기능을 구현하기 위한 클래스는 다음과 같다.

  • ImageDownloadAsyncCallback
  • ImageDownloader
    • 기본 이미지를 ImageView에 반영 후, AsyncExecutor를 이용해서 이미지 다운로드를 실행

ImageDownloadAsyncCallback 클래스


ImageDownloadAsyncCallback 클래스는 AsyncExecutor에 전달되는 콜백 객체를 구현하고 있다.


public class ImageDownloadAsyncCallback implements AsyncCallback<File>,

AsyncExecutorAware<File> {


private String url;

private WeakReference<ImageView> imageViewReference;

private AsyncExecutor<File> asyncExecutor;

private ImageCache imageCache;


public ImageDownloadAsyncCallback(String url, ImageView imageView,

ImageCache imageCache) {

this.url = url;

this.imageViewReference = new WeakReference<ImageView>(imageView);

this.imageCache = imageCache;

}


@Override

public void setAsyncExecutor(AsyncExecutor<File> asyncExecutor) {

this.asyncExecutor = asyncExecutor;

}


public boolean isSameUrl(String url2) {

return url.equals(url2);

}


@Override

public void onResult(File bitmapFile) {

Bitmap bitmap = addBitmapToCache(bitmapFile);

applyBitmapToImageView(bitmap);

}


private Bitmap addBitmapToCache(File bitmap) {

imageCache.addBitmap(url, bitmap);

return imageCache.getBitmap(url);

}


private void applyBitmapToImageView(Bitmap bitmap) {

ImageView imageView = imageViewReference.get();

if (imageView != null) {

if (isSameCallback(imageView)) {

imageView.setImageBitmap(bitmap);

imageView.setTag(null);

}

}

}


private boolean isSameCallback(ImageView imageView) {

return this == imageView.getTag();

}


public void cancel(boolean b) {

asyncExecutor.cancel(true);

}


@Override

public void exceptionOccured(Exception e) {

}


@Override

public void cancelled() {

}


}


AsyncExecutor가 파일 다운로드를 완료하면 onResult() 메서드가 호출되는데, onResult() 메서드는 전달받은 이미지 파일을 캐시에 추가하고 ImageView에 반영한다.


applyBitmapToImageView() 메서드는

  • imageViewReference로부터 ImageView를 구한다.
  • imageView가 존재한다면,
    • imageView의 콜백 객체가 this와 동일한지 확인한다.
      • 참고로, 뒤에서 살펴볼 ImageDownloader는 이미지 다운로드가 시작되면, ImageView의 tag에 관련된 콜백 객체를 설정한다.
    • 동일하다면, imageView에 이미지를 반영한다.
    • 동일하지 않다면, 이 콜백이 생성된 뒤에 동일한 ImageView에 대해 다른 URL 요청이 있었다는 것이므로 이미지를 imageView에 반영하지 않는다.

ImageDownloader 클래스


ImageDownloader 클래스의 코드 중 앞 부분은 다음과 같다.


// ImageDownloader의 앞 부분


public class ImageDownloader {

static final String TAG = "ImageDownloader";


private Context context;

private ImageCache imageCache;


public ImageDownloader(Context context, String imageCacheName) {

this.context = context;

imageCache = ImageCacheFactory.getInstance().get(imageCacheName);

}


public void download(String url, ImageView imageView, Drawable noImageDrawable) {

Bitmap bitmap = imageCache.getBitmap(url);

if (bitmap == null) {

forceDownload(url, imageView, noImageDrawable);

} else {

cancelPotentialDownload(url, imageView);

imageView.setImageBitmap(bitmap);

}

}


private void forceDownload(String url, ImageView imageView, Drawable noImageDrawable) {

if (!cancelPotentialDownload(url, imageView))

return;


imageView.setImageDrawable(noImageDrawable);

runAsyncImageDownloading(url, imageView);

}


private boolean cancelPotentialDownload(String url, ImageView imageView) {

ImageDownloadAsyncCallback asyncCallback =

(ImageDownloadAsyncCallback) imageView.getTag();

if (asyncCallback == null)

return true;

if (asyncCallback.isSameUrl(url))

return false;

asyncCallback.cancel(true);

return true;

}


ImageDownloader는 '안드로이드 이미지 캐시 구현' 글에서 만들었던 ImageCache를 이용해서 다운로드 받은 이미지 파일을 캐시하는데, 생성자를 통해서 사용할 캐시 이름을 전달받는다.


download() 메서드는 다음과 같이 세 개의 파라미터를 받는다.

  • download(String url, ImageView imageView, Drawable noImageDrawable)

첫 번째 파라미터는 이미지 URL이고, 두 번째 파라미터는 이미지를 보여줄 ImageView이다. 그리고 세 번째 noImageDrawable은 이미지를 다운로드 받는 동안에 imageView에 보여줄 이미지이다.


download 메서드는

  • 캐시에 이미지가 존재하는 지 확인한다.
  • 캐시에 존재하지 않으면 이미지 다운로드를 실행한다. (forceDownload)
  • 캐시에 존재하면 
    • cancelPotentialDownload()를 호출해서 ImageView에 대해 다른 URL을 다운로드 중이라면 작업을 취소하고,
    • 캐시에서 읽어온 이미지를 imageView에 반영한다.

forceDownload() 메서드는

  • cancelPotentialDownload() 메서드가 false를 리턴한 경우 바로 리턴한다.
    • 이 메서드가 false를 리턴하면, 같은 URL에 대해 이미 다운로드가 진행중이라는 것이다.
  • cancelPotentialDownload()가 true를 리턴한 경우
    • imageView의 이미지로 noImageDrawble를 설정한 뒤에
    • runAsyncImageDownloading()을 실행해서 이미지 다운로드를 시작한다.

cancelPotentialDownload() 메서드는 한 개의 ImageView에 대해 서로 다른 이미지를 로딩하는 것을 방지하기 위한 코드이다. 예를 들어, ListView에서 사용되는 ImageView는 재사용될 가능성이 높은데 이 경우 시간 간격을 두고 ImageView에 서로 다른 URL 이미지 다운로드가 실행될 수 있다. 이 경우 앞서 요청한 이미지는 다운로드 할 필요가 없기 때문에 다운로드를 취소함으로써 불필요한 네트워크 사용을 방지할 수 있을 것이다. cancelPotentialDownload() 메서드가 바로 이 취소작업을 처리한다.


뒤에서 살펴볼 runAsyncImageDownloading() 메서드는 이미지 다운로드를 시작하기 전에 ImageView의 tag 값으로  ImageDownloadAsyncCallback 객체를 보관하는데, cancelPottentialDownlad() 메서드는 이 tag 객체를 이용해서 ImageView와 관련된 이미지 다운로드가 진행중인 것이 있는지 확인한다. 


cancelPotentialDownload() 메서드는

  • imageView.getTag()를 이용해서 ImageDownloadAsyncCallback 객체를 구한다.
  • imageView의 tag가 null이면 ImageView와 관련된 다운로드가 진행중이지 않은 것이므로 true를 리턴한다.
  • tag에 보관된 콜백 객체의 url이 새롭게 요청한 url과 같으면 이미 동일 URL에 대한 다운로드가 진행중인 것이므로 false를 리턴한다.
  • 콜백 객체의 url이 요청 url과 다르면, 다른 이미지를 다운로드하고 있는 것이므로 작업 취소를 요청하고 false를 리턴한다.

실제로 이미지 다운로드를 시작하는 runAsyncImageDownloading() 메서드 코드 및 관련 메서드를 아래 코드에 표시하였다.


// ImageDownloader의 뒷 부분


private void runAsyncImageDownloading(String url, ImageView imageView) {

File tempFile = createTemporaryFile();

if (tempFile == null)

return;


Callable<File> callable = new FileDownloadCallable(url, tempFile);

ImageDownloadAsyncCallback callback = new ImageDownloadAsyncCallback(

url, imageView, imageCache);

imageView.setTag(callback);


new AsyncExecutor<File>().setCallable(callable).setCallback(callback).execute();

}


private File createTemporaryFile() {

try {

return File.createTempFile("image", ".tmp", context.getCacheDir());

} catch (IOException e) {

Log.e(TAG, "fail to create temp file", e);

return null;

}

}


}


runAsyncImageDownloading() 메서드는

  • 다운로드 받은 내용을 저장할 임시 파일을 생성한다. (createTemporaryFile())
  • 파일 다운로드 작업을 실행하는 FileDownloadCallable 객체를 생성한다. (이 클래스는 '안드로이드에서 URL로 파일 다운 받아 로컬에 저장하기' 글에서 작성한 것이다.)
  • 비동기 파일 다운로드가 완료될 때 사용될 ImageDownloadAsyncCallback 객체를 생성하고, ImageView의 tag 값으로 콜백 객체를 설정한다.
  • AsyncExecutor를 이용해서 비동기 파일 다운로드를 시작한다.


ImageDownloader 클래스 사용


ImageDownloader 클래스의 사용 방법은 다음과 같이 간단하다.


-- 이미지 캐시를 사용하기 때문에, onCreate() 같은 곳에서 파일캐시/이미지캐시 초기화 필요

// 2레벨 캐시(이미지 파일 캐시)를 사용하려면 동일 이름의 파일 캐시를 생성해 주어야 한다.

FileCacheFactory.getInstance().create("imagecache", cacheSize);

// 이미지 캐시 초기화

ImageCacheFactory.getInstance().createTwoLevelCache("imagecache", 40);



-- ImageDownloader가 필요한 코드에서 사용, 예를 들면 LiveView의 Adapter


public class FeaturedToonAdapter extends BaseAdapter {


private ImageDownloader imageDownloader;


public FeaturedToonAdapter(Context context) {

...

this.imageDownloader = new ImageDownloader(context, "imagecache");

}


@Override

public View getView(int position, View convertView, ViewGroup parent) {

View row = convertView;

...

BitmapDrawable noImage = new BitmapDrawable(context.getResources(), 

BitmapFactory.decodeResource(context.getResources(), R.drawable.noimage))

imageDownloader.download(imageUrl, imageView, noImage);

return row;

}



관련자료




Posted by 최범균 madvirus

댓글을 달아 주세요

  1. 안구환 2013.09.03 23:14 신고  댓글주소  수정/삭제  댓글쓰기

    ImageDownloader클래스의 download()메소드에서는 캐시 등록은 안하는 건가요?
    아니면 캐시로 등록된 이미지가 있는지 여부를 검색하고 아니면 캐시에 다운 받은 이미지를 등록하는 건가요? 혹 등록된다면 알 수 있는 방법은 무엇인지. 그리고 저장이 안된다면 어떻게 등록하는지 궁금합니다.

    ImageCache에서 putBtimap()메소드를 써야만 하나요? 이 경우 ImageCache 객체 생성시 이미지 캐시 이름 부분에서 오류가나는데.. 예로 들은 경우 처럼 "imagecache"를 사용하니 ..에러가 ㅠㅜ

    • 최범균 madvirus 2013.09.04 23:43 신고  댓글주소  수정/삭제

      ImageDownloader 클래스에서 캐시 처리를 하는데요,
      실제 캐시 기능을 제공하는 건 ImageCache 입니다.
      ImageCache를 사용하는 방법은 http://javacan.tistory.com/entry/android-image-cache-implementation 글에 나와 있으니, 이 글을 참고해 주시기 바랍니다.