1. 程式人生 > >Android 深入解析AsyncTask(doInBackground不工作)

Android 深入解析AsyncTask(doInBackground不工作)

近日開發遇到AsyncTask的doInBackground()方法不執行的問題,所以在網上查詢原因,以下博文解決了我的問題,我用Thread代替了AysncTask進行工作。博文如下: 地址:http://blog.csdn.net/hitlion2008/article/details/7983449

AsyncTask引發的一個問題

上週遇到了一個極其詭異的問題,一個小功能從網路上下載一個圖片,然後放到ImageView中,是用AsyncTask來實現的,本身邏輯也很簡單,僅是在doInBackground中用HTTP請求把圖片的輸入流取出,然後用BitmapFactory去解析,然後再把得到的Bitmap放到ImageView中。這個應用是用4.0的SDK開發的,也是執行在4.0上面的。但是有時候下載這張圖片去要用很久很久,甚至要等上幾分鐘。通過除錯發現一個令人難以接受的事實:竟然是doInBackground()未及時執行,也就是它並沒有在#execute()呼叫之後馬上執行,而是等待了很久才得以執行。

神馬情況,難道AsyncTask不是執行緒,難道不是非同步,難道AsyncTask另有內幕?

AsyncTask的內幕

AsyncTask主要有二個部分:一個是與主線各的互動,另一個就是執行緒的管理排程。雖然可能多個AsyncTask的子類的例項,但是AsyncTask的內部Handler和ThreadPoolExecutor都是程序範圍內共享的,其都是static的,也即屬於類的,類的屬性的作用範圍是CLASSPATH,因為一個程序一個VM,所以是AsyncTask控制著程序範圍內所有的子類例項。

與主執行緒互動

與主執行緒互動是通過Handler來進行的,因為本文主要探討AsyncTask在任務排程方面的,所以對於這部分不做細緻介紹,感興趣的朋友可以去看

AsyncTask的原始碼

執行緒任務的排程

內部會建立一個程序作用域的執行緒池來管理要執行的任務,也就就是說當你呼叫了AsyncTask#execute()後,AsyncTask會把任務交給執行緒池,由執行緒池來管理建立Thread和執行Therad。對於內部的執行緒池不同版本的Android的實現方式是不一樣的:

Android2.3以前的版本,也即SDK/API 10和以前的版本

內部的執行緒池限制是5個,也就是說同時只能有5個執行緒執行,超過的執行緒只能等待,等待前面的執行緒某個執行完了才被排程和執行。換句話說,如果一個程序中的AsyncTask例項個數超過5個,那麼假如前5個都執行很長時間的話,那麼第6個只能等待機會了。這是AsyncTask的一個限制,而且對於2.3以前的版本無法解決。如果你的應用需要大量的後臺執行緒去執行任務,那麼你只能放棄使用AsyncTask,自己建立執行緒池來管理Thread,或者乾脆不用執行緒池直接使用Thread也無妨。不得不說,雖然AsyncTask較Thread使用起來比較方便,但是它最多隻能同時執行5個執行緒,這也大大侷限了它的實力,你必須要小心的設計你的應用,錯開使用AsyncTask的時間,盡力做到分時,或者保證數量不會大於5個,否則就可能遇到上面提到的問題。要不然就只能使用JavaSE中的API了。


Android 3.0以後,也即SDK/API 11和以後的版本

可能是Google意識到了AsyncTask的侷限性了,從Android 3.0開始對AsyncTask的API做出了一些調整:

  1. #execute()提交的任務,按先後順序每次只執行一個

    也就是說它是按提交的次序,每次只啟動一個執行緒執行一個任務,完成之後再執行第二個任務,也就是相當於只有一個後臺執行緒在執行所提交的任務(Executors.newSingleThreadPool())。


  2. 新增了介面#executeOnExecutor()

    這個介面允許開發者提供自定義的執行緒池來執行和排程Thread,如果你想讓所有的任務都能併發同時執行,那就建立一個沒有限制的執行緒池(Executors.newCachedThreadPool()),並提供給AsyncTask。這樣這個AsyncTask例項就有了自己的執行緒池而不必使用AsyncTask預設的。

  3. 新增了二個預定義的執行緒池SERIAL_EXECUTORTHREAD_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是新增的,它的作用是保證任務執行的順序,也就是它可以保證提交的任務確實是按照先後順序執行的。它的內部有一個佇列用來儲存所提交的任務,保證當前只執行一個,這樣就可以保證任務是完全按照順序執行的,預設的execute()使用的就是這個,也就是executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)與execute()是一樣的。

