AsyncTask小記

<code>AsyncTask</code>,異步任務,顧名思義。
簡單說一下使用場景就是:

  • 有耗時邏輯、數(shù)據(jù)操作
  • 耗時在(最多)幾秒以內
  • 操作后需要更新UI層面的變化

<code>AsyncTask</code>是<code>Handler</code>和<code>Thread</code>的一種封裝,但并不是一種通用的線程框架,官方推薦了引用<code>AsyncTask</code>的場景(在引言中所提及的),既然它并不通用,那么了解它到時何時應該用就變得尤為重要。

Talk it by use it

首先,要明確的是要實現(xiàn)一個<code>AsyncTask</code>需要做什么:

  • 構造器:

      private class MyTask extends AsyncTask<Void, Void, Void> { ... }
    

你會發(fā)現(xiàn)當我們要構造一個<code>AsyncTask</code>時,需要傳入三個類型,三個類型分別代表的是<code>Params</code>, <code>Progress</code>, <code>Result</code>,下文再提用處。

  • 可以<code>Override</code>的方法有:
    onPreExecute() //執(zhí)行前的準備

      doInBackground(Params... params)                    //耗時操作的邏輯處理
    
      onProgressUpdate(Progress... progress)              //進度的更新
    
      onPostExecute(Result result)                        //結果的處理
    

現(xiàn)在意義就很明了了,<code>Params</code>指代的是耗時操作的參數(shù),<code>Progress</code>用于更新進度,<code>Result</code>用于更新結果。顯然<code>onProgressUpdate</code>和<code>onPostExecute</code>中是用于更新UI的變化的。

  • 用法需要注意的地方
  • You can specify the type of the parameters, the progress values, and the final value of the task, using generics
  • The method <code>doInBackground()</code> executes automatically on a worker thread
  • <code>onPreExecute()</code>, <code>onPostExecute()</code>, and <code>onProgressUpdate()</code> are all invoked on the UI thread
  • The value returned by <code>doInBackground()</code> is sent to <code>onPostExecute()</code>
    You can call <code>publishProgress()</code> at anytime in <code>doInBackground()</code> to execute <code>onProgressUpdate()</code> on the UI thread
  • You can cancel the task at any time, from any thread
    <div align = "right">摘自官網(wǎng)文檔 鏈接</div>

翻譯
  • 構造器的三個參數(shù)類型為泛型,可自定義
  • <code>doInBackground()</code>自動在工作線程(即非UI線程) 中執(zhí)行
  • <code>onPreExecute()</code>,<code>onPostExecute()</code>,<code>onProgressUpdate()</code>全都在UI線程中執(zhí)行
  • <code>doInBackground()</code>的返回值作為<code>onPostExecute()</code>的參數(shù),<code>publishProgress()</code>可以在<code>doInBackground()</code>中調用,AsyncTask內部會回調<code>onProgressUpdate()</code>,參數(shù)為<code>pulishProgress()</code>的參數(shù)
  • 任何時候都可以從任意線程取消異步任務

怕翻譯的不好所以貼了一份原文

用法也就是這樣了,想了想我覺得也不用再做過多的贅述。

Now Talk about HOW

那么到底<code>AsyncTask</code>表現(xiàn)如何?
?最明顯的一點就是,它讓耗時操作更新UI變得十分簡單,開發(fā)者幾乎不需要做太多的東西就可以完成這一目的,與此相比自己實現(xiàn)<code>Thread</code>還有<code>Hanlder</code>變得略為繁瑣。

當然上述的是使用<code>AsyncTask</code>的優(yōu)點,還是要說一說缺點的。
?從起因開始說,一個<code>AsyncTask</code>,它的類的加載以及實例化需要在UI線程中實現(xiàn),那么在這個實現(xiàn)過程中,任務的內部就會持有一個對上下文對象的引用(<code>Activity</code>),用于更新UI的操作,那么這個上下文對象的持有就需要開發(fā)者考慮引用是否會丟失或者改變。例如,屏幕旋轉引起的Activity recreate、任務還未結束時退出當前Activity等等。這些情況會有可能導致程序直接crash,或者引起內存泄露,那么我們就必須兼顧到<code>AsyncTask</code>結束任務的處理。
?官方文檔中包含了這樣的一段demo:

   private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
        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");
        }
   }  

我們可以看到在<code>doInBackground</code>中,每一次的循環(huán)最后都加入了一個<code>isCancelled()</code>的判斷,為什么呢?雖然<code>AsyncTask</code>提供了<code>cancel(boolean)</code>方法,其中的參數(shù)可以控制任務是否應被中斷,我手動寫了個簡單的循環(huán)在<code>doInBackground()</code>中

    @Override
    protected void onProgressUpdate(Void... values) {
        Log.e("progress", progress + "");
    }

    @Override
    protected Void doInBackground(Void... params) {
        Log.e("Task", "in");
        while (isRunning && progress < 10) {
            progress++;
            try {
                Thread.sleep(500);
                publishProgress();
                if (isCancelled())
                    Log.e("Task", "cancel");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

然后我在執(zhí)行到一半的時候就調用了<code>cancel(true)</code>,請注意是<code>true</code>!按道理講,這樣的調用過后,任務應當被中斷,然后去回調<code>onCancel()</code>,然而真相是<code>doInBackground()</code>還是會繼續(xù)執(zhí)行,但是<code>publishProgress()</code>被赤裸裸的忽略掉了。也就是說,即便你在外部調用了<code>cancel()</code>方法,你也不能確保<code>doInBackground()</code>中到底會不會發(fā)生異常,因此官方的文檔才會有上面的那段demo,在<code>doInBackground()</code>中判斷任務是否已經被取消并做出相關操作,例如結束掉網(wǎng)絡請求、終止文件的讀取等等我就不再多舉例。
?然而最重點的應該是,在<code>onCancel()</code>中究竟應該如何操作——請注意,如果調用了<code>cancel()</code>方法,不論<code>doInBackground()</code>是否已經正常的執(zhí)行完畢(<code>onCancel(Result)</code>的參數(shù),并不能保證是一個不為空的值,并且Google推薦當開發(fā)者自己實現(xiàn)<code>onCancel(Result)</code>時,不要調用<code>super.onCancel(result)</code>),<code>onCancel()</code>是一定會被執(zhí)行的,那么我們就需要在<code>onCancel()</code>當中考慮不再對不再需要駐留內存的對象保持引用,最常見的就是上下文對象了。
?另外,我們還可以做的是,將對對象的引用更換為弱引用,也算是多加了一層保險了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容