Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android AsyncTask 源碼解析

Android AsyncTask 源碼解析

編輯:關於Android編程

 

1、概述

相信大家對AsyncTask都不陌生,對於執行耗時任務,然後更新UI是一把利器,當然也是替代Thread + Handler 的一種方式。如果你對Handler機制還不了解,請看:Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關系。

2、簡單的例子

相信大家都寫過這樣的代碼:

 

package com.example.zhy_asynctask_demo01;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity
{

	private static final String TAG = MainActivity;
	private ProgressDialog mDialog;
	private TextView mTextView;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		mTextView = (TextView) findViewById(R.id.id_tv);

		mDialog = new ProgressDialog(this);
		mDialog.setMax(100);
		mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		mDialog.setCancelable(false);

		new MyAsyncTask().execute();

	}

	private class MyAsyncTask extends AsyncTask
	{

		@Override
		protected void onPreExecute()
		{
			mDialog.show();
			Log.e(TAG, Thread.currentThread().getName() +  onPreExecute );
		}

		@Override
		protected Void doInBackground(Void... params)
		{

			// 模擬數據的加載,耗時的任務
			for (int i = 0; i < 100; i++)
			{
				try
				{
					Thread.sleep(80);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				publishProgress(i);
			}

			Log.e(TAG, Thread.currentThread().getName() +  doInBackground );
			return null;
		}

		@Override
		protected void onProgressUpdate(Integer... values)
		{
			mDialog.setProgress(values[0]);
			Log.e(TAG, Thread.currentThread().getName() +  onProgressUpdate );
		}

		@Override
		protected void onPostExecute(Void result)
		{
			// 進行數據加載完成後的UI操作
			mDialog.dismiss();
			mTextView.setText(LOAD DATA SUCCESS );
			Log.e(TAG, Thread.currentThread().getName() +  onPostExecute );
		}
	}
}

進入某個Activity,Activity中需要的數據來自於網絡或者其它耗時操作,可以在AsyncTask中onPreExecute完成一些准備操作,比如上例中顯示進度對話框;然後在doInBackground完成耗時操作,在進行耗時操作時還能不時的通過publishProgress給onProgressUpdate中傳遞參數,然後在onProgressUpdate中可以進行UI操作,比如上例更新進度條的進度;當耗時任務執行完成後,最後在onPostExecute進行設置控件數據更新UI等操作,例如隱藏進度對話框。

 

效果圖:

/

3、源碼解析

注:本篇源碼分析基於Andorid-17,因為和3.0之前版本變動較大,有必要標出。

那麼大家一定好奇,AsyncTask在Android中是如何實現的,下面進行源碼分析:從我們的執行異步任務的起點開始,進入execute方法:

 

public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask 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);

        return this;
    }
18行:設置當前AsyncTask的狀態為RUNNING,上面的switch也可以看出,每個異步任務在完成前只能執行一次。
20行:執行了onPreExecute(),當前依然在UI線程,所以我們可以在其中做一些准備工作。
22行:將我們傳入的參數賦值給了mWorker.mParams
23行:exec.execute(mFuture)

 

相信大家對22行出現的mWorker,以及23行出現的mFuture都會有些困惑。
mWorker找到這個類:

 

private static abstract class WorkerRunnable implements Callable {
        Params[] mParams;
}

可以看到是Callable的子類,且包含一個mParams用於保存我們傳入的參數,下面看初始化mWorker的代碼:

 

 

public AsyncTask() {
        mWorker = new WorkerRunnable() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };
//….
        
}

可以看到mWorker在構造方法中完成了初始化,並且因為是一個抽象類,在這裡new了一個實現類,實現了call方法,call方法中設置mTaskInvoked=true,且最終調用doInBackground(mParams)方法,並返回Result值作為參數給postResult方法.可以看到我們的doInBackground出現了,下面繼續看:

 

 

  private Result postResult(Result result) {
        @SuppressWarnings(unchecked)
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();
        return result;
}

