AsyncTask是一個輕量級的異步任務類,它可以輕松地控制UI線程中后臺和后臺操作返回的結(jié)果,而無需使用線程和處理程序等組合進行切換,感興趣的朋友可以跟隨武林技術(shù)頻道小編的步伐來了解一下!
AsyncTask的介紹及基本使用方法
關(guān)于AsyncTask的介紹和基本使用方法可以參考官方文檔和《Android開發(fā)筆記之:深入理解多線程AsyncTask》這里就不重復。
AsyncTask引發(fā)的一個問題
上周遇到了一個極其詭異的問題,一個小功能從網(wǎng)絡上下載一個圖片,然后放到ImageView中,是用AsyncTask來實現(xiàn)的,本身邏輯也很簡單,僅是在doInBackground中用HTTP請求把圖片的輸入流取出,然后用BitmapFactory去解析,然后再把得到的Bitmap放到ImageView中。這個應用是用4.0的SDK開發(fā)的,也是運行在4.0上面的。但是有時候下載這張圖片去要用很久很久,甚至要等上幾分鐘。通過調(diào)試發(fā)現(xiàn)一個令人難以接受的事實:竟然是doInBackground()未及時執(zhí)行,也就是它并沒有在#execute()調(diào)用之后馬上執(zhí)行,而是等待了很久才得以執(zhí)行。
神馬情況,難道AsyncTask不是線程,難道不是異步,難道AsyncTask另有內(nèi)幕?
AsyncTask的內(nèi)幕
AsyncTask主要有二個部分:一個是與主線各的交互,另一個就是線程的管理調(diào)度。雖然可能多個AsyncTask的子類的實例,但是AsyncTask的內(nèi)部Handler和ThreadPoolExecutor都是進程范圍內(nèi)共享的,其都是static的,也即屬于類的,類的屬性的作用范圍是CLASSPATH,因為一個進程一個VM,所以是AsyncTask控制著進程范圍內(nèi)所有的子類實例。
與主線程交互
與主線程交互是通過Handler來進行的,因為本文主要探討AsyncTask在任務調(diào)度方面的,所以對于這部分不做細致介紹,感興趣的朋友可以去看AsyncTask的源碼
線程任務的調(diào)度
內(nèi)部會創(chuàng)建一個進程作用域的線程池來管理要運行的任務,也就就是說當你調(diào)用了AsyncTask#execute()后,AsyncTask會把任務交給線程池,由線程池來管理創(chuàng)建Thread和運行Therad。對于內(nèi)部的線程池不同版本的Android的實現(xiàn)方式是不一樣的:
Android2.3以前的版本,也即SDK/API 10和以前的版本
內(nèi)部的線程池限制是5個,也就是說同時只能有5個線程運行,超過的線程只能等待,等待前面的線程某個執(zhí)行完了才被調(diào)度和運行。換句話說,如果一個進程中的AsyncTask實例個數(shù)超過5個,那么假如前5個都運行很長時間的話,那么第6個只能等待機會了。這是AsyncTask的一個限制,而且對于2.3以前的版本無法解決。如果你的應用需要大量的后臺線程去執(zhí)行任務,那么你只能放棄使用AsyncTask,自己創(chuàng)建線程池來管理Thread,或者干脆不用線程池直接使用Thread也無妨。不得不說,雖然AsyncTask較Thread使用起來比較方便,但是它最多只能同時運行5個線程,這也大大局限了它的實力,你必須要小心的設計你的應用,錯開使用AsyncTask的時間,盡力做到分時,或者保證數(shù)量不會大于5個,否則就可能遇到上面提到的問題。要不然就只能使用JavaSE中的API了。
?
?

Android 3.0以后,也即SDK/API 11和以后的版本
可能是Google意識到了AsyncTask的局限性了,從Android 3.0開始對AsyncTask的API做出了一些調(diào)整:
1.#execute()提交的任務,按先后順序每次只運行一個 
也就是說它是按提交的次序,每次只啟動一個線程執(zhí)行一個任務,完成之后再執(zhí)行第二個任務,也就是相當于只有一個后臺線程在執(zhí)行所提交的任務(Executors.newSingleThreadPool())。
?
?

2.新增了接口#executeOnExecutor() 
這個接口允許開發(fā)者提供自定義的線程池來運行和調(diào)度Thread,如果你想讓所有的任務都能并發(fā)同時運行,那就創(chuàng)建一個沒有限制的線程池(Executors.newCachedThreadPool()),并提供給AsyncTask。這樣這個AsyncTask實例就有了自己的線程池而不必使用AsyncTask默認的。
3.新增了二個預定義的線程池SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR 
其實THREAD_POOL_EXECUTOR并不是新增的,之前的就有,只不過之前(Android 2.3)它是AsyncTask私有的,未公開而已。THREAD_POOL_EXECUTOR是一個corePoolSize為5的線程池,也就是說最多只有5個線程同時運行,超過5個的就要等待。所以如果使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)就跟2.3版本的AsyncTask.execute()效果是一樣的。

而SERIAL_EXECUTOR是新增的,它的作用是保證任務執(zhí)行的順序,也就是它可以保證提交的任務確實是按照先后順序執(zhí)行的。它的內(nèi)部有一個隊列用來保存所提交的任務,保證當前只運行一個,這樣就可以保證任務是完全按照順序執(zhí)行的,默認的execute()使用的就是這個,也就是executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)與execute()是一樣的。
前面問題的解法
了解了AsyncTask的內(nèi)幕就知道了前面問題的原因:因為是4.0平臺,所以所有的AsyncTask并不都會運行在單獨的線程中,而是被SERIAL_EXECUTOR順序的使用線程執(zhí)行。因為應用中可能還有其他地方使用AsyncTask,所以到網(wǎng)絡取圖片的AsyncTask也許會等待到其他任務都完成時才得以執(zhí)行而不是調(diào)用executor()之后馬上執(zhí)行。
那么解決方法其實很簡單,要么直接使用Thread,要么創(chuàng)建一個單獨的線程池(Executors.newCachedThreadPool())。或者最簡單的解法就是使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),這樣起碼不用等到前面的都結(jié)束了再執(zhí)行。
AsyncTask的使用注意事項
前面的文章曾建議使用AsyncTask而不是使用Thread,但是AsyncTask似乎又有它的限制,這就要根據(jù)具體的需求情況而選擇合適的工具,No Silver Bullet。下面是一些建議:
?改善你的設計,少用異步處理 
線程的開銷是非常大的,同時異步處理也容易出錯,難調(diào)試,難維護,所以改善你的設計,盡可能的少用異步。對于一般性的數(shù)據(jù)庫查詢,少量的I/O操作是沒有必要啟動線程的。
?與主線程有交互時用AsyncTask,否則就用Thread 
AsyncTask被設計出來的目的就是為了滿足Android的特殊需求:非主線程不能操作(UI)組件,所以AsyncTask擴展Thread增強了與主線程的交互的能力。如果你的應用沒有與主線程交互,那么就直接使用Thread就好了。
?當有需要大量線程執(zhí)行任務時,一定要創(chuàng)建線程池 
線程的開銷是非常大的,特別是創(chuàng)建一個新線程,否則就不必設計線程池之類的工具了。當需要大量線程執(zhí)行任務時,一定要創(chuàng)建線程池,無論是使用AsyncTask還是Thread,因為使用AsyncTask它內(nèi)部的線程池有數(shù)量限制,可能無法滿足需求;使用Thread更是要線程池來管理,避免虛擬機創(chuàng)建大量的線程。比如從網(wǎng)絡上批量下載圖片,你不想一個一個的下,或者5個5個的下載,那么就創(chuàng)建一個CorePoolSize為10或者20的線程池,每次10個或者20個這樣的下載,即滿足了速度,又不至于耗費無用的性能開銷去無限制的創(chuàng)建線程。
?對于想要立即開始執(zhí)行的異步任務,要么直接使用Thread,要么單獨創(chuàng)建線程池提供給AsyncTask 
默認的AsyncTask不一定會立即執(zhí)行你的任務,除非你提供給他一個單獨的線程池。如果不與主線程交互,直接創(chuàng)建一個Thread就可以了,雖然創(chuàng)建線程開銷比較大,但如果這不是批量操作就沒有問題。
?Android的開發(fā)沒有想像中那樣簡單,要多花心思和時間在代碼上和測試上面,以確信程序是優(yōu)質(zhì)的
附上相關(guān)資源:
使用自定義的CorePoolSize為7的Executor(Executors.newFixedThreadPool(7)):
?
?

