Ringster's Techblog

Tech, Mobile, Internet

Bitmap을 UI Thread 외부에서 처리하기 (2) – ListView, GridView

leave a comment »

지난번에 포스팅한 Bitmap을 UI Thread 외부에서 처리하기 (1) (여기)에 이어서 이번에는 Bitmap을 ListView와 GridView 등에서 다루는 법을 포스팅한다.. 이 역시 Google 개발자 튜토리얼(여기)를 참조하여 작성하였고,  지난번 포스팅과 내용이 연결되므로 기존 포스팅을 먼저 읽은 후 읽기를 추천한다.


App 제작 시 흔히 쓰이는 ListView나 GridView의 경우  AsyncTask와 함께 사용 시 새로운 문제를 발생시킨다. 이들 컴포넌트들은 메모리의 효율적 사용을 위해 유저 scroll이 있을 경우 child view 들을 재사용하는데 이 때문에 각각의 child view가 AysncTask를 시작할 경우 해당 task가 종료되었을 때 그 child view가 이미 재사용되었을 수도 있고, 또한 async task가 시작된 순서대로 끝나는 것도 보장하지 않는다.

1. Dedicated Drawable subclass 생성하기

이 문제를 해결하기 위해 기존에 생성한 BitmapWorker Task로부터 reference를 저장할 수 있는 subclass를 하나 생성한다. 여기서 BitmapDrawable은 task 가 종료되기까지 출력될 임시 이미지를 보여주기 위해 사용된다.

static class AsyncDrawable extends BitmapDrawable {
    private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

    public AsyncDrawable(Resources res, Bitmap bitmap,
            BitmapWorkerTask bitmapWorkerTask) {
        super(res, bitmap);
        bitmapWorkerTaskReference =
            new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
    }

    public BitmapWorkerTask getBitmapWorkerTask() {
        return bitmapWorkerTaskReference.get();
    }
}

2. ImageView와 AsyncDrawable class binding 하기

BitmapWorkerTask를 execute하기 전에 AsyncDrawable 인스턴스를 생성하여 Bitmap을 보여줄 imageView와 묶는다

public void loadBitmap(int resId, ImageView imageView) {
    if (cancelPotentialWork(resId, imageView)) {
        final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
        final AsyncDrawable asyncDrawable =
                new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
        imageView.setImageDrawable(asyncDrawable); // imageView에 AsyncDrawable을 묶어줌
        task.execute(resId);
    }
}

cancelPotentialWork 메소드는 imageView가 이미 running task와 연관되있는지 확인한다. 만약 연결되어있다면 cancel() 메소드 호출을 통해 이전 task를 종료시킨다. cancelPotentialWork는 다음과 같다

public static boolean cancelPotentialWork(int data, ImageView imageView) {
    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

    if (bitmapWorkerTask != null) {
        final int bitmapData = bitmapWorkerTask.data;
        // If bitmapData is not yet set or it differs from the new data
        if (bitmapData == 0 || bitmapData != data) {
            // Cancel previous task
            bitmapWorkerTask.cancel(true);
        } else {
            // The same work is already in progress
            return false;
        }
    }
    // No task associated with the ImageView, or an existing task was cancelled
    return true;
}

cancelPotentialWork 메소드에서는 imageView와 연관된 task를 가져오기위해 getBitmapWorkerTask라는 help 메소드가 사용되었다. getBitmapWorkerTask 메소드는 imageView와 imageview의  drawable이 null이 아닐 경우 Workertask를 반환한다.

private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
   if (imageView != null) {
       final Drawable drawable = imageView.getDrawable();
       if (drawable instanceof AsyncDrawable) { //drawable 이 null일 경우 false 반환
           final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
           return asyncDrawable.getBitmapWorkerTask();
       }
    }
    return null;
}

3.  기존 BitmapWorker Task의 onPostExecute 업데이트 하기

마지막으로 task가 cancel되었는지 혹은 이미 imageView와 연계되어 있는지를 확인하기 위한 코드를 추가하여 onPostExecute를 업데이트 한다.

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    ...

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            final BitmapWorkerTask bitmapWorkerTask =
                    getBitmapWorkerTask(imageView);
            if (this == bitmapWorkerTask && imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}

이로써 ListView나 GridView와 같이 view를 재활용하는 컴포넌트에서도 loadBitmap 메소드가 문제없이 동작할 수 있게 되었다. 해당 컴포넌트들의 adapter 구현 시 getView() 메소드 내에서 간단히 loadBitmap을 호출하기만 하면 된다.

Advertisements

Written by Ringster

2014/11/27 , 시간: 10:14 오전

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중

Jay Jin, Programmer&Designer

Fork my brain because I'm ready to commit

쉐어보드

쉐어메이트에 관한 모든 것

jamesjungkuelee's biotech story

Biotech, entrepreneur, life

Communications as Ikor

기업 위기관리의 모든 것

Charles Pyo Ventures

시도와 실패, 성장의 기록. 2막에서도 계속되는 모험들.

VentureBeat

News About Tech, Money and Innovation

Open API, Cloud, DevOps 와 eBook

Open API, eBook, Cloud, DevOps

Economics of almost everything

Tech, Mobile, Internet

cylee

Tech, Mobile, Internet

gorekun.log

고어쿤로그

Google Developers Korea 블로그

Tech, Mobile, Internet

Android Developers Blog

Tech, Mobile, Internet

최피디의 앱스 개발기

기술, 앱스, SNS, 창업

D2 Blog

Tech, Mobile, Internet

All of Software

Tech, Mobile, Internet

'Startup's Story Platform’

'Startup's Story Platform’

%d 블로거가 이것을 좋아합니다: