Android AsyncTask運作原理和原始碼分析

yangxi_001發表於2013-11-26
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 = 10;

    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);

    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;
    private static final int MESSAGE_POST_CANCEL = 0x3;

    private static final InternalHandler sHandler = new InternalHandler();

    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;

    private volatile Status mStatus = Status.PENDING;

    /**
     * Indicates the current status of the task. Each status will be set only once
     * during the lifetime of a task.
     */
    public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }

    /**
     * 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 {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                return doInBackground(mParams);
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                Message message;
                Result result = null;

                try {
                    result = 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) {
                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
                    message.sendToTarget();
                    return;
                } catch (Throwable t) {
                    throw new RuntimeException("An error occured while executing "
                            + "doInBackground()", t);
                }

                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                        new AsyncTaskResult<Result>(AsyncTask.this, result));
                message.sendToTarget();
            }
        };
    }

    /**
     * Returns the current status of this task.
     *
     * @return The current status.
     */
    public final Status getStatus() {
        return mStatus;
    }

   
    protected abstract Result doInBackground(Params... params);

   
    protected void onPreExecute() {
    }

   
    @SuppressWarnings({"UnusedDeclaration"})
    protected void onPostExecute(Result result) {
    }

    
    @SuppressWarnings({"UnusedDeclaration"})
    protected void onProgressUpdate(Progress... values) {
    }

   
    protected void onCancelled() {
    }

  
    public final boolean isCancelled() {
        return mFuture.isCancelled();
    }

    
    public final boolean cancel(boolean mayInterruptIfRunning) {
        return mFuture.cancel(mayInterruptIfRunning);
    }

  
    public final Result get() throws InterruptedException, ExecutionException {
        return mFuture.get();
    }

    
    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
            ExecutionException, TimeoutException {
        return mFuture.get(timeout, unit);
    }

   
    public final AsyncTask<Params, Progress, Result> execute(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;
        sExecutor.execute(mFuture);

        return this;
    }

   
    protected final void publishProgress(Progress... values) {
        sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }

    private void finish(Result result) {
        onPostExecute(result);
        mStatus = Status.FINISHED;
    }

    private static class InternalHandler extends Handler {
        @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;
                case MESSAGE_POST_CANCEL:
                    result.mTask.onCancelled();
                    break;
            }
        }
    }

    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
}

  

  上述是AsyncTask的全部程式碼。

 

    AsyncTask多用於Android的Context(Activity)處理後臺邏輯同時又要兼顧主執行緒的一些邏輯(比如說Activity的UI更新)。

 

    AsyncTask它其實是封裝了一個多執行緒、向開發者遮蔽了考慮多執行緒的問題。開發人員只需去重寫AsyncTask中的doInBackground、onProgressUpdate、onPreExecute、onPostExecute等方法,然後生成該物件並執行execute方法即可實現非同步操作。

 

  由於Android負責Context(Activity)的主執行緒(或者說是UI控制執行緒)不是執行緒安全的,也就是說開發者不能在自己生成的執行緒物件裡面操作Activity的UI更新。

 

    對於習慣了Java開發的開發人員來說,非要自己生成執行緒物件,在這個執行緒裡操作一些時間較長的邏輯(比如下載檔案),完成之後再提醒使用者下載完成(UI更新,比如在一個TextView中把文字修改成"下載完成")的話,那麼UI更新的工作只能藉助於Handler(這個Handler必須是在主執行緒中生成,或者說和主執行緒共用一個Looper和MessageQueue,通常的方法是在Activity中的onCreate方法中生成重寫的Handler物件)。當開發者的執行緒完成邏輯操作之後,傳送一個訊息到Handler,再由Handler去處理。如果Handler由主執行緒生成,那麼這個Handler的handleMessage()會在主執行緒中執行,因此在handleMessage()中可以訪問Activity中的某個View並修改。如一下程式碼:

  

public class TestActivity extends Activity{
       private static final int WHAT = 0x01;<br>
       private Thread downloadThread;
       private Handler refleshHandler;
 
