AsyncTask為什麼可以在回撥中修改UI(原始碼分析)

weixin_33976072發表於2016-10-24

今天偶然想到了使用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);

二、原始碼中找答案

  1. AsyncTask在其中開啟Structure檢視,馬上就看到了有個getHandler()方法還有一個成員變數InternalHandler。於是馬上猜測AsyncTask,其實是使用了Handler機制實現了執行緒切換。


    3385286-e4acc30c0d8b0de3
    image
  2. 繼續看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;
            }
        }
    }
  1. finish方法裡有什麼呢?繼續看,果然呼叫onPostExecute()方法,至此,基本已經確定AsyncTask能夠在回撥中修改UI就是使用了Handler機制。
 private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
  1. 什麼時候向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);
                }
            }
        };
    }
  1. 在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;
    }
  1. 最後看下第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()方法。

相關文章