標籤:android style blog io ar color os 使用 sp
AsyncTask是Android給開發人員提供的一個簡單輕量級的多線程類,通過它我們可以很容易建立一個線程讓在後台做一些耗時的操作(如IO操作、網路訪問等),並在這個過程中更新UI。之所以說它輕量級,是因為不需要直接使用Handler、Thread等知識,使用起來比較簡單,但也失去了一些靈活性,對於一些複雜的情境處理起來不方便。
如果一個APP進程中同時只建立和運行一個AsyncTask執行個體,則不會有任何問題。但如果在一個進程中如果有多個AsyncTask任務同時在執行,問題就比較複雜了。下面我們通過例子來看(我們例子是在Android 4中啟動並執行)。
一、測試1(預設多個Task是串列執行的)
1、建立一個預設的app工程
2、建立一個類繼承AsyncTask,代碼如下
package com.example.asynctaskdemo;import android.os.AsyncTask;public class MyAsyncTask extends AsyncTask<String, Void, String>{private String name;public MyAsyncTask(String name){this.name = name;}@Overrideprotected String doInBackground(String... params) {System.out.println(name+" is run "+System.currentTimeMillis()+" thread id "+Thread.currentThread().getId());try {Thread.sleep(1000*10);} catch (InterruptedException e) {e.printStackTrace();}return null;}}
3、在Activity的onCreate方法中使用該Task,代碼如下
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);for(int i=1;i<5;i++){MyAsyncTask task = new MyAsyncTask("task"+i);task.execute(new String[0]);}}
我們在調試視窗,觀察MyAsyncTask列印資訊的間隔和順序。發現這建立的4個任務是串列執行的,並不是並發的。
研究了AsyncTask的實現細節,在建立一個AsyncTask並通過其execute方法啟動執行時,AsyncTask並不是建立一個獨立的線程去執行。AsyncTask是通過線程池來管理和調度進程中的所有Task的。
在 Android2.3以前的版本(SDK/API 大於等於10的版本)
多個AsyncTask任務是並發執行的,也就是說如果啟動多個task,則會並發執行。但並發執行的數量取決於AsyncTask內部的線程池限制數量。如果超過了這個限額,新的任務只能等待。
在Android 3.0及以後版本(SDK/API 大於等於11的版本)
Google從Android 3.0開始對AsyncTask的調度執行做出了一些變化,對於execute提交的任務,按先後順序每次只運行一個。也就是說它是按提交的次序,每次只啟動一個線程執行一個任務,完成之後再執行第二個任務,也就是相當於只有一個後台線程在執行所提交的任務。上面的例子就驗證了這一點。
二、讓AsyncTask並發執行
因為預設情況下多個task是串列的,那怎麼樣讓並發執行呢?AsyncTask增加了一個新的介面executeOnExecutor,這個介面允許開發人員提供自訂的線程池來運行和調度Thread。我們把上面代碼改下:
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);for(int i=1;i<11;i++){MyAsyncTask task = new MyAsyncTask("task"+i);//task.execute(new String[0]);task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new String[0]);}}
這裡我們使用了executeOnExecutor方法代替了execute方法。並且executeOnExecutor方法的第一個參數是一個預定義的線程池。這時這幾個task就可以並發執行了。這時我們觀察列印的結果,發現有5個任務並發執行,可以看出有5個不同的線程號,查看AsyncTask的源碼,發現並發線程數跟裝置的cpu數量是有關的,因此不同的裝置上可能看到的結果不完全一致,這點需要注意。只有前面的5個任務執行完後,才會執行後面的,並且通過列印的線程號可以看出,後面執行的任務是重用原來的線程,並沒有建立新的線程,這就是線程池的作用。
我們再來改下代碼,使用Android提供的另外一個預定義的線程池。代碼如下:
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);for(int i=1;i<11;i++){MyAsyncTask task = new MyAsyncTask("task"+i);//task.execute(new String[0]);task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, new String[0]);}}
觀察列印資訊我們可以發現,這和調用execute方法一樣,每個任務都是串列執行的。並且這個過程中最多建立了5個新的線程。
三、自訂線程池
我們可以利用java.util.concurrent.Executors中的各種靜態方法建立供AsyncTask執行的線程池 ,可以指定線程的數量和調度的方式。其方法很多,我們這裡介紹其中兩種較為常用的。
1、讓每個AsyncTask任務都單獨起一個線程執行,也就是說所有的都是並發的。代碼如:
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();for(int i=1;i<11;i++){MyAsyncTask task = new MyAsyncTask("task"+i);task.executeOnExecutor(newCachedThreadPool, new String[0]);}}
通過觀察列印可以看出,這多個任務都是並發執行的。
2、建立指定線程數量的線程池,並發數上限就是指定的線程數。但新任務產生,沒有閒置線程,就只能等待。代碼如:
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);for(int i=1;i<11;i++){MyAsyncTask task = new MyAsyncTask("task"+i);task.executeOnExecutor(newFixedThreadPool, new String[0]);}}
通過觀察可以看出,有3個線程在並發執行。
總結下,從上面的例子中可以看出,如果一個進程中存在多個TASK需要並發執行的情況,那就需要用到AsyncTask一些更深的知識,需要考慮的問題更多。
Android學習筆記:多個AsyncTask執行個體的並發問題