       @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.main);
        super.onCreate(savedInstanceState);
         
                 
        downloadThread = new Thread(new DownloadRunnable());
        refleshHandler = new RefleshHandler();
		downloadThread.start();

	}     
	
	private class DownloadRunnable implements Runnable { 
	  public void run() {
			System.out.println("開始處理業務"); 
			//耗時較長的邏輯程式碼省略          
			refleshHandler.sendEmptyMessage(WHAT);
		}       
	}       
	
	private class RefleshHanler extends Handler{         
		@Override 
		public void handleMessage(Message msg) { 
			if(msg.what == WHAT){
			//更新TestActivity UI          
		}      
	}
}

  

  上述的方法也未嘗不是一種好的方法。然而Android為開發者提供了一種新的解決方案,那就是AsyncTask,分析完原始碼會發現,其實AsyncTask為我們封裝了上述的方法。

 

     對於我們重寫AsyncTask的doInBackground()的方法,AsyncTask會將它封裝成一個實現Callable介面的物件,線上程池中找出一個執行緒是實現它,因此我們所需要的耗時較長的邏輯可以放在這個方法裡面。

 

     AsyncTask也維護著一個靜態的Handler,這個Handler屬於建立AsyncTask的執行緒,而建立AsyncTask一般都是主執行緒(UI執行緒),因此這個Handler可以訪問並Activity的UI。

      

private static class InternalHandler extends Handler {
        @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;
                case MESSAGE_POST_CANCEL:
                    result.mTask.onCancelled();
                    break;
            }
        }
    }

  

  可以看到這個Handler操作了AsyncTask的三個方法:finish() , onProgressUpdate() , onCancelled(),而finish() 又呼叫onPostExecute()方法。

 

     可以這麼說,onProgressUpdate() ,onCancelled(),onPostExecute() 用來被開發者重寫,去更新UI的,這3個方法會涉及到UI的操作,因此doInBackground()方法裡不能呼叫這幾個方法,也就是說開發者可以重寫這些方法,但是又不能直接呼叫這些方法,只能通過傳送訊息給Handler的方式來隱式的呼叫。

 

      onProgressUpdate()是一個更新處理進度的方法,開發者可以重寫它,可以將它關聯到Activity的一個進度條控制元件上,在doInBackground()裡可以用publishProgress()去間接呼叫它(其實這個函式也是通過傳送what為MESSAGE_POST_PROGRESS的Message給Handler的方式來呼叫onProgressUpdate()的),這樣就能在UI上顯示進度資訊。

 

     finish()方法,也就是onPostExecute(),是處理完doInBackground()得到結果之後的呼叫。doInBackground()的函式封裝在實現Callable介面的名叫一個WorkerRunnable的抽象類中,再將這個類封裝在Future中,並重寫Future的done()方法,這個方法會在Callable的call方法執行完之後呼叫,就是doInBackground()方法執行完之後呼叫,它可以獲得執行完的結果,Future.get()方法(可能阻塞),並將得到的結果封裝在what為MESSAGE_POST_RESULT的Message中,併傳送。上訴的核心程式碼如下:

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }
	
	
	 mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                return doInBackground(mParams);
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                Message message;
                Result result = null;

                try {
                    result = 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) {
                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
                    message.sendToTarget();
                    return;
                } catch (Throwable t) {
                    throw new RuntimeException("An error occured while executing "
                            + "doInBackground()", t);
                }

                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                        new AsyncTaskResult<Result>(AsyncTask.this, result));
                message.sendToTarget();
            }
        };

 

    同理,onCancelled()是在AsyncTask被取消時的呼叫,也是通過Handler的方法。

 

    有一點要注意到,還有一個onPreExecute()方法,雖然這個方法是未被Handler加入到訊息處理的方法裡,但是這個方法是在execute()裡執行的,execute是主執行緒(UI執行緒)才會去執行的,所以這個方法也能訪問和修改UI。

 

 

   總結:  開發者可以重寫AsyncTask的onProgressUpdate() , onCancelled(), onPostExecute(), onPreExecute(), doInBackground()方法,以得到非同步實現後臺邏輯並更新UI的操作。

 

   其中onProgressUpdate() , onCancelled(), onPostExecute(), onPreExecute() 可以直接訪問並修改UI。

 

   但是doInBackground()不能出現涉及UI的操作,也不能直接呼叫onProgressUpdate() , onCancelled(), onPostExecute(), onPreExecute() 這四個方法,後三者不需要呼叫,可以通過publishProgress()去間接的呼叫onProgressUpdate()方法。

 

  最後要說的是AsyncTask的物件一旦生成之後,execute()方法只能被呼叫一次,即使是同樣的操作,也需要重新生成AsyncTask物件才行。

相關文章