前面問題的解法

瞭解了AsyncTask的內幕就知道了前面問題的原因:因為是4.0平臺,所以所有的AsyncTask並不都會執行在單獨的執行緒中,而是被SERIAL_EXECUTOR順序的使用執行緒執行。因為應用中可能還有其他地方使用AsyncTask,所以到網路取圖片的AsyncTask也許會等待到其他任務都完成時才得以執行而不是呼叫executor()之後馬上執行。

那麼解決方法其實很簡單,要麼直接使用Thread,要麼建立一個單獨的執行緒池(Executors.newCachedThreadPool())。或者最簡單的解法就是使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),這樣起碼不用等到前面的都結束了再執行。

AsyncTask的使用注意事項

前面的文章曾建議使用AsyncTask而不是使用Thread,但是AsyncTask似乎又有它的限制,這就要根據具體的需求情況而選擇合適的工具,No Silver Bullet。下面是一些建議:

  • 改善你的設計,少用非同步處理

    執行緒的開銷是非常大的,同時非同步處理也容易出錯,難除錯,難維護,所以改善你的設計,儘可能的少用非同步。對於一般性的資料庫查詢,少量的I/O操作是沒有必要啟動執行緒的。

  • 與主執行緒有互動時用AsyncTask,否則就用Thread

    AsyncTask被設計出來的目的就是為了滿足Android的特殊需求:非主執行緒不能操作(UI)元件,所以AsyncTask擴充套件Thread增強了與主執行緒的互動的能力。如果你的應用沒有與主執行緒互動,那麼就直接使用Thread就好了。

  • 當有需要大量執行緒執行任務時,一定要建立執行緒池

    執行緒的開銷是非常大的,特別是建立一個新執行緒,否則就不必設計執行緒池之類的工具了。當需要大量執行緒執行任務時,一定要建立執行緒池,無論是使用AsyncTask還是Thread,因為使用AsyncTask它內部的執行緒池有數量限制,可能無法滿足需求;使用Thread更是要執行緒池來管理,避免虛擬機器建立大量的執行緒。比如從網路上批量下載圖片,你不想一個一個的下,或者5個5個的下載,那麼就建立一個CorePoolSize為10或者20的執行緒池,每次10個或者20個這樣的下載,即滿足了速度,又不至於耗費無用的效能開銷去無限制的建立執行緒。

  • 對於想要立即開始執行的非同步任務,要麼直接使用Thread,要麼單獨建立執行緒池提供給AsyncTask

    預設的AsyncTask不一定會立即執行你的任務,除非你提供給他一個單獨的執行緒池。如果不與主執行緒互動,直接建立一個Thread就可以了,雖然建立執行緒開銷比較大,但如果這不是批量操作就沒有問題。

  • Android的開發沒有想像中那樣簡單,要多花心思和時間在程式碼上和測試上面,以確信程式是優質的

附上相關資源:

使用自定義的CorePoolSize為7的Executor(Executors.newFixedThreadPool(7)):


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


這些例子所用的程式碼:

