Android 비동기 메시지 처리 메커니즘 (4) AsyncTask 소스 코드 분석
1. AsyncTask 를 실례 화하 고 호출
execute(Params... params)
은 어떤 작업 을 완 성 했 습 니까?우 리 는 실례 화 대상 을 알 았 을 때 먼저 부모 류 의 구조 기 를 호출 합 니 다. 그러면 우 리 는 먼저 AsyncTask 의 구조 기 와 관련 된 내용 과 관련 된 소스 코드 를 살 펴 보 겠 습 니 다.
// AsyncTask , Callable( call ),
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
// AsyncTask , UI
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
//
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask<Result>(mWorker) {
@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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
우 리 는 AsyncTask 구조 기 가 주로 두 대상 을 초기 화 하 는 것 을 보 았 다. mWorker 와 mFuture.mWorker 는 실제 적 으로 Callable 대상 이 고 구성원 변수 인 mParams (첫 번 째 일반적인 매개 변수 배열 유형) 를 가지 고 있 습 니 다. 우 리 는 Callable 인터페이스 가 실현 하 는 스 레 드 에 반환 값 이 있다 는 것 을 알 고 있 습 니 다. 즉,
call()
방법의 반환 값 postResult(doInBackground(mParams))
을 호출 했 습 니 다. 여 기 는 doInBackground(mParams)
방법 이 아직 실행 되 지 않 았 습 니 다.mFuture 는 Future Task 대상 으로 mWorker 를 사용 하여 초기 화 합 니 다.Future Task 는 Future 인터페이스 와 Runnable 인터페이스의 실현 클래스 로 관련 된 callable 대상 인 mWorker 를 제어 할 수 있 으 며 이 대상 의 어떠한 방법 도 실행 되 지 않 았 습 니 다.AsyncTask 류 를 예화 한 후에 우 리 는 보통
call()
방법 을 호출 합 니 다. 그러면 자 연 스 럽 게 execute(Params... params)
관련 소스 코드 를 봐 야 합 니 다.
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
// execute
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;
// mFuture
exec.execute(mFuture);
return this;
}
`execute(Params... params)
직접 호출 execute()
. 이 방법 은 첫 번 째 매개 변 수 는 SerialExecutor 대상 sDefaultExecutor 이 고 두 번 째 매개 변 수 는 우리 가 정의 한 Params 입 니 다. 그러면 이 방법 을 상세 하 게 분석 해 보 겠 습 니 다.먼저 임 무 를 처음 수행 하 는 지 여 부 를 판단 하고 첫 번 째 가 아니라면 이상 을 던 집 니 다 (케이스 블록 에 break 문 이 없 음 을 주의 하 십시오).그렇지 않 으 면 아래로 실 행 될 것 이다.먼저 작업 을 실행 상태 로 표시 합 니 다.이 어 우리 가 덮어 쓰 는 executeOnExecutor(Executor exec,Params... params)
방법 을 호출 했다. 이것 은 왜 이것 이 첫 번 째 로 실행 을 시작 하 는 방법 인지 설명 한다. 또한 우 리 는 onPreExecute()
UI 를 초기 화 하 는 데 자주 사용 되 는 지 알 고 있다. 반드시 메 인 스 레 드 에서 호출 해 야 하기 때문에 onPreExecute()
메 인 스 레 드 에서 호출 해 야 한다.그리고 우 리 는 현재 여전히 메 인 스 레 드 에 있 음 을 관찰 했다.마지막 으로 SerialExecutor 대상 sDefaultExecutor execute()
를 실행 합 니 다.그렇다면 이 방법 은 또 어떤 일 을 할 것 인가?우 리 는 이 방법 에 들 어가 서 탐구 하 자마자
private static class SerialExecutor implements Executor {
// AsyncTask
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
// mFuture mWorker call()
r.run();
} finally {
// ,
scheduleNext();
}
}
});
// AsyncTask ,
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
정적 내부 클래스 SerialExecutor
execute(mFuture)
방법 은 다음 과 같은 내용 을 순서대로 수행 합 니 다. 먼저 mTask execute()
를 호출 하여 Runnable 인 스 턴 스 를 추가 하고 이 인 스 턴 스 offer()
방법 에서 전 달 된 매개 변수 mFuture run()
방법 을 호출 하 는 동시에 finally 블록 에서 실행 run()
합 니 다. 이 단 계 는 대기 열 mTask 에 Runnable 인 스 턴 스 를 추가 합 니 다.이 어 mActive 가 null 인지 아 닌 지 를 판단 하고 첫 번 째 는 당연히 null 이 므 로 실행 scheduleNext()
합 니 다.scheduleNext()
역시 SerialExecutor 클래스 의 방법 입 니 다. 이 방법 은 실 행 됩 니 다 scheduleNext()
. 그 중에서 mActive 는 대기 열 에서 꺼 낸 Runnable 대상 입 니 다.THREAD_POOL_EXECUTOR 는 스 레 드 탱크 대상 으로 스 레 드 탱크 의 스 레 드 를 사용 하여 mActive 를 실행 합 니 다.mActive 에서 방금 넣 은 mFuture THREAD_POOL_EXECUTOR.execute(mActive)
방법 을 실행 합 니 다. 이 방법의 코드 는 다음 과 같 습 니 다.
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// call()
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
이 방법 은 실제로 Callable 대상
run()
을 호출 하 는데 이 Callable 대상 은 무엇 일 까?AsyncTask 에서 실례 화 된 mWorker 대상 을 기억 하 십 니까...그래, 이 건 mWorker 대상 call()
방법 을 수행 하 는 거 야. 드디어 돌 아 왔 어!!!!call()
방법 에서 실행 call()
되 기 때문에 이 방법 은 백 스테이지 스 레 드 에서 실행 하 는 방법 이다.sDefaultExecutor 는 정적 구성원 변수 로 AsyncTask 대상 을 만 들 때마다 이 변수 에 있 는 mTasks 대기 열 에 추 가 됩 니 다. 또한 AsyncTask 대상 은 직렬 로 실 행 됩 니 다 (실현 원 리 는 try / finnally 블록 을 통 해 이 루어 집 니 다. 코드 는 위의 SerialExecutor
doInBackground(mParams)
와 같 습 니 다.아직
execute()
결 과 를 어떻게 되 돌려 야 할 지 모 르 겠 으 니 이 문 제 를 어떻게 해결 하 는 지 살 펴 보 자.2. 어떻게
doInBackground(mParams)
결 과 를 되 돌려 줍 니까?AsyncTask 에서 실례 화 된 mWorker 대상 의
doInBackground(mParams)
방법 이 post Result (doInBackground (mParams) 로 돌아 간 다 는 것 을 알 고 있 습 니 다. call()
소스 코드 와 관련 된 다른 소스 코드 를 살 펴 보 겠 습 니 다.
// post()
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
// getHandler()
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
// InternalHandler
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:
//
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
// AsyncTaskResult
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
위 에 모두 네 부분 이 있 는데 첫 번 째 부분 은
postResult()
소스 코드 이다. 이 방법 에서 먼저 postResult()
(소스 코드 는 두 번 째 부분) 를 호출 하여 Internal Handler 대상 (소스 코드 는 세 번 째 부분) 을 되 돌려 주 었 다.한편, Internal Handler 대상 은 Handler 대상 입 니 다.메시지 큐 에 메 시 지 를 보 냅 니 다.그리고 Handler 대상 getHandler()
에서 정 보 를 처리 합 니 다.세 번 째 부분 에 서 는 Internal Handler 류 소스 코드 를 제 시 했 으 며, 보 낸 메 시 지 는 이러한 종류의
obtainMessage()
에서 처 리 될 것 이다.이 방법 에서 먼저 AsyncTask Result 대상 (원본 코드 는 네 번 째 부분) 을 얻 습 니 다. AsyncTask Result 대상 은 현재 AsyncTask 대상 과 sendToTarget()
전달 대상 을 저장 하 는 데 사 용 됩 니 다. 여기 서 전달 하 는 것 은 바로 handleMessage()
결 과 를 되 돌려 줍 니 다. 그러면 우 리 는 결 과 를 Message 대상 에 추가 합 니 다.AsyncTask 도 Handler 메커니즘 을 통 해 비동기 처 리 를 하 는 것 을 볼 수 있 습 니 다. 하위 스 레 드 에서 작업 을 처리 한 후 실행 결 과 를 Message 에 추가 한 다음 주 스 레 드 에서 Looper 대상 을 통 해 이 Message 를 꺼 내 해당 Handler 대상
handleMessage()
을 통 해 처리 합 니 다.postResult()
에서 우리 가 설정 한 Message 의 what 는 doInBackground()
이기 때문에 handleMessage()
에서 첫 번 째 케이스 블록 을 실행 합 니 다. postResult()
에서 결과 배열 의 첫 번 째 값 만 받 습 니 다.그럼 계속 MESSAGE_POST_RESULT
소스 코드 를 보 겠 습 니 다.
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
작업 이 멈 추 지 않 으 면 덮어 쓰 는 방법
handleMessage()
을 사용 할 수 있 습 니 다.result.mTask.finish(result.mData[0])
케이스 블록 이 하나 더 있 습 니 다. 그럼 이 건 언제 실 행 됩 니까?3. 호출
finish()
은 어떻게 메 인 스 레 드 에 퀘 스 트 진 도 를 보 냅 니까?우 리 는
finish()
에서 호출 onPostExecute(result)
할 때 현재 작업 진 도 를 메 인 스 레 드 에 보 내 는 것 을 알 고 있 습 니 다. 그러면 이 방법의 소스 코드 를 살 펴 보 겠 습 니 다.
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
어디서 많이 본 것 같 지 않 아 요?이 방법 은
handleMessage()
방법 과 마찬가지 로 메시지 대기 열 에 메 시 지 를 보 냅 니 다. 이 메시지 의 what 속성 은 publishProgress()
이 므 로 doInBackground()
의 두 번 째 케이스 블록, 즉 우리 가 덮어 쓴 publishProgress()
을 실행 하면 UI 를 업데이트 할 수 있 습 니 다.4. 임 무 를 어떻게 병행 합 니까?
SerialExecutor 대상 을 통 해 순서 (직렬) 로 만 임 무 를 수행 할 수 있다 는 것 을 알 고 있 습 니 다. 그러면 어떻게 병행 을 실현 합 니까?호출
postResult()
이 실제로 실 행 된 것 은 MESSAGE_POST_PROGRESS
이라는 것 을 알 고 있 습 니 다. 이 방법의 첫 번 째 매개 변 수 는 기본적으로 SerialExecutor 대상 입 니 다.따라서 우 리 는 호출 handleMessage()
방법 을 통 해 병행 집행 을 실현 할 수 있다. 전 제 는 우리 가 이 방법 에서 의 첫 번 째 매개 변 수 는 병행 실행 스 레 드 를 전달 하 는 스 레 드 탱크 이다.이 스 레 드 탱크 는 AsyncTask 류 자체 스 레 드 탱크 onProgressUpdate()
를 사용 할 수 있 습 니 다. 소스 코드 는 다음 과 같 습 니 다.
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
이 스 레 드 탱크 의 최대 스 레 드 개 수 는 128 이 며, 물론 사용자 정의 스 레 드 탱크 도 사용 할 수 있 습 니 다.
Android 3.0 이후 기본 적 인 상황 에서 AsyncTask 는 하나의 작업 만 수행 할 수 있 습 니 다. 우 리 는 기본 설정 을 유연 하 게 바 꾸 어 AsyncTask 가 여러 작업 을 병행 할 수 있 도록 할 수 있 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JAVA- 소스 코드 분할(Package 사용)▪️test45.java 소스 코드 ▪️test47.java 소스 코드 ▪️실행 결과 더하면 12, 당기면 8 ▪️예① 클래스 이름에 대한 완전한 입력 생략 import 문 사용 ▪️예① test45.java 소스 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.