Android

AsyncTaskLoaderについて

投稿日:2018年12月3日 更新日:

 

AsyncTaskLoaderの概要

  • AsyncTaskをActivityでインスタンス定義すれば、ライフサイクルは同等となるが、非同期タスク(loadInBackgroundでの処理)とActivityのライフサイクルを別々に関する管理するもので、シングルトンで構成される。
  • 非同期タスクの実行中にActivityが再作成されても、再作成後のActivityで実行結果を受け取る事が可能になる。
  • 非同期処理の枠組を最低限定義したもので、実際に使用する場合は、AsyncTaskLoaderの主要メソッドをいくつかオーバーライドしないと使い物にならない。
  • Activityのライフサイクルと連携している
  • Low Memory KILLでは、LoaderもLoaderで管理されている非同期タスクも消滅するので初期起動と同じフェーズで処理される。よって、Configuration Change発生時のActivityの再作成の時を主な検討点とすれば良い。

 

AsyncTaskLoaderの特徴

AsyncTaskLoaderはAsyncTaskをLoaderの仕組みの中で管理し、処理結果を受け取るコールバックの管理の煩雑さを軽減しようとしてAsyncTaskをLoaderでラップしただけです。実行される非同期タスク(loadInBackgroundでの処理)とLoaderは別物として考える必要があります。基本的にプロセスがKILLされない限り実行中の非同期タスクは実行を続けるようです。よって、Loaderが破棄されても(resetイベントが発生しても)、非同期タスクは実行を続けているようです。Loaderが破棄された場合、実行中の非同期タスクは最後まで実行されますが、Loaderが破棄されているためonLoadFinishedが実行されない状況になるようです。開発者向けオプションの「アクティビティを保持しない」に設定した時、Loader実行中にホームボタンを押下した時にこの状況になるようです。

システムによる破棄処理とLoaderとの関係は、①回転等によるconfiguration changeの場合はLoaderと非同期タスクともに破棄されず引き継ぎ可能です、②Low MemoryによるプロセスKILLはプロセス死滅なので、さすがにLoaderも非同期タスクも破棄されるようです、③開発者向けオプションの「アクティビティを保持しない」に設定した時のアクティビティの破棄はLoaderのみが破棄されて非同期タスクは実行を続けます。項②と項③の場合に、Loaderによる非同期タスクは実行を開始したが、Loader破棄によりonLoadFinishedが実行されない状況になるようです。

AsyncTaskLoaderの非同期タスクにおいて、開始Loader(非同期タスクも含む)と対になる非同期タスクの完了を検知して、onLoadFinishedを確実に実行させる方法はないようです。例えば、同じ処理のタスクを重複して実行させたくない場合などで、タスクの実行中のステータス管理をonLoadFinishedで行っている場合などは注意が必要です。タスクの進捗状況を表示している場合もonLoadFinishedの確実なコールは期待できないようです。いろいろなケースにおいてonLoadFinishedがコールされるか否かを検討するのであれば、EventBusなどを使って非同期タスク処理の中で完了状況を伝達した方が、onLoadFinishedより確実性が増すと思われます。しかし、プロセスKILLによる非同期タスクの破棄は非同期タスクの完了を捉える事が出来ないので、結果的には、確実に非同期タスクの完了を検知する事は出来ない事になりそうです。Low MemoryによるプロセスKILLの場合も、Loaderも非同期タスクも途中で破棄されるので、onLoadFinishedは実行されない。どうしても、onLoadFinishedが確実に発生させるべき処理であれば、システムの破棄処理によるLoader(非同期タスク)の引き継ぎはあきらめて、破棄された以前のLoaderや非同期タスクは無視するとして、onCreate発生都度にLoader(非同期タスク)を新規作成して、onLoadFinishedが発生してくれるのを待つしかない。

