關於AsyncTask的介紹和基本使用方法能夠參考官方文檔和Android實戰技巧:多線程AsyncTask這裏就不重複。html
上週遇到了一個極其詭異的問題,一個小功能從網絡上下載一個圖片,而後放到ImageView中,是用AsyncTask來實現的,自己邏輯也很簡單,僅是在doInBackground中用HTTP請求把圖片的輸入流取出,而後用BitmapFactory去解析,而後再把獲得的Bitmap放到ImageView中。這個應用是用4.0的SDK開發的,也是運行在4.0上面的。可是有時候下載這張圖片去要用好久好久,甚至要等上幾分鐘。經過調試發現一個使人難以接受的事實:居然是doInBackground()未及時執行,也就是它並無在#execute()調用以後立刻執行,而是等待了好久才得以執行。神馬狀況,難道AsyncTask不是線程,難道不是異步,難道AsyncTask另有內幕?java
AsyncTask主要有二個部分:一個是與主線程的交互,另外一個就是線程的管理調度。雖然可能有多個AsyncTask子類的實例,可是AsyncTask的內部Handler和ThreadPoolExecutor都是進程範圍內共享的,其都是static的,也即屬於類的,類的屬性的做用範圍是CLASSPATH,由於一個進程一個VM,因此是AsyncTask控制着進程範圍內全部的子類實例。android
與主線程交互是經過Handler來進行的,由於本文主要探討AsyncTask在任務調度方面的,因此對於這部分不作細緻介紹,感興趣的朋友能夠去看AsyncTask的源碼git
內部會建立一個進程做用域的線程池來管理要運行的任務,也就就是說當你調用了AsyncTask#execute()後,AsyncTask會把任務交給線程池,由線程池來管理建立Thread和運行Therad。對於內部的線程池不一樣版本的Android的實現方式是不同的。github
內部的線程池限制是5個,也就是說同時只能有5個線程運行,超過的線程只能等待,等待前面的線程某個執行完了才被調度和運行。換句話說,若是一個進程中的AsyncTask實例個數超過5個,那麼假如前5個都運行很長時間的話,那麼第6個只能等待機會了。這是AsyncTask的一個限制,並且對於2.3之前的版本沒法解決。若是你的應用須要大量的後臺線程去執行任務,那麼你只能放棄使用AsyncTask,本身建立線程池來管理Thread,或者乾脆不用線程池直接使用Thread也無妨。不得不說,雖然AsyncTask較Thread使用起來比較方便,可是它最多隻能同時運行5個線程,這也大大侷限了它的實力,你必需要當心的設計你的應用,錯開使用AsyncTask的時間,盡力作到分時,或者保證數量不會大於5個,不然就可能遇到上面提到的問題。要否則就只能使用JavaSE中的API了。數據庫
多是Google意識到了AsyncTask的侷限性了,從Android 3.0開始對AsyncTask的API作出了一些調整:網絡
這個接口容許開發者提供自定義的線程池來運行和調度Thread,若是你想讓全部的任務都能併發同時運行,那就建立一個沒有限制的線程池(Executors.newCachedThreadPool()),並提供給AsyncTask。這樣這個AsyncTask實例就有了本身的線程池而沒必要使用AsyncTask默認的。多線程
其實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是新增的,它的做用是保證任務執行的順序,也就是它能夠保證提交的任務確實是按照前後順序執行的。它的內部有一個隊列用來保存所提交的任務,保證當前只運行一個,這樣就能夠保證任務是徹底按照順序執行的,默認的execute()使用的就是這個,也就是executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)與execute()是同樣的。less
瞭解了AsyncTask的內幕就知道了前面問題的緣由:由於是4.0平臺,因此全部的AsyncTask並不都會運行在單獨的線程中,而是被SERIAL_EXECUTOR順序的使用線程執行。由於應用中可能還有其餘地方使用AsyncTask,因此到網絡取圖片的AsyncTask也許會等待到其餘任務都完成時才得以執行而不是調用executor()以後立刻執行。
那麼解決方法其實很簡單,要麼直接使用Thread,要麼建立一個單獨的線程池(Executors.newCachedThreadPool())。或者最簡單的解法就是使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),這樣起碼不用等到前面的都結束了再執行。
前面的文章曾建議使用AsyncTask而不是使用Thread,可是AsyncTask彷佛又有它的限制,這就要根據具體的需求狀況而選擇合適的工具,No Silver Bullet。下面是一些建議:
線程的開銷是很是大的,同時異步處理也容易出錯,難調試,難維護,因此改善你的設計,儘量的少用異步。對於通常性的數據庫查詢,少許的I/O操做是沒有必要啓動線程的。
AsyncTask被設計出來的目的就是爲了知足Android的特殊需求:非主線程不能操做(UI)組件,因此AsyncTask擴展Thread加強了與主線程的交互的能力。若是你的應用沒有與主線程交互,那麼就直接使用Thread就行了。
線程的開銷是很是大的,特別是建立一個新線程,不然就沒必要設計線程池之類的工具了。當須要大量線程執行任務時,必定要建立線程池,不管是使用AsyncTask仍是Thread,由於使用AsyncTask它內部的線程池有數量限制,可能沒法知足需求;使用Thread更是要線程池來管理,避免虛擬機建立大量的線程。好比從網絡上批量下載圖片,你不想一個一個的下,或者5個5個的下載,那麼就建立一個CorePoolSize爲10或者20的線程池,每次10個或者20個這樣的下載,即知足了速度,又不至於耗費無用的性能開銷去無限制的建立線程。
默認的AsyncTask不必定會當即執行你的任務,除非你提供給他一個單獨的線程池。若是不與主線程交互,直接建立一個Thread就能夠了,雖然建立線程開銷比較大,但若是這不是批量操做就沒有問題。
附上相關資源:使用自定義的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 >
轉自:http://blog.csdn.net/hitlion2008/article/details/7983449