這篇文章主要介紹了AsyncTask怎么用,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
成都創(chuàng)新互聯(lián)公司是專業(yè)的東臺網(wǎng)站建設公司,東臺接單;提供做網(wǎng)站、成都網(wǎng)站建設,網(wǎng)頁設計,網(wǎng)站設計,建網(wǎng)站,PHP網(wǎng)站建設等專業(yè)做網(wǎng)站服務;采用PHP框架,可快速的進行東臺網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
AsyncTask
,相信大家已經(jīng)很熟悉了。它的內(nèi)部封裝了Thread
和Handler
,這讓我們可以將一些耗時操作放到AsyncTask
,并且能將結果及時更新到UI上。AsyncTask
主要用于短時間耗時操作,長時間耗時操作不建議使用AsyncTask
。
private class DownloadFilesTask extends AsyncTask{ 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{} 復制代碼
它接收三個泛型參數(shù),分別表示參數(shù)類型、進度類型、結果類型。
上述的例子中DownloadFilesTask
接收參數(shù)類型為URL
類型,使用Integer
類型表示任務進度,最終的任務結果是一個Long
類型。
注意:上面三個泛型類型不一定都得用一個明確的類型,對于沒有使用的類型,可以使用
Void
類型代替。
繼承AsyncTask
至少需要重寫doInBackground
方法,同時AsyncTask
也提供了另外三個方法供我們重寫,分別是onPreExecute
、onProgressUpdate
、onPostExecute
。
onPreExecute方法。在任務開始執(zhí)行之前執(zhí)行,它運行在UI線程中。通常我們可以在這里展示一個等待進度條。
doInBackground方法。貫穿整個耗時任務,它運行在子線程中。在這里執(zhí)行耗時操作。
onProgressUpdate方法。貫穿整個耗時任務,在publishProgress
方法被調(diào)用后執(zhí)行,它運行在UI線程中。通常用于展示整個任務的一個進度。
onProgressUpdate方法。在任務接收后調(diào)用,doInBackground
的返回結果會透傳給onPostExecute
的參數(shù)值,它運行在主線程中。通常我們從這里獲取任務執(zhí)行完成后的結果數(shù)據(jù)。
AsyncTask
類必須在UI線程加載。(在4.1系統(tǒng)版本以上會自動完成)
AsyncTask
對象必須在UI線程創(chuàng)建,也就是說AsyncTask
的構造方法必須在UI線程中調(diào)用。(經(jīng)過測試AsyncTask
對象可以在子線程創(chuàng)建,只要保證execute
方法在UI線程執(zhí)行就OK的。但是沒有人會這樣做,因為多此一舉?。。。?/p>
execute
方法必須在UI線程中調(diào)用。這樣做是保證onPreExecute
方法運行在UI線程。
不要主動調(diào)用onPreExecute
、doInBackground
、onProgressUpdate
、onProgressUpdate
方法。
單線程下,AsyncTask對象的任務只能執(zhí)行一次,否則會報運行時錯誤。
在AsyncTask
誕生之初,任務是在一個后臺線程中順序執(zhí)行的。從Android 1.6開始,就變成了可以在后臺線程中并行執(zhí)行任務。然后,到了Android 3.0版本,又改成了單線程順序執(zhí)行,以此避免并發(fā)任務產(chǎn)生的錯誤行為。
為了驗證上述結論,下面看一個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{ 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
啟動時,執(zhí)行了六次MyTask
,并將任務執(zhí)行后的時間節(jié)點打印出來。
image.png
手機的系統(tǒng)版本是Android 8.0,從上面的Log信息可以看出,AsyncTask
的確是串行執(zhí)行的。由于現(xiàn)有測試機最低系統(tǒng)版本都是Android 4.1,已經(jīng)很難找到Android 3.0以下的老古董機子了????,所以我們只能通過源碼去驗證Android 1.6到Android 3.0期間,AsyncTask
是否是并行執(zhí)行的。
AsyncTask
是否串行或者并行執(zhí)行,取決于它的execute
方法。
public final AsyncTaskexecute(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
是一個多線程容器,其中可以創(chuàng)建多個線程來執(zhí)行多個任務。由此驗證了Android 1.6版本到Android 3.0版本直接,AsyncTask
執(zhí)行任務的機制的確也現(xiàn)在的機制不一樣,它可以讓任務并行執(zhí)行。
我們對比一下Android 8.0版本的execute
方法。
public final AsyncTaskexecute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }public final AsyncTask executeOnExecutor(Executor exec, Params... params) { ...省略 mWorker.mParams = params; exec.execute(mFuture); return this; } 復制代碼
execute
方法中調(diào)用了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 ArrayDequemTasks = new ArrayDeque (); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); //任務執(zhí)行完畢后繼續(xù)執(zhí)行scheduleNext方法 } } }); if (mActive == null) { //第一個任務會執(zhí)行該方法 scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { //判斷mTask隊列中是否有下一個任務,有則取出來執(zhí)行 THREAD_POOL_EXECUTOR.execute(mActive); } } } 復制代碼
可以看到,在Android 8.0版本中,創(chuàng)建了一個ArrayDeque
隊列,每次只從隊列中獲取一個任務執(zhí)行,執(zhí)行完畢后會繼續(xù)判斷隊列中是否有任務,如果有則取出來執(zhí)行,直到所有任務執(zhí)行完畢為止。由此可見,Android 8.0版本執(zhí)行任務是串行執(zhí)行的。
如果我們想改變AsyncTask
這種默認行為呢,可以修改么?答案是肯定的。
我們可以直接調(diào)用AsyncTask
的executeOnExecutor
方法,并將一個Executor
對象傳遞過去,就能變成并行的執(zhí)行方法了。
對于上面的例子,可以這樣改動。
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{ 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())); } } } 復制代碼
執(zhí)行后,打印出來的Log信息如下圖所示。
image.png
注意:這里前五個Task是同時執(zhí)行的,因為AsyncTask.THREAD_POOL_EXECUTOR創(chuàng)建了五個核心線程,第六個任務需要等待空閑線程才能繼續(xù)執(zhí)行。所以會出現(xiàn)第六個任務和前五個任務執(zhí)行時間不一致的現(xiàn)象,特此說明。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“AsyncTask怎么用”這篇文章對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián),關注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關知識等著你來學習!