AsyncTaskもAsyncTaskLoaderにおいて、Activity破棄による非同期タスクを引き継ぐケースを考えた場合、①非同期タスク終了のコールバック処理(onLoadFinished)はコールされないケースが存在する事を検討した方が良い。②よって、UI側のコールバック処理でなく、なるべく非同期タスク(loadInBackground)の終了で終了処理を行った方が良い、③非同期処理は実行されるとプロセス破棄まで実行を継続するので影響がないようにする(同じ処理の重複実行など)、④UI側の通知(終了も含めて)はEventBusなどを使ってUI側のコールバック処理(onLoadFinished)より確実性を上げる。⑤もしくは、引き継ぐ事をあきらめて、以前の非同期タスクは無視して、都度、新規タスクを発生させる事が可能な仕様とする。進捗状況を表示したり、極端に重い処理だったり、タスク実行中のステータス管理などした場合は、Activity破棄による非同期タスクを引き継ぐ必要があるので、処理が難しくなる。

onCreateメソッドにおけるinitLoaderやrestartLoaderの記述パターン

  1. 初期の画面表示処理など: 画面処理時間が極端に長くなければ、configuration changeもLow Memoryの場合も同様に扱う事が可能で、以前のタスク実行は関係なく、新規の作成したLoaderとタスクが実行中、及び、実行後に新しいUIスレッドを通信できれば良いと思います。onCreateメソッドの実行都度、新規にLoaderを作成しても良いのではないかと思います。onLoadFinishedで必ず処理をさせたいのであれば、onCreateメソッドでrestartLoaderを実行させて、都度、新規にLoaderを再作成するしかないと思います。。
  2. 画面表示後の操作処理(操作ボタンによる処理):最初からやり直しても良い処理であれば、上記と同様に、以前のタスク実行は関係なく、新規にLoderを作成しても良いと思います(restartLoderメソッド)。また、再操作してもらえる仕様なら、onCreateで新規にLoaderを作成する事も不要と思います。進捗経過などを表示している場合や処理が重い場合などで、以前の処理を引き継ぎたい場合は、onCreateのinitLoaderメソッドでローダ(非同期タスク)を引き継ぐ事を検討して、同時にonLoadFinishedが実行されない対応を検討する必要があると思われます。

AsyncTaskLoaderの注意点

AsyncTaskLoaderの実行結果をonLoadFinishedによってメインスレッドの画面や変数に反映する場合は、その対象に注意する必要がある。

  • 非同期タスクの実行完了で、onLoadFinishedが確実に発生するとは限らない。2重登録などに気を付ける。
  • onLoadFinishedは何度か発生する可能性がある。onLoadFinishedでLoaderをDestroyすれば良いと思う。
  • Activityがバックグランドに遷移した時にAsyncTaskLoaderを止めるか否か。onPauseに遷移すれば、LowMemoryKillが発生する可能性があるので、必要な情報はバンドルに保存して、いつ死んでも良い準備をしておく。バックタスクでAsyncTaskLoaderが終了して結果を反映させても、onPauseでバンドルに保存した状態とは異なるフォアグラウンドに遷移できれば良いがバックグランドのままLowMemoryKillが発生すれば、実行結果はバンドルに保存されない。AsyncTaskLoaderの実行は終わっているので、その結果がActivityの再作成で表現出来れば問題ないが、実行結果をバンドルに保存すべき情報の場合は問題が発生する。

isLoadInBackgroundCanceled

  • isLoadInBackgroundCanceled() は cancelLoad() が実行されないと Trueにならない。 
  • よって、destroyLoade()の実行(ローダーの破棄)で、isLoadInBackgroundCanceled() をTrueにしたい場合は、onReset でcancelLoad() を実行する必要がある。

 

AsyncTaskLoaderの仕様(Activity側)

initLoader

  • ローダー開始機能
  • 処理を始めたい時、又は、既に走っている処理にLoaderCallbacksを再度設定したいときに呼びます。
  • LoaderManagerではonDestroy時にLoaderCallbacksは破棄されるため、Activity再起動時のLoaderCallbacksの再設定が必要です。

restartLoader

  • initLoaderとrestartLoaderは、どちらもローダー開始機能を有しているのは同じですが、両者の違いは既存のローダー(同じIDのローダー)が存在していた場合に現れる。既存のローダーを「再利用する」のか、「破棄してローダーを再作成する」のかという違い。
  • initLoader() … 再利用する。destroyLoaderが実行されない状態(ローダーが破棄されていない状態)で、initLoaderの2度目以降の実行ではonCreateLoaderはコールされず、既にloadInBackgroundでの処理が終わっていれば、何もコールされないまま、いきなり、onLoadFinishedが実行される。
  • restartLoader() … 破棄してローダーを再作成する

 

AsyncTaskLoaderの仕様(ローダ側)

cancelLoad

  • isLoadInBackgroundCanceled() を True にする。 
  • destroyLoade()の実行(ローダーの破棄)で、isLoadInBackgroundCanceled() をTrueにしたい場合は、onReset でcancelLoad() を実行する必要がある。

 

AsyncTaskLoaderの使用場面における考察

画面の初期表示

複数の画像をダウンロードして、ダウンロードが完了した画像から画面のグリッド上に順次表示するようなケース

  • ActivityのonCeatedにinitLoader()を記述として、処理開始とActivity再起動時の再利用を一緒に定義する。
  • 順次、結果を反映させるので、onLoadFinishedが発生しない場合でも融通がきく。
  • 処理中ダイアログような表示は必要ない

 

ボタン押下で重い処理の実行

DB登録処理など行うケース

  • ボタン押下時の開始処理とActivity再起動時の再利用処理を分けて管理する必要がある。
  • 登録結果で処理を行う場面が多い(onLoadFinishedが重要となる)
  • onLoadFinishedが発生しない場合を想定する必要がある。
  • 処理中であることを利用者に示すダイアログが必要。処理中に他の操作はさせたくない。
  • Activityがバッグタスクに移動した時でも登録処理は行いたい。特に処理が重い場合は、登録処理中に他のアプリを利用させたい。

対応検討案

  • 画面回転(configuration change)によるActivity再作成時の登録処理継続をあきらめる。ActivityのDestroyでAsyncTaskLoaderをDestroyして、非同期処理を完了させる事ができる。
  • 処理中のダイアログを表示する処理も考えなくて良い。

(configuration changeでAsyncTaskLoaderを止めようが継続させようが、再作成されたActivityと連携させようが連携しないに関係なく、どのような条件でも検討が必要な事)

  • onLoadFinishedが発生しない想定はどのような条件でも行う必要がある。
  • Activityが作成、再作成された初期処理において、どのような条件でも、以前に非同期処理が実行されていたかもしれない(その結果も分からない)・・・事を考慮する必要がある。

EventBus対応検討案

  • onLoadFinishedが発生しない場合を想定すれば、あえて、AsyncTaskLoaderを使う必要がないのでは。
  • 使っても良いが、メインスレッドとの通信はインターフェースのコールバックでなく、EventBusを使えば、Activity再起動時に再作成後との連携も考えなくて良い。
  • 実行中のダイアログ表示も非同期タスク側でEventBusを使って実行中を教えてやれば、それを受けて実行中はダイアログを表示するだけで良い。
  • DialogFragmentの場合は、Activityの再作成でも表示するが、EventBusの実行中の信号がなければ、表示しないとすれば良い。
  • 実行中の表現はProgressBarなどにして、処理が終わっても実行後の状態で画面をそのままにしておくようなUIが良いのではないか。ダイアログ表示した場合は、処理の完了で確実にダイアログを消す必要がある。なるべくやりっぱなしの考えが良い。

 

 

 

 

参考にしたページ

正しいAsyncTaskLoaderの使い方

initLoader()とrestartLoader()のどちらを使うか

 

 

スポンサーリンク

スポンサーリンク

-Android

執筆者:

関連記事

Activityの復元について

  参考ページ 【Android】savedInstanceStateの意味と開発者オプション【初心者向け】 Android の罠 [1] ちゃんと onSaveInstanceState ...

Android開発のリストビューに関するTIPS

android開発のリストビューに関するTIPSをまとめました。   ListViewの使い方 業務アプリによるListViewの使い方のポイント 基本的に表示するだけの一覧表示するだけの機 ...

Android開発TIPS

android開発TIPSをまとめました。   androidのswitch文で「定数式が必要です」エラーが発生する対応 android開発で、switchのcase文にR.idを入れたらエ ...

Android開発のTIPS WEBページ 

  スレッドから画面操作 スレッドからUIを操作する編集する Android 非同期処理についてまとめてみた 別スレッドでキュー管理(Handler, Looper, HandlerThre ...

Android StudioのTIPS

Android StudioのTIPSをまとめました。   Android Studioで解決できないコンパイルエラーが発生した時の対応 今まで問題がなかったプロジェクトが、急にコンパイルエ ...