Android 다중 스레드(3) AsyncTask 소스 분석(Android 7.0)
전언
전에 AsyncTask 원본 분석 글을 썼는데 잘 못 썼어요. 최근에 android 7 을 봤어요.0의 AsyncTask 소스이므로 한 편 더 쓸 예정입니다.
1. Android 3.0 버전 이전 AsyncTask
다음은 Android 2.3.7 버전의 AsyncTask 소스의 일부입니다.
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
...
}
여기서 ThreadPoolExecutor를 보았습니다. 핵심 스레드 수는 5개입니다. 스레드 탱크에서 만들 수 있는 최대 스레드 수는 128이고 비핵심 스레드가 새 작업을 기다리는 최대 시간은 10초입니다.사용된 차단 대기열은 링크드 블록Queue입니다. 용량은 10입니다.3.0 버전 이전의 AsyncTask는 스레드 탱크의 최대 스레드 수가 128이고 대기열을 막는 10개의 임무를 포함하기 때문에 AsyncTask는 최대 138개의 임무를 동시에 수용할 수 있으며 139개의 임무를 제출할 때 포화 정책을 실행하고 기본적으로 RejectedExecutionException 이상을 던집니다.
2. Android 7.0 버전의 AsyncTask
Android 7.0 버전의 AsyncTask를 예로 들면 AsyncTask의 구조 함수부터 살펴보겠습니다.
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {//1
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mFuture = new FutureTask<Result>(mWorker) {//2
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
주석 1에서 볼 때 이WorkerRunnable는Callable 인터페이스를 실현하고, 그 call 방법을 실현하였으며, call 방법에서doInBackground (mParams) 를 호출하여 작업을 처리하고 결과를 얻었으며, 최종적으로postResult를 호출하여 결과를 배달하였다.주석 2에 있는 FutureTask는 Runnable과 Futrue 두 인터페이스를 구현하는 관리 가능한 비동기식 작업입니다.따라서 Runnable와Callable를 포장하여 Executor에 제공할 수 있습니다.스레드를 호출하여 직접 실행할 수도 있습니다(FutureTask.run().여기서 WorkerRunnable은 FutureTask에 매개변수로 전달됩니다.이 두 변수는 메모리에 잠시 저장되며, 이따가 사용될 것이다.AsyncTask를 실행하려면 다음과 같이 excute 방법을 호출해야 합니다.
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
excute 메서드에서 excuteOnExecutor 메서드가 호출되었습니다.
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;//1
exec.execute(mFuture);
return this;
}
여기서는 먼저 onPreExecute 방법을 호출하여 주석 1에서 AsyncTask의 매개 변수를 WorkerRunnable에 전달합니다. 앞에서 우리는 WorkerRunnable가 매개 변수로FutureTask에 전달되는 것을 알았기 때문에 매개 변수는FutureTask에 봉인됩니다.다음에 exec의 excute 방법을 호출하고 mFuture 즉 앞에서 말한 Future Task를 전송합니다.여기 exec는 전송된 매개 변수 sDefaultExecutor입니다. 이것은 직렬 스레드 탱크입니다. 코드는 다음과 같습니다.
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {//1
public void run() {
try {
r.run();//2
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
주석 1에서 볼 수 있듯이 SerialExecutor의 execute 방법을 호출할 때 FutureTask를 mTasks에 추가합니다.작업이 완료되었거나 현재 활동이 없는 작업이 있을 때 scheduleNext 방법을 실행합니다. mTasks에서FutureTask 작업을 꺼내THREADPOOL_EXECUTOR 처리THREAD 정보POOL_EXECUTOR 는 다음에 설명합니다.여기서 보면 Serial Executor가 직렬로 실행된 것을 알 수 있다.주석 2에서 FutureTask를 실행하는run 방법을 볼 수 있으며, 최종적으로WorkerRunnable의call 방법을 호출합니다.앞에서 언급한call방법postResult방법은 결과를 배달한다.postResult방법 코드는 다음과 같다.
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
postResult 방법에서 메시지를 만들고 결과를 이 메시지에 부여합니다. getHandler 방법으로Handler를 받고 이Handler를 통해 메시지를 보냅니다. getHandler 방법은 다음과 같습니다.
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
getHandler 방법에서 인터넷Handler를 만들었는데 인터넷Handler의 정의는 다음과 같다.
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
MESSAGE 수신 중POST_RESULT 메시지가 나타나면 AsyncTask의finish 메서드가 호출됩니다.
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
AsyncTask 작업이 취소되면 onCancelled 메소드를 실행하고 그렇지 않으면 onPostExecute 메소드를 호출합니다.OnPostExecute 방법을 통해 비동기 작업이 실행된 후의 결과를 얻을 수 있습니다.이어서 뒤돌아보면 SerialExecutor입니다. 스레드 탱크 SerialExecutor는 주로 줄을 서서 임무를 직렬로 처리하는 데 사용됩니다.SerialExecutor에서 scheduleNext 방법을 호출할 때THREADPOOL_EXECUTOR.THREAD_POOL_EXECUTOR 역시 스레드 풀로서 작업을 수행합니다.
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new threadPoolExecutor (
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
THREAD_POOL_EXECUTOR는threadPoolExecutor를 가리키며 그의 핵심 스레드와 스레드 탱크가 만들 수 있는 최대 스레드 수는 모두 CPU의 핵수로 계산된다.그것이 채택한 차단 대기열은 여전히 링크드 블록Queue이며 용량은 128이다.여기까지 Android 7.0 버전의 AsyncTask의 원본 코드를 분석했습니다. AsyncTask에서 스레드 탱크를 사용했고 스레드 탱크에서 스레드를 운행하며 대기열을 막는 데 사용했기 때문에 본 장에서 앞에서 소개한 지식은 이 부분에 잘 깔려 있습니다.Android 3.0 및 이상의 버전은 SerialExecutor를 기본 라인으로 하고, 작업의 직렬 처리는 한 시간대에 하나의 작업만 수행하며, 3.0 이전 버전은 병렬 처리입니다.3.0 이전 버전의 단점은 3.0 이후 버전에도 나타나지 않는다. 라인이 하나하나 실행되기 때문에 작업 수를 초과하여 포화 전략을 실행하지 않는다.3.0 이상의 버전에서 병렬 스레드 처리를 사용하려면 다음과 같은 코드를 사용할 수 있습니다.
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
그 중에서 asyncTask는 우리가 사용자 정의한 AsyncTask이고 자바에서 제공하는 스레드 풀도 전송할 수 있다. 예를 들어CachedThreadPool도 전송할 수 있다.
asyncTask.executeOnExecutor(Executors.newCachedThreadPool(),"");
사용자 정의 스레드 풀을 가져올 수도 있습니다.
Executor exec =new ThreadPoolExecutor(0, Integer.MAX_VALUE,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
asyncTask.executeOnExecutor(exec,"");
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Java 다중 스레드를 순차적으로 실행하는 몇 가지 방법 요약Java 다중 스레드를 순차적으로 실행하는 몇 가지 방법 요약 동료는 무심결에 이 문제를 제기하고 두 가지 방법을 직접 실천했다.물론 더 좋은 방법이 있을 거야. 방법 1 이런 방법은 비교적 흔히 볼 수 있는 해결 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.