[java] view plaincopyprint?
  1. publicclass AsyncTaskDemoActivity extends Activity {  
  2.     privatestaticint ID = 0;  
  3.     privatestaticfinalint TASK_COUNT = 9;  
  4.     privatestatic ExecutorService SINGLE_TASK_EXECUTOR;  
  5.     privatestatic ExecutorService LIMITED_TASK_EXECUTOR;  
  6.     privatestatic ExecutorService FULL_TASK_EXECUTOR;  
  7.     static {  
  8.         SINGLE_TASK_EXECUTOR = (ExecutorService) Executors.newSingleThreadExecutor();  
  9.         LIMITED_TASK_EXECUTOR = (ExecutorService) Executors.newFixedThreadPool(7);  
  10.         FULL_TASK_EXECUTOR = (ExecutorService) Executors.newCachedThreadPool();  
  11.     };  
  12.     @Override
  13.     publicvoid onCreate(Bundle icicle) {  
  14.         super.onCreate(icicle);  
  15.         setContentView(R.layout.asynctask_demo_activity);  
  16.         String title = "AsyncTask of API " + VERSION.SDK_INT;  
  17.         setTitle(title);  
  18.         final ListView taskList = (ListView) findViewById(R.id.task_list);  
  19.         taskList.setAdapter(new AsyncTaskAdapter(getApplication(), TASK_COUNT));  
  20.     }  
  21.     privateclass AsyncTaskAdapter extends BaseAdapter {  
  22.         private Context mContext;  
  23.         private LayoutInflater mFactory;  
  24.         privateint mTaskCount;  
  25.         List<SimpleAsyncTask> mTaskList;  
  26.         public AsyncTaskAdapter(Context context, int taskCount) {  
  27.             mContext = context;  
  28.             mFactory = LayoutInflater.from(mContext);  
  29.             mTaskCount = taskCount;  
  30.             mTaskList = new ArrayList<SimpleAsyncTask>(taskCount);  
  31.         }  
  32.         @Override
  33.         publicint getCount() {  
  34.             return mTaskCount;  
  35.         }  
  36.         @Override
  37.         public Object getItem(int position) {  
  38.             return mTaskList.get(position);  
  39.         }  
  40.         @Override
  41.         publiclong getItemId(int position) {  
  42.             return position;  
  43.         }  
  44.         @Override
  45.         public View getView(int position, View convertView, ViewGroup parent) {  
  46.             if (convertView == null) {  
  47.                 convertView = mFactory.inflate(R.layout.asynctask_demo_item, null);  
  48.                 SimpleAsyncTask task = new SimpleAsyncTask((TaskItem) convertView);  
  49.                 /* 
  50.                  * It only supports five tasks at most. More tasks will be scheduled only after 
  51.                  * first five finish. In all, the pool size of AsyncTask is 5, at any time it only 
  52.                  * has 5 threads running. 
  53.                  */
  54. //                task.execute();
  55.                 // use AsyncTask#SERIAL_EXECUTOR is the same to #execute();
  56. //                task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
  57.                 // use AsyncTask#THREAD_POOL_EXECUTOR is the same to older version #execute() (less than API 11)
  58.                 // but different from newer version of #execute()
  59. //                task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  60.                 // one by one, same to newer version of #execute()
  61. //                task.executeOnExecutor(SINGLE_TASK_EXECUTOR);
  62.                 // execute tasks at some limit which can be customized
  63. //                task.executeOnExecutor(LIMITED_TASK_EXECUTOR);
  64.                 // no limit to thread pool size, all tasks run simultaneously
  65.                 task.executeOnExecutor(FULL_TASK_EXECUTOR);  
  66.                 mTaskList.add(task);  
  67.             }  
  68.             return convertView;  
  69.         }  
  70.     }  
  71.     privateclass SimpleAsyncTask extends AsyncTask<Void, Integer, Void> {  
  72.         private TaskItem mTaskItem;  
  73.         private String mName;  
  74.         public SimpleAsyncTask(TaskItem item) {  
  75. 相關推薦

    Android 深入解析AsyncTaskdoInBackground工作

    近日開發遇到AsyncTask的doInBackground()方法不執行的問題,所以在網上查詢原因,以下博文解決了我的問題,我用Thread代替了AysncTask進行工作。博文如下: 地址:http://blog.csdn.net/hitlion2008/article/details/7983

    android 個別機型Toast顯示出來問題

    我用的是魅藍note升級5.1之後,開發應用Toat無法顯示。網上找了好多方法,大致有兩種 1、手動方案:設定——>應用軟體管理——>對應APP——>勾選顯示通知框 2、替代方

    Android JSON解析JSONObject的使用

        在沒有了解Android之前,以為跟C++一樣,解析json資料都是外部開源庫來處理的,但是Android自帶就有json解析器!言歸正傳,使用Android原生的Json解析器來解析json資料,這裡只跟大家提及需要注意的一點:使用JSONObject時,需要加上t

    Android深入解析】Manifest配置檔案解析英文版

    <action> 語法規則: <action android:name="string"/> 描述 : Adds an action to an intentfilter. An elementmust contain one or more

    Android查缺補漏線程篇-- AsyncTask的使用及原理詳細分析

    catch 返回 rri 理解 ams tee ive lean keyword 本文作者:CodingBlock 文章鏈接:http://www.cnblogs.com/codingblock/p/8515304.html 一、AsyncTask的使用 AsyncT

    Cannot resolve symbol KeyEventCompatandroid.support.v4.view.KeyEventCompat找

    blog forward CA res tco sdn hand andro exe Cannot resolve symbol KeyEventCompat(android.support.v4.view.KeyEventCompat找不到) 解決方案 KeyEventC

    深入解析Vue中的computed工作原理

    我們通過實現一個簡單版的和Vue中computed具有相同功能的函式來了解computed是如何工作的。 JS屬性: JavaScript有一個特性是 Object.defineProperty ,它能做很多事,但我在這篇文章只專注於這個方法中的一個: var perso

    Android : 跟我學Binder --- (2) AIDL分析及手動實現 Android : 跟我學Binder --- (1) 什麼是Binder IPC?為何要使用Binder機制? Android : 跟我學Binder --- (3) 深入程式碼實戰假裝有連結

    一、關於Android日常開發中程序間通訊-AIDL   通常Android應用開發中實現程序間通訊用的最多的就是 AIDL,藉助 AIDL 編譯以後的程式碼能幫助我們進一步理解 Binder IPC 的通訊原理。但是無論是從可讀性還是可理解性上來看,編譯器生成的程式碼對開發者並不友好。比如一個 INano

    深入解析Struts攔截器的工作原理

    Struts2中有三大核心:ognl,攔截器和action;想要學好Struts2,這三個核心是重中之重;這裡就給大家介紹一下Struts2攔截器的工作原理 。 什麼是攔截器 攔截器是在請求到達Action前後進行的一系列操作,Struts2預設自帶了很大攔

    Android原始碼解析-->Launcher啟動流程

    上一篇文章中我們講解了關於SystemServer程序相關的知識,我們知道SystemServer程序主要用於啟動系統的各種服務,二者其中就包含了負責啟動Launcher的服務,LauncherAppService。具體更多關於SystenServer的啟動

    Android原始碼解析十三-->apk安裝流程

    上一篇文章中給大家分析了一下android系統啟動之後呼叫PackageManagerService服務並解析系統特定目錄,解析apk檔案並安裝的過程,這個安裝過程實際上是沒有圖形介面的,底層呼叫的是我們平時比較熟悉的adb命令,那麼我們平時安裝apk檔案的

    Android原始碼解析十一-->應用程序啟動流程

    本節主要是通過分析Activity的啟動過程介紹應用程式程序的啟動流程。關於Android的應用程序在android guide中有這樣的一段描述: By default, every application runs in its own Linu

    Android原始碼解析-->IntentService

    上一篇文章中我們講解了HandlerThread,其本質上是一個Thread,只不過內部定義了其自身的Looper和MessageQueue。為了讓多個執行緒之間能夠方便的通訊,我們會使用Handler實現執行緒間的通訊。這個時候我們手動實現的多執行緒+Ha

    Android原始碼解析-->SystemServer程序啟動流程

    上面一文中我們講過android系統中比較重要的幾個程序:init程序,Zygote程序,SystemServer程序已經各種應用程序,其中Zygote程序是整個android系統的根程序,包含SystemServer程序已經各種應用程序在內的程序都是通過Z

    Android原始碼解析-->HandlerThread

    上一篇文章中我們講解了AsyncTast的基本使用以及實現原理,我們知道AsyncTask內部是通過執行緒池和Handler實現的,通過對執行緒池和handler的封裝實現了對非同步任務操作。更多關於AsyncTask相關的內容,可參考我的android原始

    Android原始碼解析-->LruCache快取類

    android開發過程中經常會用到快取,現在主流的app中圖片等資源的快取策略一般是分兩級,一個是記憶體級別的快取,一個是磁碟級別的快取。 作為android系統的維護者google也開源了其快取方案,LruCache和DiskLruCache。從andr

    Android基礎之AsyncTaskdoInBackground方法引數詳解

    1、對於耗時的操作,我們的一般方法是開啟“子執行緒”。如果需要更新UI,則需要使用handler 2、如果耗時的操作太多,那麼我們需要開啟太多的子執行緒,這就會給系統帶來巨大的負擔,隨之也會帶來效能方面的問題。在這種情況下我們就可以考慮使用類AsyncTask來非同步執行任務,不需要子執行緒和handler

    android深入解析Activity的launchMode啟動模式,Intent Flag,taskAffinity

      最近看到一篇文章講launchMode,想到以前的一次面試,就問了這一個問題,最基本的大家都知道,但是詳細的我就迷糊了,最終失敗了,所以在此總結一下,希望能夠幫助一下大家   launchMode分為四種:    standard   s

    深入解析OutOfMemoryError

    在Java中,所有物件都儲存在堆中。他們通過new關鍵字來進行分配,JVM會檢查是否所有執行緒都無法在訪問他們了,並且會將他們進行回收。在大多數時候程式設計師都不會有一絲一毫的察覺,這些工作都被靜悄悄的執行。但是,有時候在釋出前的最後一天,程式掛了。 Exception

    Android之ExpandableListView的屬性Group展開

    1. 設定ExpandableListView 預設是展開的: 先例項化exListView 然後  exListView.setAdapter(exlvAdapter);    //遍歷所有group,將所有項設定成預設展開  intgroupCount = exL