可以看到postResult中出現了我們熟悉的異步消息機制,傳遞了一個消息message, message.what為MESSAGE_POST_RESULT;message.object= new AsyncTaskResult(this,result);

 

 

 private static class AsyncTaskResult {
        final AsyncTask mTask;
        final Data[] mData;

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

AsyncTaskResult就是一個簡單的攜帶參數的對象。

 

看到這,我相信大家肯定會想到,在某處肯定存在一個sHandler,且復寫了其handleMessage方法等待消息的傳入,以及消息的處理。

 

private static final InternalHandler sHandler = new InternalHandler();
    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;
            }
        }
}

哈哈,出現了我們的handleMessage,可以看到,在接收到MESSAGE_POST_RESULT消息時,執行了result.mTask.finish(result.mData[0]);其實就是我們的AsyncTask.this.finish(result),於是看finish方法

 

 

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

可以看到,如果我們調用了cancel()則執行onCancelled回調;正常執行的情況下調用我們的onPostExecute(result);主要這裡的調用是在handler的handleMessage中,所以是在UI線程中。如果你對異步消息機制不理解請看:Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關系
最後將狀態置為FINISHED。

 

mWoker看完了,應該到我們的mFuture了,依然實在構造方法中完成mFuture的初始化,將mWorker作為參數,復寫了其done方法。

 

public AsyncTask() {
	...
        mFuture = new FutureTask(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);
                }
            }
        };
}
16行:任務執行結束會調用:postResultIfNotInvoked(get());get()表示獲取mWorker的call的返回值,即Result.然後看postResultIfNotInvoked方法

 

 

private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
}
如果mTaskInvoked不為true,則執行postResult;但是在mWorker初始化時就已經將mTaskInvoked為true,所以一般這個postResult執行不到。
好了,到了這裡,已經介紹完了execute方法中出現了mWorker和mFurture,不過這裡一直是初始化這兩個對象的代碼,並沒有真正的執行。下面我們看真正調用執行的地方。
execute方法中的:
還記得上面的execute中的23行:exec.execute(mFuture)

 

exec為executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor

下面看這個sDefaultExecutor
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
        final ArrayDeque mTasks = new ArrayDeque();
        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);
            }
        }
}
可以看到sDefaultExecutor其實為SerialExecutor的一個實例,其內部維持一個任務隊列;直接看其execute(Runnable runnable)方法,將runnable放入mTasks隊尾;
16-17行:判斷當前mActive是否為空,為空則調用scheduleNext方法
20行:scheduleNext,則直接取出任務隊列中的隊首任務,如果不為null則傳入THREAD_POOL_EXECUTOR進行執行。
下面看THREAD_POOL_EXECUTOR為何方神聖:
public static final Executor THREAD_POOL_EXECUTOR
          =new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可以看到就是一個自己設置參數的線程池,參數為:

 

private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
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 BlockingQueue sPoolWorkQueue =
            new LinkedBlockingQueue(10);

看到這裡,大家可能會認為,背後原來有一個線程池,且最大支持128的線程並發,加上長度為10的阻塞隊列,可能會覺得就是在快速調用138個以內的AsyncTask子類的execute方法不會出現問題,而大於138則會拋出異常。
其實不是這樣的,我們再仔細看一下代碼,回顧一下sDefaultExecutor,真正在execute()中調用的為sDefaultExecutor.execute:
private static class SerialExecutor implements Executor {
        final ArrayDeque mTasks = new ArrayDeque();
        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);
            }
        }
}

可以看到,如果此時有10個任務同時調用execute(s synchronized)方法,第一個任務入隊,然後在mActive = mTasks.poll()) != null被取出,並且賦值給mActivte,然後交給線程池去執行。然後第二個任務入隊,但是此時mActive並不為null,並不會執行scheduleNext();所以如果第一個任務比較慢,10個任務都會進入隊列等待;真正執行下一個任務的時機是,線程池執行完成第一個任務以後,調用Runnable中的finally代碼塊中的scheduleNext,所以雖然內部有一個線程池,其實調用的過程還是線性的。一個接著一個的執行,相當於單線程。

 

4、總結

到此源碼解釋完畢,由於代碼跨度比較大,我們再回顧一下:

 

public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask 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);

        return this;
    }

