android 비동기 메시지 메커니즘 원본 차원 에서 분석(2)
AsyncTask 가 뭐야?
AsyncTask 는 경량급 비동기 작업 클래스 로 온라인 스 레 드 에서 배경 작업 을 수행 한 다음 에 실 행 된 진도 와 결 과 를 메 인 스 레 드 에 전달 하고 메 인 스 레 드 에서 UI 를 업데이트 할 수 있 습 니 다.
AsyncTask 와 같은 종류의 성명 은 다음 과 같 습 니 다.
public abstract class AsyncTask<Params, Progress, Result>
이것 은 Params,Progress,Result 세 개의 범 형 파 라미 터 를 제 공 했 고 아래 에서 이 세 개의 범 형 파라미터 의 구체 적 인 의 미 를 자세히 분석 할 것 이다.AsyncTask 는 네 가지 핵심 방법 을 제공 했다.
onPreExecute()
이 방법 은 메 인 스 레 드 에서 실 행 됩 니 다.비동기 작업 이 실 행 될 때 까지 이 방법 은 호출 됩 니 다.보통 진도 항목 을 다운로드 하 는 초기 화 등 준비 작업 에 사 용 됩 니 다.
doInBackground(Params… params)
이 방법 은 하위 스 레 드 에서 실 행 됩 니 다.비동기 작업 을 수행 하 는 데 사 용 됩 니 다.이 params 는 AsyncTask 의 첫 번 째 매개 변수 형식 입 니 다.이 방법 에서 publicProgress 방법 을 호출 하여 작업 진 도 를 업데이트 할 수 있 으 며,publicProgress 는 onProgressUpdate 방법 을 호출 합 니 다.
onProgressUpdate(Progress… values)
이 방법 은 주 스 레 드 에서 실 행 됩 니 다.values 의 유형 은 AsyncTask 에서 들 어 오 는 두 번 째 매개 변수 형식 입 니 다.배경 작업 의 실행 진도 가 바 뀌 었 을 때 이 방법 을 실행 합 니 다.
onPostExecute(Result result)
이 방법 은 주 스 레 드 에서 실 행 됩 니 다.doInBackground 방법 이 실 행 된 후에 이 방법 이 호출 됩 니 다.그 중에서 result 의 유형 은 AsyncTask 가 들 어 오 는 세 번 째 매개 변수 형식 입 니 다.그 값 은 doInBackground 방법의 반환 값 입 니 다.
이 어 AsyncTask 에서 가장 흔히 볼 수 있 는 용법 을 살 펴 보 자.이 사례 는 핸드폰 메모리 에 있 는 cache 디 렉 터 리 에 그림 을 다운로드 하면 다운로드 시작 시 진행 상자 가 팝 업 되 고 다운로드 과정 에서 다운로드 진 도 를 표시 하 며 다운로드 가 완료 되면 진행 상 자 를 닫 는 것 이다.성공 하면 다운로드 에 성공 한 Toast 가 나타 나 고 실패 하면 실패 한 Toast 가 팝 업 된다.
class MyAsyncTask extends AsyncTask<Void,Integer,Boolean> {
@Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setTitle(" ");
progressDialog.setMax(100);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setCancelable(true);
progressDialog.show();
}
@Override
protected Boolean doInBackground(Void... params) {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) params[0].openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
final int contentLength;
if (conn.getResponseCode() == 200) {
contentLength = conn.getContentLength();
File file = new File(getCacheDir(),"hh.jpg");
InputStream is= null;
FileOutputStream fos= new FileOutputStream(file);
is = conn.getInputStream();
int len = 0;
long totalSize = 0;
byte[] bytes = new byte[102];
while ((len = is.read(bytes))!=-1) {
fos.write(bytes,0,len);
totalSize += len*100;
int progress = (int) (totalSize/contentLength);
publishProgress(progress);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
progressDialog.setProgress(values[0]);
}
@Override
protected void onPostExecute(Boolean aBoolean) {
progressDialog.dismiss();
if (result) {
Toast.makeText(context, " ", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, " ", Toast.LENGTH_SHORT).show();
}
}
}
이 종 류 는 주로 파일 의 다운로드 과정 을 모 의 하 는 데 사 용 됩 니 다.그림 url 주소 로 인 자 를 입력 하고 배경 프로 세 스 인 자 는 Integer 형식 이 며 배경 작업 의 반환 결 과 는 bollean 형식 입 니 다.위 다운로드 작업 을 수행 하려 면 다음 과 같은 방식 으로 수행 할 수 있 습 니 다.
// , doInBackground
URL url = new URL("http://192.168.43.21:8080/ditu.jpg");
new MyAsyncTask().execute(url);
소스 코드 분석우선 AsyncTask 의 구조 방법 부터 시작 하 겠 습 니 다.
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
1.주석 에서 말 한 바 와 같이 이 구조 방법 은 UI 스 레 드 에서 호출 되 어야 합 니 다.2.구조 함 수 는 주로 두 개의 인 스 턴 스 를 만 들 었 습 니 다.하 나 는 WorkerRunnable 이 고 Callback 대상 입 니 다.다른 하 나 는 Future Task 입 니 다.그 매개 변 수 는 앞에서 만 든 WorkerRunnable 대상 입 니 다.
다음은 AsyncTask 의 execute()방법 을 살 펴 보 겠 습 니 다.이 방법 은 전체 비동기 작업 의 입구 입 니 다.
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
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()
onPreExecute();
// AsyncTask , doInBackground(mParams)
mWorker.mParams = params;
//*******************
exec.execute(mFuture);
return this;
}
우 리 는 onPreExecute()가 호출 되 는 것 을 보 았 습 니 다.배경 작업 을 수행 하기 전에 호출 되 었 고 메 인 스 레 드 에서 호출 되 었 음 을 증명 합 니 다.이 때 우 리 는 인 스 턴 스 코드 의 진도 표시 상자 의 디 스 플레이 동작 을 실 행 했 습 니 다.이 화면 은 다음 과 같 습 니 다. 이 방법 에서 exec.execute(mFuture)를 작 동 했 습 니 다.우선 exec 가 무엇 인지 알 아야 합 니 다.아래 소스 코드 에서 우 리 는 답 을 얻 을 수 있다.
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
위의 코드 와 execute OnExecutor 가 들 어 오 는 매개 변수 에 따 르 면,우 리 는 이곳 의 exec 가 사실은 SerialExecutor 라 는 것 을 보 았 다.그러면 SerialExecutor 의 execute 방법 을 살 펴 보 자.
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
우 리 는 하위 스 레 드 에서 r.run()을 실행 하 는 것 을 보 았 습 니 다.execute()방법 에 따라 들 어 오 는 매개 변 수 를 통 해 알 수 있 듯 이 이곳 의 r 는 구조 방법 에서 처음 만 든 Future Task 입 니 다.물론 다음 에는 Future Task 의 run()방법 을 봐 야 합 니 다.
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//****************************
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
여기 result=c.call()방법 이 있 는데 여기 c 는 Callable c=callable 입 니 다.들 어 오 는 callable,callable 은 우리 가 처음에 AsyncTask 구조 방법 에서 들 어 온 WorkerRunnable 입 니 다.따라서 WorkerRunnable 의 call()방법 을 살 펴 봐 야 합 니 다.이 call()방법 은 주로 doInBackground(mParams)와 post Result(result)방법 을 호출 했 습 니 다.
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
//*********************************
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
1.여기 서 doInBackground(mParams)방법의 호출 을 볼 수 있 고 이 때 는 하위 스 레 드 에 있 기 때문에 시간 이 걸 릴 수 있 습 니 다.위의 사례 에서 doInBackground 방법 에서 코드 가 실 행 됩 니 다.PublishProgress()방법 을 실행 할 때 진행 정 보 를 onProgressUpdate()방법 에 계속 전달 합 니 다.(뒤의 분석 에서 PublishProgress()의 실행 이 왜 onProgressUpdate()방법 을 호출 하 는 지 설명 합 니 다)이 방법 을 업데이트 UI 를 실행 하도록 합 니 다.여기까지 실 행 될 때,사례 중의 다운로드 화면 은 다음 과 같 습 니 다.이때 진도 가 50%에 이 르 렀 습 니 다.2.방법의 마지막 에 post Result(result)방법 을 실 행 했 습 니 다.이 안의 result 매개 변 수 는 바로 우리 doInBackground(mParams)의 반환 값 입 니 다.이 방법의 주요 역할 은 Internal Handler 인 스 턴 스 를 만 들 고 what=MESSAGE 를 보 내 는 것 입 니 다.POST_RESULT 의 소식,우 리 는 이어서 이 방법의 소스 코드 를 보 았 다.
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
//message sendToTarget(), handler
public void sendToTarget() {
target.sendMessage(this);
}
handler 를 잘 아 는 친구 들 은 target.sendmessage(this)방법 이 메시지 대기 열 에 메 시 지 를 보 내 는 것 이라는 것 을 알 고 있 습 니 다.이 메 시 지 는 message 자체 입 니 다.message 에는 MESSAGE 가 들 어 있 습 니 다.POST_RESULT 와 new AsyncTask Result(this,result)대상 은 getHandler 에서 보 내 고 마지막 으로 handle Message()(이 메 시 지 를 처리 합 니 다)를 찾 아야 합 니 다.따라서 이 handler 를 찾 아야 합 니 다.
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
// sHandler InternalHandler
sHandler = new InternalHandler();
}
return sHandler;
}
}
위의 getHandler()방법 에 따 르 면 이 handler 대상 이 Internal Handler 라 는 것 을 알 수 있 습 니 다.다음 임 무 는 Internal Handler 의 handler Message()방법 을 분석 하 는 것 입 니 다.이 방법의 역할 은 post Result(Result result)방법 중의 sHandler 가 보 낸 서로 다른 메 시 지 를 판단 하여 서로 다른 논 리 를 수행 하 는 것 입 니 다.
@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;
}
}
//result.mTask.finish(result.mData[0])
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
//result.mTask.onProgressUpdate(result.mData)
@MainThread
protected void onProgressUpdate(Progress... values) {
}
위의 소스 코드 를 통 해 알 수 있 듯 이 msg 가 가지 고 있 는 what 정보 에 따라 서로 다른 정 보 를 처리 합 니 다.what=MESSAGEPOST_RESULT 시,최종 적 으로 onCanceled(result)또는 onPost Execute(result)를 실행 합 니 다.이 두 가지 방법 은 모두 메 인 스 레 드 에 있 습 니 다.MESSAGEPOST_RESULT 는 방금 정 보 를 얻 었 을 때 들 어 온 것 이다.여기 서 실 행 될 때 저희 가 사례 를 다운로드 한 코드 는 onPost Execute(result)에 도 착 했 습 니 다.저 희 는 진도 상자 의 숨겨 진 동작 을 실 행 했 습 니 다.실 행 된 화면 은 다음 과 같 습 니 다. 하지만 MESSAGEPOST_PROGRESS 인 자 는 어디서 들 려 왔 습 니까?맞습니다.바로 Publish Progress(Progress..values)입 니 다.
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
여기 MESSAGE 들 어 갑 니 다.POST_프로그램,그리고 이 방법 은 WorkerThread 에서 호출 됩 니 다.이렇게 하면 onProgressUpdate(Progress...values)방법 이 순조롭게 실 행 될 수 있 습 니 다.전체 프로 세 스 종료총결산
1.AsyncTask 의 대상 은 주 스 레 드 에서 만 만 만 들 수 있 습 니 다.
2.execute()방법 은 UI 스 레 드 에서 만 실 행 됩 니 다.
3.프로그램 에서 onPreExecute,onPost Execute,doInBackground,onProgressUpdate 방법 을 직접 호출 하지 마 십시오.
4.하나의 AsyncTask 대상 은 execute 방법 을 한 번 만 실행 할 수 있 습 니 다.실행 시 오 류 를 보고 할 수 있 습 니까?execute 방법 을 실행 할 때 다음 코드 로 호출 하여 이 결론 을 증명 합 니 다.
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)");
}
}
5.Android 1.6 이전에 AsyncTask 는 직렬 로 임 무 를 수행 하 였 으 며,1.6 이후 에는 온라인 풀 에서 임 무 를 병행 처리 하 는 것 으로 바 뀌 었 으 며,3.0 이후 에는 온라인 풀 에서 직렬 로 임 무 를 수행 하 는 것 으로 바 뀌 었 다.AsyncTask 에는 두 개의 스 레 드 풀(SerialExecutor 와 THREADPOOL_EXECUTOR)와 handler(Internal Handler).
그 중에서 SerialExecutor 는 작업 의 줄 서기 에 사 용 됩 니 다.위의 코드 에서 우 리 는 AsyncTask.execute 를 실행 하 는 과정 에서 execute 방법 을 호출 하 는 것 을 보 았 습 니 다.
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() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
우선 Future Task 대상 을 대기 열 mTask 에 삽입 하 는 것 을 보 았 습 니 다.이때 실행 중인 AsyncTask 활동 이 없 으 면 schedule Next()를 호출 합 니 다.다음 작업 을 수행 합 니 다.작업 이 완료 되면 다음 작업 을 수행 합 니 다.이 를 통 해 AsyncTask 는 기본적으로 직렬 로 실 행 됩 니 다.이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.