AsyncTask為什麼可以在回撥中修改UI(原始碼分析)
今天偶然想到了使用AsyncTask非同步任務棧,可以在onPostExecute()中修改UI,這不是一個很奇怪的現象嗎?於是便萌發想法看看原始碼這到底是怎麼回事。
一、AsyncTask的使用介紹
還記得AsyncTask的使用方法不?主要是重寫幾個方法
protected void onPreExecute() //開始前
protected void onProgressUpdate() //進行中
protected Object doInBackground() //後臺執行的具體邏輯
protected void onPostExecute() //任務執行完畢
protected void onCancelled() //任務取消
// Using an AsyncTask to load the slow images in a background thread
new AsyncTask<ViewHolder, Void, Bitmap>() {
private ViewHolder v;
@Override
protected Bitmap doInBackground(ViewHolder... params) {
v = params[0];
return mFakeImageLoader.getImage();
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (v.position == position) {
// If this item hasn't been recycled already, hide the
// progress and set and show the image
v.progress.setVisibility(View.GONE);
v.icon.setVisibility(View.VISIBLE);
v.icon.setImageBitmap(result);
}
}
}.execute(holder);
二、原始碼中找答案
-
AsyncTask在其中開啟Structure檢視,馬上就看到了有個getHandler()方法還有一個成員變數InternalHandler。於是馬上猜測AsyncTask,其實是使用了Handler機制實現了執行緒切換。
繼續看InternalHandler的原始碼,已經發現端倪,它已經直接呼叫了onProgressUpdate()方法。
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;
}
}
}
- finish方法裡有什麼呢?繼續看,果然呼叫onPostExecute()方法,至此,基本已經確定AsyncTask能夠在回撥中修改UI就是使用了Handler機制。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
- 什麼時候向Handler發的訊息呢?看AsyncTask構造方法就可以了。構造方法裡初始化了工作執行緒和FutureTask。
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); //執行完了呼叫postResult
}
};
//放入FutureTask中
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);
}
}
};
}
- 在executeOnExecutor方法中開始執行。
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;
exec.execute(mFuture); //exec是傳入的執行緒池
return this;
}
- 最後看下第4步中的postResult()方法的原始碼,找到怎麼給Handler發訊息的。到這裡整個鏈條已經串起來。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget(); //傳送訊息
return result;
}
三、總結
AsyncTask並沒有違反Android定義的非UI執行緒不能修改UI的規則,實際上它是使用了Handler機制實現了執行緒切換。在onPostExecute() 實際上已經處於UI執行緒當中,所以可以對UI進行修改,同理於onProgressUpdate()方法。
相關文章
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- AsyncTask原始碼解析原始碼
- 在c中,怎麼註冊回撥函式函式
- AsyncTask 程式碼分析
- Hystrix微服務容錯處理及回撥方法原始碼分析微服務原始碼
- [譯] JavaScript:回撥是什麼鬼?JavaScript
- javascript非同步回撥是什麼JavaScript非同步
- 從原始碼解析-掌握AsyncTask工作原理 為什麼序列執行和記憶體洩漏原始碼記憶體
- jQuery 原始碼學習 (三) 回撥函式jQuery原始碼函式
- JavaScript基礎——回撥(callback)是什麼JavaScript
- 在專案中自定義路徑放入element-ui並修改編譯原始碼UI編譯原始碼
- .Net5 下Dictionary 為什麼可以在foreach中RemoveREM
- kotlin中將回撥改寫為協程Kotlin
- 你知道什麼是空閒回撥(requestIdleCallback)嗎?
- 可以修改的網站原始碼,可修改的網站原始碼範圍指南網站原始碼
- 通達信回撥必抓選股指標公式原始碼指標公式原始碼
- jQuery原始碼剖析(四) - Deferred非同步回撥解決方案jQuery原始碼非同步
- shell 命令在終端可以執行成功,為什麼放在 groovy 指令碼中不執行?指令碼
- 為什麼要閱讀原始碼原始碼
- 為什麼插畫在UI設計中這麼火?新手想學怎麼入門?UI
- 【原始碼解析】AsyncTask的用法與規則原始碼
- 原始碼分析 Mybatis 的 foreach 為什麼會出現效能問題原始碼MyBatis
- petite-vue原始碼剖析-為什麼要讀原始碼?Vue原始碼
- 關於.NET在中國為什麼工資低的分析
- 在Dash中更靈活地編寫回撥函式函式
- 為什麼 Python 程式碼在函式中執行得更快?Python函式
- GDB為什麼檢視不了原始碼?原始碼
- Vue原始碼中為什麼要const _toStr = Object.prototype.toString?Vue原始碼Object
- 原始碼分析:如何定製Semantic-UI原始碼UI
- 在日本,為什麼修改遊戲存檔可能違法遊戲
- [JS]回撥函式和回撥地獄JS函式
- 解讀element-ui中table元件部分原始碼與需求分析UI元件原始碼
- 回撥方法
- 哪些人可以學習資料分析?為什麼學資料分析?
- React 為什麼要把事件掛載到 document 上 & 事件機制原始碼分析React事件原始碼
- ajax中回撥的幾個坑
- C++中的回撥函式C++函式
- 為什麼在js中需要新增addEventListener()?JSdev
- JNI-Thread中start方法的呼叫與run方法的回撥分析thread