您好,登錄后才能下訂單哦!
這篇文章主要介紹了AsyncTask怎么用,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
AsyncTask
,相信大家已經很熟悉了。它的內部封裝了Thread
和Handler
,這讓我們可以將一些耗時操作放到AsyncTask
,并且能將結果及時更新到UI上。AsyncTask
主要用于短時間耗時操作,長時間耗時操作不建議使用AsyncTask
。
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected void onPreExecute() { showProgress(); } protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } } 復制代碼
AsyncTask
是一個抽象類,我們要使用時必須自定義一個類繼承于它。AsyncTask
的原型為:
public abstract class AsyncTask<Params, Progress, Result> {} 復制代碼
它接收三個泛型參數,分別表示參數類型、進度類型、結果類型。
上述的例子中DownloadFilesTask
接收參數類型為URL
類型,使用Integer
類型表示任務進度,最終的任務結果是一個Long
類型。
注意:上面三個泛型類型不一定都得用一個明確的類型,對于沒有使用的類型,可以使用
Void
類型代替。
繼承AsyncTask
至少需要重寫doInBackground
方法,同時AsyncTask
也提供了另外三個方法供我們重寫,分別是onPreExecute
、onProgressUpdate
、onPostExecute
。
onPreExecute方法。在任務開始執行之前執行,它運行在UI線程中。通常我們可以在這里展示一個等待進度條。
doInBackground方法。貫穿整個耗時任務,它運行在子線程中。在這里執行耗時操作。
onProgressUpdate方法。貫穿整個耗時任務,在publishProgress
方法被調用后執行,它運行在UI線程中。通常用于展示整個任務的一個進度。
onProgressUpdate方法。在任務接收后調用,doInBackground
的返回結果會透傳給onPostExecute
的參數值,它運行在主線程中。通常我們從這里獲取任務執行完成后的結果數據。
AsyncTask
類必須在UI線程加載。(在4.1系統版本以上會自動完成)
AsyncTask
對象必須在UI線程創建,也就是說AsyncTask
的構造方法必須在UI線程中調用。(經過測試AsyncTask
對象可以在子線程創建,只要保證execute
方法在UI線程執行就OK的。但是沒有人會這樣做,因為多此一舉!!!)
execute
方法必須在UI線程中調用。這樣做是保證onPreExecute
方法運行在UI線程。
不要主動調用onPreExecute
、doInBackground
、onProgressUpdate
、onProgressUpdate
方法。
單線程下,AsyncTask對象的任務只能執行一次,否則會報運行時錯誤。
在AsyncTask
誕生之初,任務是在一個后臺線程中順序執行的。從Android 1.6開始,就變成了可以在后臺線程中并行執行任務。然后,到了Android 3.0版本,又改成了單線程順序執行,以此避免并發任務產生的錯誤行為。
為了驗證上述結論,下面看一個Demo例子。
public class MainActivity extends Activity { public static final String TAG = "MyApplication"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new MyTask("task1").execute(); new MyTask("task2").execute(); new MyTask("task3").execute(); new MyTask("task4").execute(); new MyTask("task5").execute(); new MyTask("task6").execute(); } private class MyTask extends AsyncTask<Void, Void, Void> { private String taskName; MyTask(String taskName) { this.taskName = taskName; } @Override protected Void doInBackground(Void... integers) { try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void aVoid) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Log.e(TAG, taskName + " finish at: " + df.format(new Date())); } } } 復制代碼
這個例子比較簡單,就是在MainActivity
啟動時,執行了六次MyTask
,并將任務執行后的時間節點打印出來。
image.png
手機的系統版本是Android 8.0,從上面的Log信息可以看出,AsyncTask
的確是串行執行的。由于現有測試機最低系統版本都是Android 4.1,已經很難找到Android 3.0以下的老古董機子了????,所以我們只能通過源碼去驗證Android 1.6到Android 3.0期間,AsyncTask
是否是并行執行的。
AsyncTask
是否串行或者并行執行,取決于它的execute
方法。
public final AsyncTask<Params, Progress, Result> execute(Params... params) { ...省略 mWorker.mParams = params; sExecutor.execute(mFuture); return this; } 復制代碼
而execute
方法中通過sExecutor
,實際為ThreadPoolExecutor
對象,它的初始化如下所示。
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory); 復制代碼
ThreadPoolExecutor
是一個多線程容器,其中可以創建多個線程來執行多個任務。由此驗證了Android 1.6版本到Android 3.0版本直接,AsyncTask
執行任務的機制的確也現在的機制不一樣,它可以讓任務并行執行。
我們對比一下Android 8.0版本的execute
方法。
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { ...省略 mWorker.mParams = params; exec.execute(mFuture); return this; } 復制代碼
execute
方法中調用了executeOnExecutor
方法,并將sDefaultExecutor
作為Executor
對象傳遞進去,sDefaultExecutor
的初始化如下所示。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;public static final Executor SERIAL_EXECUTOR = new SerialExecutor();private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); //任務執行完畢后繼續執行scheduleNext方法 } } }); if (mActive == null) { //第一個任務會執行該方法 scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { //判斷mTask隊列中是否有下一個任務,有則取出來執行 THREAD_POOL_EXECUTOR.execute(mActive); } } } 復制代碼
可以看到,在Android 8.0版本中,創建了一個ArrayDeque
隊列,每次只從隊列中獲取一個任務執行,執行完畢后會繼續判斷隊列中是否有任務,如果有則取出來執行,直到所有任務執行完畢為止。由此可見,Android 8.0版本執行任務是串行執行的。
如果我們想改變AsyncTask
這種默認行為呢,可以修改么?答案是肯定的。
我們可以直接調用AsyncTask
的executeOnExecutor
方法,并將一個Executor
對象傳遞過去,就能變成并行的執行方法了。
對于上面的例子,可以這樣改動。
public class MainActivity extends Activity { public static final String TAG = "MyApplication"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new MyTask("task1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task6").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private class MyTask extends AsyncTask<Void, Void, Void> { private String taskName; MyTask(String taskName) { this.taskName = taskName; } @Override protected Void doInBackground(Void... integers) { try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void aVoid) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Log.e(TAG, taskName + " finish at: " + df.format(new Date())); } } } 復制代碼
執行后,打印出來的Log信息如下圖所示。
image.png
注意:這里前五個Task是同時執行的,因為AsyncTask.THREAD_POOL_EXECUTOR創建了五個核心線程,第六個任務需要等待空閑線程才能繼續執行。所以會出現第六個任務和前五個任務執行時間不一致的現象,特此說明。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“AsyncTask怎么用”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。