LIFO 구조의 AsyncTask 디스패칭 구현하기..

안드로이드에서 우측의 GridView의 썸네일들을 API 서버에서 받아온다고 가정을 합니다. API 서버를 통해서 각 썸네일을 가져오려면, 보통은 Adapter의 getView()메쏘드에서 썸네일을 가져오는 AsyncTask를 순서대로 실행을 할 것입니다. 순서대로 FIFO의 구조로.. 그렇지만, 터치를 해서 두번 정도만 아래로 내려도, FIFO 구조가 좋지 않은 것은 자명한 일리겠죠.. 화면에 보이지도 않는 썸네일을 가져오는 쓰레드가 Background로 돌고 있겠죠.. 그래서, LIFO 구조로 AsyncTask를 디스패칭하는 넘을 만들어 봤습니다.. 

많은 썸네일을 보여주기 위해서는, 보통 페이징을 합니다. 썸네일을 보려고, 빠르게 터치해서 이동하다 보면, 쓰레드가 많이 생기게 마련이고, 이런경우에 쓰레드 응답문제로 앱이 죽을 수 있습니다.. 물론, THREAD_POOLING을 이용하는 EXECUTOR를 사용한다고 해도, AsyncTask의 과도한 큐잉으로 인해서 죽을 수도 있습니다.. 

그래서, AsyncTask를 중간에 큐잉(화면에 나오지 않을 넘들까지 큐잉할 필요는 없다고 생각함)을 하고, AsyncTask를 디스패칭(엄밀히 말하면, Executor에 넘겨주는..)을 담당하는 매니저를 두는 것으로 간단하게 해결을 합니다. 디스패칭을 담당하는 클래스는 기본적으로 DEQUE를 사용해서 LIFO구조의 QUEUE를 사용합니다.. 

참고로, 구현된 코드의 일부만 발췌를 했기 때문에, 아래의 코드는 컴파일이 안됩니다.. 필요한 부분은 적당한 수정과 추가가 필요합니다..

1. AsyncTaskVO.java

public class AsyncTaskVO {

public Context c;

public AsyncTask<String, Integer, Bitmap> task;

public static AsyncTaskVO newInstance(Context c, AsyncTask<String, Integer, Bitmap> task) {

AsyncTaskVO vo = new AsyncTaskVO();

vo.c = c;

vo.task = task;

return vo;

}

}

2. AsyncTaskDispatcher.java

import java.util.ArrayList;

import java.util.concurrent.LinkedBlockingDeque;

import java.util.concurrent.atomic.AtomicInteger;

public class AsyncTaskDispatcher {

static String CNAME = AsyncTaskDispatcher.class.getSimpleName();

public static AtomicInteger runningTask = new AtomicInteger(0);

static int MAX_SIZE = 72;

static int HALF_MAX_SIZE = 36;

static LinkedBlockingDeque<AsyncTaskVO> queue = new LinkedBlockingDeque<AsyncTaskVO>(MAX_SIZE); 

static ArrayList<Thread> consumers = new ArrayList<Thread>(3);

static {

consumers.add(new Thread(new Consumer(“1”)));

consumers.add(new Thread(new Consumer(“2”)));

consumers.add(new Thread(new Consumer(“3”)));

consumers.get(0).start();

consumers.get(1).start();

consumers.get(2).start();

}

public static void put(AsyncTaskVO e) {

if(e == null)

return;

try {

queue.addFirst(e);

if(queue.size() > HALF_MAX_SIZE) {

AsyncTaskVO taskVo =queue.takeLast();

if(DEBUG)

Logger.w(CNAME, “task deleted ~~” + taskVo);

taskVo = null;

return;

}

if(DEBUG)

Logger.w(CNAME, “task added~~”);

} catch (InterruptedException e1) {

if(DEBUG)

Logger.e(CNAME, ExceptionUtil.getException(e1));

}

}

public static class Consumer implements Runnable {

private String name = “”;

final Object lock = new Object();

public Consumer(String name) {

this.name = name;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public void run() {

while(true) {

synchronized (lock) {

try {

// 실행중인 Task가 20개가 넘으면 wait을 한다. 

if(runningTask.intValue() > 20)

lock.wait(200);

if(DEBUG)

Logger.e(CNAME, “running task size = ” + runningTask.intValue());

AsyncTaskVO taskVo = queue.takeFirst();

if(taskVo == null || taskVo.c == null || taskVo.task == null) {

if(DEBUG)

Logger.e(CNAME, “TASK : NULL”);

return;

}

if(!NetworkUtil.isAvailable(taskVo.c)) {

queue.clear();

return;

}

taskVo.task.executeOnExecutor(EXECUTOR, “”);

} catch(Exception e) {

if(DEBUG)

Logger.e(CNAME, ExceptionUtil.getException(e));

}

}

}

}

}

3. GetThumbnailTask.java

public class GetThumbnailTask extends AsyncTask<String, Integer, Bitmap> {

static final String CNAME = GetThumbnailTask.class.getSimpleName();

@Override

public void onPreExecute() {

AsyncTaskDispatcher.runningTask.incrementAndGet();

}

@Override

protected Bitmap doInBackground(String… params) {

// 여기에서 Bitmap 이미지를 가져온다. 

}

@Override

protected void onPostExecute(Bitmap result) {

AsyncTaskDispatcher.runningTask.decrementAndGet();

}

}

LIFO 구조의 AsyncTask 디스패칭을 실행하는 방법은, 위의 GetThumbnailTask를 Adapter에서 생성해서 AsyncTaskDispatcher에 추가만 하면 됩니다. 

LIFO 구조의 AsyncTask 디스패칭 구현하기..”에 대한 2개의 생각

  1. 카베

    안녕하세요
    비동기식 이미지 로딩 방식을 찾다가

    이사이트를 찾게 되었고 해당 포스팅의
    도움으로 해당 부분을 구현해 보았습니다.

    그러던중 질문이 있어서 이렇게 글을 남김니다.
    AsyncTaskDispatcher.java

    static LinkedBlockingDeque queue = new LinkedBlockingDeque(MAX_SIZE);

    저의 테스트폰인 갤럭시S, SIII를 포함 대다수의 폰은 에러가 없지만
    몇몇폰에서 위의 부분을 초기화 하지 못하는 에러를 내고 있습니다.
    Caused by: java.lang.NoClassDefFoundError: java.util.concurrent.LinkedBlockingDeque

    해당 부분의 에러는 컴파일시에는 나타나지 않으면
    AsyncTaskDispatcher.put(vo) 를 호출 하면 에러를 냅니다.
    — 글 작성시기에는 몰랐지만 해당문제는 안드로이드 2.2 버전이하에서 발생합니다. 아직 해결 못했어요.—-

    인터넷을 이곳저곳 찾아봐도 해당문제에 대한 명확한 답을 찾기란
    초보자인 저에게는 매우 어려운 일이었습니다.

    혹시 해당문제를 경험하시거나 해결방법을 알고 계시다면 알려주실수 있으신가요??

    응답
    1. mcsong

      확인해 보니, API 9버전에서는 LinkedBlockingDeque를 지원하지 않네요. ^^;; BlockingDeque를 사용하시면 쉽게 해결이 되겠습니다. ^^

      응답

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.