?
使用未設限制的Executor(Executors.newCachedThreadPool()):

這些例子所用的代碼:
?
?
?
public class AsyncTaskDemoActivity extends Activity {
??? private static int ID = 0;
??? private static final int TASK_COUNT = 9;
??? private static ExecutorService SINGLE_TASK_EXECUTOR;
??? private static ExecutorService LIMITED_TASK_EXECUTOR;
??? private static ExecutorService FULL_TASK_EXECUTOR;
??? static {
??????? SINGLE_TASK_EXECUTOR = (ExecutorService) Executors.newSingleThreadExecutor();
??????? LIMITED_TASK_EXECUTOR = (ExecutorService) Executors.newFixedThreadPool(7);
??????? FULL_TASK_EXECUTOR = (ExecutorService) Executors.newCachedThreadPool();
??? };
??? @Override
??? public void onCreate(Bundle icicle) {
??????? super.onCreate(icicle);
??????? setContentView(R.layout.asynctask_demo_activity);
??????? String title = "AsyncTask of API " + VERSION.SDK_INT;
??????? setTitle(title);
??????? final ListView taskList = (ListView) findViewById(R.id.task_list);
??????? taskList.setAdapter(new AsyncTaskAdapter(getApplication(), TASK_COUNT));
??? }
??? private class AsyncTaskAdapter extends BaseAdapter {
??????? private Context mContext;
??????? private LayoutInflater mFactory;
??????? private int mTaskCount;
??????? List<SimpleAsyncTask> mTaskList;
??????? public AsyncTaskAdapter(Context context, int taskCount) {
??????????? mContext = context;
??????????? mFactory = LayoutInflater.from(mContext);
??????????? mTaskCount = taskCount;
??????????? mTaskList = new ArrayList<SimpleAsyncTask>(taskCount);
??????? }
??????? @Override
??????? public int getCount() {
??????????? return mTaskCount;
??????? }
??????? @Override
??????? public Object getItem(int position) {
??????????? return mTaskList.get(position);
??????? }
??????? @Override
??????? public long getItemId(int position) {
??????????? return position;
??????? }
??????? @Override
??????? public View getView(int position, View convertView, ViewGroup parent) {
??????????? if (convertView == null) {
??????????????? convertView = mFactory.inflate(R.layout.asynctask_demo_item, null);
??????????????? SimpleAsyncTask task = new SimpleAsyncTask((TaskItem) convertView);
??????????????? /*
???????????????? * It only supports five tasks at most. More tasks will be scheduled only after
???????????????? * first five finish. In all, the pool size of AsyncTask is 5, at any time it only
???????????????? * has 5 threads running.
???????????????? */
//??????????????? task.execute();
??????????????? // use AsyncTask#SERIAL_EXECUTOR is the same to #execute();
//??????????????? task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
??????????????? // use AsyncTask#THREAD_POOL_EXECUTOR is the same to older version #execute() (less than API 11)
??????????????? // but different from newer version of #execute()
//??????????????? task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
??????????????? // one by one, same to newer version of #execute()
//??????????????? task.executeOnExecutor(SINGLE_TASK_EXECUTOR);
??????????????? // execute tasks at some limit which can be customized
//??????????????? task.executeOnExecutor(LIMITED_TASK_EXECUTOR);
??????????????? // no limit to thread pool size, all tasks run simultaneously
??????????????? task.executeOnExecutor(FULL_TASK_EXECUTOR);
??????????????? mTaskList.add(task);
??????????? }
??????????? return convertView;
??????? }
??? }
??? private class SimpleAsyncTask extends AsyncTask<Void, Integer, Void> {
??????? private TaskItem mTaskItem;
??????? private String mName;
??????? public SimpleAsyncTask(TaskItem item) {
??????????? mTaskItem = item;
??????????? mName = "Task #" + String.valueOf(++ID);
??????? }
??????? @Override
??????? protected Void doInBackground(Void... params) {
??????????? int prog = 1;
??????????? while (prog < 101) {
??????????????? SystemClock.sleep(100);
??????????????? publishProgress(prog);
??????????????? prog++;
??????????? }
??????????? return null;
??????? }
??????? @Override
??????? protected void onPostExecute(Void result) {
??????? }
??????? @Override
??????? protected void onPreExecute() {
??????????? mTaskItem.setTitle(mName);
??????? }
??????? @Override
??????? protected void onProgressUpdate(Integer... values) {
??????????? mTaskItem.setProgress(values[0]);
??????? }
??? }
}
class TaskItem extends LinearLayout {
??? private TextView mTitle;
??? private ProgressBar mProgress;
??? public TaskItem(Context context, AttributeSet attrs) {
??????? super(context, attrs);
??? }
??? public TaskItem(Context context) {
??????? super(context);
??? }
??? public void setTitle(String title) {
??????? if (mTitle == null) {
??????????? mTitle = (TextView) findViewById(R.id.task_name);
??????? }
??????? mTitle.setText(title);
??? }
??? public void setProgress(int prog) {
??????? if (mProgress == null) {
??????????? mProgress = (ProgressBar) findViewById(R.id.task_progress);
??????? }
??????? mProgress.setProgress(prog);
??? }
}
?
?
?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
??? android:layout_width="match_parent"
??? android:layout_height="match_parent"
??? android:paddingLeft="10dip"
??? android:paddingRight="10dip"
??? android:orientation="vertical" >
??? <ListView android:id="@+id/task_list"
??????? android:layout_width="fill_parent"
??????? android:layout_height="wrap_content"
??????? android:divider="#cccccc"
??????? android:dividerHeight="0.6dip"
??????? android:footerDividersEnabled="true"
??????? android:headerDividersEnabled="true" />
</LinearLayout>
?
?
?
<?xml version="1.0" encoding="utf-8"?>
<com.hilton.effectiveandroid.os.TaskItem xmlns:android="http://schemas.android.com/apk/res/android"
??? android:layout_width="match_parent"
??? android:layout_height="50dip"
??? android:gravity="center_vertical"
??? android:layout_gravity="center_vertical"
??? android:orientation="horizontal" >
??? <TextView android:id="@+id/task_name"
??????? android:layout_width="wrap_content"
??????? android:layout_height="wrap_content"
??????? android:textColor="#ffff00"
??????? android:textSize="26sp" />
??? <ProgressBar android:id="@+id/task_progress"
??????? android:layout_width="fill_parent"
??????? android:layout_height="15dip"
??????? android:max="100"
??????? style="@android:style/Widget.ProgressBar.Horizontal" />
</com.hilton.effectiveandroid.os.TaskItem >
Android開發(fā)筆記之:AsyncTask的應用詳解內(nèi)容武林技術(shù)頻道小編就為各位朋友們介紹到這里了,大家看完之后,是不是受益匪淺呢?