溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

AsyncTask怎么用

發(fā)布時(shí)間:2021-12-14 16:17:09 來源:億速云 閱讀:195 作者:小新 欄目:移動(dòng)開發(fā)

這篇文章主要介紹了AsyncTask怎么用,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

引言

AsyncTask,相信大家已經(jīng)很熟悉了。它的內(nèi)部封裝了ThreadHandler,這讓我們可以將一些耗時(shí)操作放到AsyncTask,并且能將結(jié)果及時(shí)更新到UI上。AsyncTask主要用于短時(shí)間耗時(shí)操作,長時(shí)間耗時(shí)操作不建議使用AsyncTask

一個(gè)例子

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");
  }
 }
復(fù)制代碼

AsyncTask是一個(gè)抽象類,我們要使用時(shí)必須自定義一個(gè)類繼承于它。AsyncTask的原型為:

public abstract class AsyncTask<Params, Progress, Result> {}
復(fù)制代碼

它接收三個(gè)泛型參數(shù),分別表示參數(shù)類型、進(jìn)度類型、結(jié)果類型。

上述的例子中DownloadFilesTask接收參數(shù)類型為URL類型,使用Integer類型表示任務(wù)進(jìn)度,最終的任務(wù)結(jié)果是一個(gè)Long類型。

注意:上面三個(gè)泛型類型不一定都得用一個(gè)明確的類型,對于沒有使用的類型,可以使用Void類型代替。

繼承AsyncTask至少需要重寫doInBackground方法,同時(shí)AsyncTask也提供了另外三個(gè)方法供我們重寫,分別是onPreExecute、onProgressUpdate、onPostExecute。

  • onPreExecute方法。在任務(wù)開始執(zhí)行之前執(zhí)行,它運(yùn)行在UI線程中。通常我們可以在這里展示一個(gè)等待進(jìn)度條。

  • doInBackground方法。貫穿整個(gè)耗時(shí)任務(wù),它運(yùn)行在子線程中。在這里執(zhí)行耗時(shí)操作。

  • onProgressUpdate方法。貫穿整個(gè)耗時(shí)任務(wù),在publishProgress方法被調(diào)用后執(zhí)行,它運(yùn)行在UI線程中。通常用于展示整個(gè)任務(wù)的一個(gè)進(jìn)度。

  • onProgressUpdate方法。在任務(wù)接收后調(diào)用,doInBackground的返回結(jié)果會(huì)透傳給onPostExecute的參數(shù)值,它運(yùn)行在主線程中。通常我們從這里獲取任務(wù)執(zhí)行完成后的結(jié)果數(shù)據(jù)。

AsyncTask的規(guī)則

  • AsyncTask類必須在UI線程加載。(在4.1系統(tǒng)版本以上會(huì)自動(dòng)完成)

  • AsyncTask對象必須在UI線程創(chuàng)建,也就是說AsyncTask的構(gòu)造方法必須在UI線程中調(diào)用。(經(jīng)過測試AsyncTask對象可以在子線程創(chuàng)建,只要保證execute方法在UI線程執(zhí)行就OK的。但是沒有人會(huì)這樣做,因?yàn)槎啻艘慌e?。。。?/p>

  • execute方法必須在UI線程中調(diào)用。這樣做是保證onPreExecute方法運(yùn)行在UI線程。

  • 不要主動(dòng)調(diào)用onPreExecute、doInBackgroundonProgressUpdate、onProgressUpdate方法。

  • 單線程下,AsyncTask對象的任務(wù)只能執(zhí)行一次,否則會(huì)報(bào)運(yùn)行時(shí)錯(cuò)誤。

AsyncTask執(zhí)行任務(wù)的規(guī)則

AsyncTask誕生之初,任務(wù)是在一個(gè)后臺(tái)線程中順序執(zhí)行的。從Android 1.6開始,就變成了可以在后臺(tái)線程中并行執(zhí)行任務(wù)。然后,到了Android 3.0版本,又改成了單線程順序執(zhí)行,以此避免并發(fā)任務(wù)產(chǎn)生的錯(cuò)誤行為。

為了驗(yàn)證上述結(jié)論,下面看一個(gè)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()));
        }
    }
}
復(fù)制代碼

這個(gè)例子比較簡單,就是在MainActivity啟動(dòng)時(shí),執(zhí)行了六次MyTask,并將任務(wù)執(zhí)行后的時(shí)間節(jié)點(diǎn)打印出來。

AsyncTask怎么用

image.png

手機(jī)的系統(tǒng)版本是Android 8.0,從上面的Log信息可以看出,AsyncTask的確是串行執(zhí)行的。由于現(xiàn)有測試機(jī)最低系統(tǒng)版本都是Android 4.1,已經(jīng)很難找到Android 3.0以下的老古董機(jī)子了????,所以我們只能通過源碼去驗(yàn)證Android 1.6到Android 3.0期間,AsyncTask是否是并行執(zhí)行的。

源碼分析

Android 2.3版本

AsyncTask是否串行或者并行執(zhí)行,取決于它的execute方法。

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    ...省略 
    mWorker.mParams = params;
    sExecutor.execute(mFuture);    return this;
}
復(fù)制代碼

execute方法中通過sExecutor,實(shí)際為ThreadPoolExecutor對象,它的初始化如下所示。

private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
復(fù)制代碼

ThreadPoolExecutor是一個(gè)多線程容器,其中可以創(chuàng)建多個(gè)線程來執(zhí)行多個(gè)任務(wù)。由此驗(yàn)證了Android 1.6版本到Android 3.0版本直接,AsyncTask執(zhí)行任務(wù)的機(jī)制的確也現(xiàn)在的機(jī)制不一樣,它可以讓任務(wù)并行執(zhí)行。

Android 8.0版本

我們對比一下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;
}
復(fù)制代碼

execute方法中調(diào)用了executeOnExecutor方法,并將sDefaultExecutor作為Executor對象傳遞進(jìn)去,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(); //任務(wù)執(zhí)行完畢后繼續(xù)執(zhí)行scheduleNext方法
                }
            }
        });        if (mActive == null) { //第一個(gè)任務(wù)會(huì)執(zhí)行該方法
            scheduleNext();
        }
    }    protected synchronized void scheduleNext() {        if ((mActive = mTasks.poll()) != null) { //判斷mTask隊(duì)列中是否有下一個(gè)任務(wù),有則取出來執(zhí)行
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}
復(fù)制代碼

可以看到,在Android 8.0版本中,創(chuàng)建了一個(gè)ArrayDeque隊(duì)列,每次只從隊(duì)列中獲取一個(gè)任務(wù)執(zhí)行,執(zhí)行完畢后會(huì)繼續(xù)判斷隊(duì)列中是否有任務(wù),如果有則取出來執(zhí)行,直到所有任務(wù)執(zhí)行完畢為止。由此可見,Android 8.0版本執(zhí)行任務(wù)是串行執(zhí)行的。

如果我們想改變AsyncTask這種默認(rèn)行為呢,可以修改么?答案是肯定的。

我們可以直接調(diào)用AsyncTaskexecuteOnExecutor方法,并將一個(gè)Executor對象傳遞過去,就能變成并行的執(zhí)行方法了。

對于上面的例子,可以這樣改動(dòng)。

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()));
        }
    }
}
復(fù)制代碼

執(zhí)行后,打印出來的Log信息如下圖所示。

AsyncTask怎么用

image.png

注意:這里前五個(gè)Task是同時(shí)執(zhí)行的,因?yàn)锳syncTask.THREAD_POOL_EXECUTOR創(chuàng)建了五個(gè)核心線程,第六個(gè)任務(wù)需要等待空閑線程才能繼續(xù)執(zhí)行。所以會(huì)出現(xiàn)第六個(gè)任務(wù)和前五個(gè)任務(wù)執(zhí)行時(shí)間不一致的現(xiàn)象,特此說明。

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“AsyncTask怎么用”這篇文章對大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI