1. ホーム
  2. android

Activityが破棄されてもAsyncTaskが停止しない

2023-08-19 16:06:01

質問

私は AsyncTask オブジェクトが実行されると、このオブジェクトは実行を開始します。 Activity が作成されると実行を開始し、バックグラウンドで何かを行います(最大100枚の画像をダウンロードします)。すべてがうまくいくのですが、理解できない奇妙な動作があります。

例えば アンドロイドの画面の向きが変わると Activity は破棄され、再度作成されます。そこで、私は onRetainNonConfigurationInstance() メソッドをオーバーライドし、実行されたすべてのダウンロードされたデータの保存を AsyncTask . このようにする目的は AsyncTask が実行されるたびに Activity が破壊され作成されるのですが、ログで見る限り、前の AsyncTask は実行されたままです。(データは正しく保存されていますが)

をキャンセルしようとしても AsyncTask の中にある onDestroy() メソッドを作成しましたが、ログにはまだ AsyncTask が実行されていると表示されます。

これは本当に不思議な動作です。 AsyncTask .

どのように解決するのですか?

ロマンガイの回答は正しいです。しかし、私は情報を補足し、長時間実行する AsyncTask、さらにネットワーク指向の Asynctask に使用できるライブラリまたは 2 つへのポインタを提供したいと思います。

AsyncTasksはバックグラウンドで何かを行うために設計されています。そして、そうです、あなたは cancel メソッドを使って停止できます。インターネットから何かをダウンロードする場合、私は強くお勧めします。 が IO ブロッキング状態になっているときは、 スレッドに気を配ることです。 . あなたは、次のようにダウンロードを整理する必要があります。

public void download() {
    //get the InputStream from HttpUrlConnection or any other
    //network related stuff
    while( inputStream.read(buffer) != -1 && !Thread.interrupted() ) {
      //copy data to your destination, a file for instance
    }
    //close the stream and other resources
}

を使うことで Thread.interrupted フラグを使用すると、スレッドがブロッキングされた io 状態を適切に終了できるようになります。を呼び出すと、スレッドがより反応するようになります。 cancel メソッドの呼び出しに反応しやすくなります。

AsyncTaskの設計上の欠陥

しかし、AsyncTaskがあまりにも長く続くと、2つの異なる問題に直面することになります。

  1. アクティビティがライフサイクルとうまく連携しておらず、アクティビティが終了するとAsyncTaskの結果を得られない。確かに、そうですが、それは荒っぽい方法でしょう。
  2. AsyncTaskはあまりよく文書化されていない。直感的ではあるが、素朴な実装と非同期タスクの使用は、すぐにメモリリークにつながる可能性がある。

RoboSpice 今回紹介するライブラリ RoboSpice は、このようなリクエストを実行するためにバックグラウンドサービスを使用しています。これはネットワークからのリクエストを想定して設計されている。また、リクエストの結果を自動的にキャッシュするなどの機能も備えている。

ここで、AsyncTasksが長時間実行されるタスクに向かない理由を説明します。以下の理由は、以下の記事から抜粋したものです。 RoboSpiceの動機 : RoboSpice を使うことが Android プラットフォームのニーズを満たすことになる理由を説明したアプリからの抜粋です。

AsyncTask と Activity のライフサイクル

AsyncTaskはActivityインスタンスのライフサイクルに従わない。Activity内でAsyncTaskを起動し、デバイスを回転させると、Activityは破棄され、新しいインスタンスが作成されます。しかし、AsyncTaskは死なない。完了するまで生き続けるのです。

そして完了したとき、AsyncTaskは新しいActivityのUIを更新しません。実際、もう表示されていないアクティビティの以前のインスタンスが更新されます。 が表示されなくなります。これは、java.lang.IllegalArgumentException.View not attached to window manager というタイプの例外につながる可能性があります。ビューがウィンドウマネージャにアタッチされていない場合 を使用している場合、例えば、findViewByIdでアクティビティ内のビューを取得します。

メモリリークの問題

アクティビティの内部クラスとしてAsyncTasksを作成するのは非常に便利です。AsyncTask はタスクが完了または進行しているときに Activity のビューを操作する必要があるため、Activity の内部クラスを使用することは非常に便利です。 タスクが完了または進行中のとき、Activityの内部クラスを使用することは便利です:内部クラスは、外部クラスの任意のフィールドに直接アクセスできます。 内部クラスは外部クラスの任意のフィールドに直接アクセスすることができます。

とはいえ、これは内部クラスが外部クラスであるActivityの不可視の参照を保持することを意味します。

AsyncTaskが長く続くと、アクティビティが生かされます。 一方、Android は、もはや表示できないので、それを取り除きたいと考えています。アクティビティはガベージコレクトされないので、これはAndroidがリソースを保存するための中心的なメカニズムです。 Android がデバイス上のリソースを維持するための中心的なメカニズムです。

タスクの進行状況は失われます

長時間稼働する非同期タスクを作成し、そのライフサイクルをアクティビティのライフサイクルに従って管理するために、いくつかの回避策を使うことができます。次のいずれかを行います。 アクティビティのonStopメソッドでAsyncTaskをキャンセルする。 または、非同期タスクを終了させ、その進捗を失わないようにして アクティビティの次のインスタンスに再リンクさせます。 .

これは可能で、RobopSpiceのmotivationsでその方法を紹介していますが、複雑になってしまい、コードも汎用的ではありません。さらに、ユーザがアクティビティから離れ、戻ってきた場合、タスクの進行状況は失われたままです。これと同じ問題はローダーにも現れますが、上記の再リンクによるAsyncTaskの回避策と同等により単純なものになります。

Android サービスを使用する

最良の選択肢は、長時間実行されるバックグラウンド タスクを実行するためにサービスを使用することです。これはまさに RoboSpice が提案するソリューションです。繰り返しますが、これはネットワーク用に設計されていますが、ネットワークに関連しないものにも拡張できます。このライブラリには 多数の機能 .

のおかげで、30秒足らずでそのイメージをつかむこともできます。 インフォグラフィックス .


AsyncTasksを長時間実行する操作に使用するのは、本当に非常に悪い考えです。しかし、1~2秒後にビューを更新するような短時間実行のものには問題ありません。

私はあなたが RoboSpice Motivationsアプリ このアプリは、ネットワークに関連するさまざまな方法のサンプルやデモを提供し、この問題を深く説明しています。


ネットワークに関連しないタスク(たとえばキャッシュなし)で RoboSpice の代替品をお探しなら、次のサイトも参照してください。 テープ .