18行:設置當前AsyncTask的狀態為RUNNING,上面的switch也可以看出,每個異步任務在完成前只能執行一次。
20行:執行了onPreExecute(),當前依然在UI線程,所以我們可以在其中做一些准備工作。
22行:將我們傳入的參數賦值給了mWorker.mParams ,mWorker為一個Callable的子類,且在內部的call()方法中,調用了doInBackground(mParams),然後得到的返回值作為postResult的參數進行執行;postResult中通過sHandler發送消息,最終sHandler的handleMessage中完成onPostExecute的調用。
23行:exec.execute(mFuture),mFuture為真正的執行任務的單元,將mWorker進行封裝,然後由sDefaultExecutor交給線程池進行執行。

 

 

5、publishProgress

說了這麼多,我們好像忘了一個方法:publishProgress

 

protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult
(this, values)).sendToTarget(); } } 也很簡單,直接使用sHandler發送一個消息,並且攜帶我們傳入的值;

 

 

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

在handleMessage中進行了我們的onProgressUpdate(result.mData);的調用。

6、AsyncTask曾經缺陷

 

記得以前有個面試題經常會問道:AsyncTask運行的原理是什麼?有什麼缺陷?

以前對於缺陷的答案可能是:AsyncTask在並發執行多個任務時發生異常。其實還是存在的,在3.0以前的系統中還是會以支持多線程並發的方式執行,支持並發數也是我們上面所計算的128,阻塞隊列可以存放10個;也就是同時執行138個任務是沒有問題的;而超過138會馬上出現java.util.concurrent.RejectedExecutionException;

而在在3.0以上包括3.0的系統中會為單線程執行(即我們上面代碼的分析);

空說無憑:下面看測試代碼:

 

package com.example.zhy_asynctask_demo01;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity
{

	private static final String TAG = MainActivity;
	private ProgressDialog mDialog;
	private TextView mTextView;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		mTextView = (TextView) findViewById(R.id.id_tv);

		mDialog = new ProgressDialog(this);
		mDialog.setMax(100);
		mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		mDialog.setCancelable(false);
		
		
		for(int i = 1 ;i <= 138 ; i++)
		{
			new MyAsyncTask2().execute();
		}
		
		//new MyAsyncTask().execute();

		
	}

	private class MyAsyncTask2 extends AsyncTask
	{

		@Override
		protected Void doInBackground(Void... params)
		{
			try
			{
				Log.e(TAG, Thread.currentThread().getName());
				Thread.sleep(10000);
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			return null;
		}
		
	}
}
可以看到我for循環中執行138個異步任務,每個異步任務的執行需要10s;下面使用2.2的模擬器進行測試:

 

輸出結果為:

AsyncTask#1 - AsyncTask #128同時輸出
然後10s後,另外10個任務輸出。
可以分析結果,得到結論:AsyncTask在2.2的系統中同時支持128個任務並發,至少支持10個任務等待;

下面將138個任務,改成139個任務:

 

for(int i = 1 ;i <= 139 ; i++)
{
	new MyAsyncTask2().execute();
}
運行結果:會發生異常:java.util.concurrent.RejectedExecutionException ; 於是可以確定僅支持10個任務等待,超過10個則立即發生異常。
簡單說一下出現異常的原因:現在是139個任務,幾乎同時提交,線程池支持128個的並發,然後阻塞隊列數量為10,此時當第11個任務提交的時候則會發生異常。

 

簡單看一下源碼:

 

 public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
看ThreadPoolExecutor的execute方法:

 

 

if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);

當阻塞隊列滿的時候workQueue.offer(command)返回false;然後執行addWorker(command,false)方法,如果返回false則執行reject()方法.

 

 

private boolean addWorker(Runnable firstTask, boolean core) {
…
int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
…
}

可以看到當任務數目大於容量則返回false,最終在reject()中拋出異常。

 

上面就是使用2.2模擬器測試的結果;

下面將系統改為4.1.1,也就是我的測試機小米2s

把線程數改為139甚至1000,你可以看到任務一個接一個的在那緩慢的執行,不會拋什麼異常,不過線程倒是1個1個的在那執行;

 

好了,如果現在大家去面試,被問到AsyncTask的缺陷,可以分為兩個部分說,在3.0以前,最大支持128個線程的並發,10個任務的等待。在3.0以後,無論有多少任務,都會在其內部單線程執行;

 

至此,AsyncTask源碼分析完畢,相信大家對AsyncTask有了更深的理解~~~

 

 

測試代碼點擊下載



 

 



 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved