android-asynctask asynctask用法 - 適用於長時間運行的Android AsyncTask




android教學 cancel (5)

為什麼?

因為默認情況下, AsyncTask使用了一個你沒有創建的線程池。 永遠不要捆綁你沒有創建的池中的資源,因為你不知道池的要求是什麼。 如果該池的文檔告訴你不要這樣做,那麼不要將池中的資源捆綁在一起,因為這種情況就是這樣。

特別是,從Android 3.2開始,默認情況下AsyncTask使用的線程池(對於android:targetSdkVersion設置為13或更高的應用程序)只有一個線程 - 如果無限期地綁定此線程,則不會執行其他任務會跑。

它說,引用在here找到的AsyncTask的文檔

理想情況下,AsyncTasks應該用於短操作(最多幾秒)。如果您需要保持線程長時間運行,強烈建議您使用java.util.concurrent pacakge提供的各種API,例如執行器,ThreadPoolExecutor和FutureTask。

現在我的問題出現了,為什麼? doInBackground函數運行在UI線程之外。 那麼在這裡進行長時間的手術會有什麼危害?


這是一個非常好的問題,作為一名Android程序員需要時間來充分理解這個問題。 的確,AsyncTask有兩個相關的主要問題:

  • 他們與活動生命週期的關係很差
  • 他們很容易造成內存洩漏。

RoboSpice Motivations應用程序( 在Google Play上可用 )中,我們詳細回答了這個問題。 它將深入了解AsyncTasks,Loaders及其功能和缺點,並向您介紹網絡請求的替代解決方案:RoboSpice。 網絡請求是Android中的常見要求,本質上是長時間運行的操作。 以下是該應用的摘錄:

AsyncTask和Activity生命週期

AsyncTasks不遵循活動實例的生命週期。 如果你在一個Activity中啟動一個AsyncTask並且你旋轉了這個設備,那麼這個Activity將被銷毀並且一個新的實例將被創建。 但AsyncTask不會死。 它會繼續活著直到完成。

完成後,AsyncTask將不會更新新Activity的UI。 事實上,它更新了以前不再顯示的活動實例。 這可能會導致java.lang.IllegalArgumentException類型的異常:如果您使用findViewById檢索Activity中的視圖,則視圖不會附加到窗口管理器。

內存洩漏問題

創建AsyncTasks作為活動的內部類非常方便。 由於AsyncTask在任務完成或正在進行時需要操作Activity的視圖,因此使用Activity的內部類似乎很方便:內部類可以直接訪問外部類的任何字段。

不過,這意味著內部類將在其外部類實例上保留一個不可見的引用:活動。

從長遠來看,這會產生內存洩漏:如果AsyncTask持續很長時間,它會保持活動“活躍”,而Android則希望擺脫它,因為它不能再顯示。 該活動無法進行垃圾回收,這是Android保留設備資源的中心機制。

對於長時間運行的操作,使用AsyncTasks確實是一個非常糟糕的主意。 儘管如此,對於短暫的生活,例如在1或2秒後更新視圖,它們都很好。

我鼓勵你下載RoboSpice Motivations應用程序 ,它真正解釋了這一點,並提供了一些後台操作的不同方式的示例和演示。


Aysnc任務是專門的線程,仍然意味著與您的應用程序GUI一起使用,但同時保持UI線程的資源繁重的任務。 因此,當更新列表,更改視圖等需要您執行一些提取操作或更新操作時,您應該使用異步任務,以便您可以將這些操作保留在UI線程之外,但請注意這些操作仍以某種方式連接到UI 。

對於不需要UI更新的長時間運行的任務,您可以改用服務,因為即使沒有UI,它們也可以存活。

因此,對於短期任務,使用異步任務是因為在產卵活動消失後(通常不會中途停止,但會完成任務),操作系統可能會被操作系統殺死。 而對於長期和重複的任務,請改用服務。

欲了解更多信息,請參閱主題:

AsyncTask超過幾秒鐘?

即使活動已被破壞,AsyncTask也不會停止


AsyncTask的問題在於,如果它被定義為活動的非靜態內部類,它將具有對活動的引用。 在異步任務容器完成的活動中,但AsyncTask中的後台工作繼續的情況下,活動對像不會被垃圾回收,因為存在對其的引用,這會導致內存洩漏。

解決這個問題的方法是將異步任務定義為靜態內部活動 ,並對上下文使用較弱的引用。

但是,將它用於簡單和快速的後台任務是一個不錯的主意。 要使用乾淨的代碼開發應用程序,最好使用RxJava來運行複雜的後台任務並使用其結果更新UI。


移動這兩行:

TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");

在你的AsyncTask的doInBackground方法中,並將它們放在onPostExecute方法中。 你的AsyncTask應該是這樣的:

private class LongOperation extends AsyncTask<String, Void, String> {

    @Override
    protected String doInBackground(String... params) {
        try {
            Thread.sleep(5000); // no need for a loop
        } catch (InterruptedException e) {
            Log.e("LongOperation", "Interrupted", e);
            return "Interrupted";
        }
        return "Executed";
    }      

    @Override
    protected void onPostExecute(String result) {               
        TextView txt = (TextView) findViewById(R.id.output);
        txt.setText(result);
    }
}




android android-asynctask