タイトルは釣りです。すいません。
AsyncTaskLoaderを気軽に使えるように整理したのでメモとして残しておきます。
基本形
- 別スレッドでの処理はAsyncTaskLoaderのloadInBackgroundに記述
- loadInBackgroundで完了した処理データをUIスレッドで操作するにはLoaderManager.LoaderCallbacksのonLoadFinishedに記述
supportLoaderManager
supportLoaderManagerがinitLoader()を実行してLoader(別スレッド)が起動。supportLoaderManagerはActivity内で一つしか存在しない- Activity内で複数のLoaderを扱いたい時はinitLoader()の第一引数
idを変更して管理します。 - 例えば、
supportLoaderManager.initLoader(1, null, callbacksA)とした後、supportLoaderManager.initLoader(1, null, callbacksB)としてもcallbacksAに定義されたローダーが再び呼び出される。正しくはidを変更してsupportLoaderManager.initLoader(2, null, callbacksB)とします。
- Activity内で複数のLoaderを扱いたい時はinitLoader()の第一引数
LoaderManagerのコールバック
initLoader()にはLoaderManagerがコールバックするLoaderの生成処理、Loaderがロードを完了した時の処理、Loaderがリセットされた時の処理のインターフェース実装を渡します。initLoader()時にLoaderの生成処理がコールバックされます。
- Loaderの生成処理は
AsyncTaskLoaderを実装したオブジェクトを生成してLoaderManagerに返します。
AsyncTaskLoaderの実装
- AsyncTaskLoaderは
実際にバックグラウンド(別スレッド)で行う処理とバックグランドで処理を開始する準備が整った時に呼び出される処理(onStartLoading)を記述します。- 理由はよくわからないのですが、私の環境では
initLoader()後、ActivityのonResumeがトリガされたタイミングでinitLoaderせずともonStartLoadingが呼び出されるような挙動を確認しました。必要に応じて、正しいAsyncTaskLoaderの使い方 - Qiita この記事のように再ロード防止のフラグを設けます。
- 理由はよくわからないのですが、私の環境では
上記の実装の最小構成が以下となります。
val context = this //supportLoaderManagerがコールバックするメソッドの実装 val callbacks = object : LoaderManager.LoaderCallbacks<String> { //ローダー生成の実装 override fun onCreateLoader(id: Int, args: Bundle?): Loader<String> { return object : AsyncTaskLoader<String>(context) { //生成したローダーが行う処理の実装 override fun loadInBackground(): String? { return null } } override fun onStartLoading() { forceLoad() } } } override fun onLoadFinished(loader: Loader<String>, data: String?) { } override fun onLoaderReset(loader: Loader<String>) { } } //ローダーの起動 supportLoaderManager.initLoader( 1, null, callbacks )
ジェネリクスはloadInBackgroundで返却されるオブジェクトの型です。大半の場合はStringで良いかと思います。
インターフェース実装をメソッド化して取り扱う
他の各Activityや同じActivity内で複数のローダーが必要な場合など実装を生成しやすいようメソッド化します。 各、コールバックメソッドの実装をエンクロージャ引数として渡すようにすれば、呼び出し側から各処理の中身を定義することができます。
fun <T>createLoaderCallbacks( context: Context, backgroundTask:(()->T?)?, onLoadFinishedTask:((Loader<T>, T?)->Unit)?, onLoaderResetTask:((Loader<T>)->Unit)? ):LoaderManager.LoaderCallbacks<T> { return object : LoaderManager.LoaderCallbacks<T> { override fun onCreateLoader(id: Int, args: Bundle?): Loader<T> { return object : AsyncTaskLoader<T>(context) { override fun loadInBackground(): T? { return backgroundTask?.let { backgroundTask!!() } } override fun onStartLoading() { forceLoad() } } } override fun onLoadFinished(loader: Loader<T>, data: T?) { onLoadFinishedTask?.let { onLoadFinishedTask!!(loader, data) } } override fun onLoaderReset(loader: Loader<T>) { onLoaderResetTask?.let { onLoaderResetTask!!(loader) } } } }
呼び出し側の実装
呼び出し側はシンプルに実装できます。
private fun getWord():String?{ return "文字列" } private fun printWord(loader:Loader<String>, data:String?){ Log.d("word", data) } private fun doBackground() { //引数つきの関数オブジェクト val funcAssignTopics:(Loader<String>, String?)->Unit = ::assignTopics //無名関数の場合 //val funcAssignTopics:(Loader<String>, String?)->Unit = fun (loader:Loader<String>, data: String?){} //ラムダ式で渡す場合 //val funcAssignTopics:(Loader<String>, String?)->Unit = { loader, s -> } val callbacks = createLoaderCallbacks<String>( this, ::getWord, funcAssignTopics, // { loader, s -> } ラムダ式で記述も出来る null ) val lm = supportLoaderManager lm.initLoader(1, null, callbacks) }
このように必要な処理のみ記述でコールバックの実装の生成が可能です。
参考 https://developer.android.com/guide/components/loaders?hl=ja
リンク
リンク
