開発している中でHTTP通信をする必要が出てきたのですが、
最近はメインスレッドでブロックするような処理を行うとAndroidさんに怒られます。
そこで非同期処理用のクラスであるAsyncTaskLoaderを使います。
なんでAsyncTaskLoader?
非同期処理といえばThreadやAsyncTaskなんかもありますが、こちらは使わないほうが良いです。
ThreadはAndroidのライフサイクルに適応していませんし、AsyncTaskはコールバック処理が面倒です。
その点AsyncTaskLoaderはAsyncTaskの後継として作られており最初からコールバック用のインターフェースが準備されているので自前で作る必要がありません。
ということで今から開発というのであればAsyncTaskLoader一択ですね。
AsyncTaskLoaderで例外処理
AsyncTaskLoaderとのインターフェースを考えてて、ふと思いました。
そういえば例外処理ってどうやればいいんだっけ?
とりあえずググってみる・・・
検索のトップに来たのがこれ
http://dev.classmethod.jp/smartphone/android/async-errorhandling/
うーん、AsyncTaskLoaderその外側からは例外をキャッチできないので
処理結果のクラスに成功した場合のクラスと失敗した場合の例外クラスを持っておいて、成否に応じて内容を変えてるみたいですね。
確かにこれが真っ先に思いつきますし、私も前にそんな風にやってたような気がします。
だけどこれはクラス定義しなきゃいけませんし、結果の分岐処理が煩雑になる予感がしますね。
ScalaでAsyncTaskLoaderを考えてみる
Scalaには簡単に非同期処理を行うためのFutureというものが存在します。
val f: Future[String] = Future {
// 時間がかかる処理
}
f.onComplete {
case Success(e) => println("成功した: " + e)
case Failure(t) => println("エラーが発生した: " + t.getMessage)
}
Futureは処理の結果としてSuccessまたはFailureを持っているので、これを使用してパターンマッチで処理ができます。
なんとかこれを使って結果の分岐処理を行いたかったので、無理やりAsyncTaskLoaderに突っ込んでみました。
Future + AsyncTaskLoader
本来はFutureの内容はHTTP通信なのですが今回は説明用に文字列を返すだけにしてあります。
loadInBackground内でFutureの処理を終えないといけないのでAwait.readyを使用してFutureの処理が終わるまで処理をブロックしています。
class SampleTask(context: Context) extends AsyncTaskLoader[Future[String]](context) {
override def loadInBackground(): Future[String] = {
val f = Future {
"success"
}
Await.ready(f, Duration.Inf)
f
}
}
結果を受け取る側のUIクラスはこんな感じです。
class SampleFragment extends Fragment with LoaderCallbacks[Future[String]] {
...
override def onLoadFinished(loader: Loader[Future[String]], data: Future[String]): Unit = {
data.value.get match {
case Success(e) => println("e = " + e)
case Failure(t) => println("t = " + t)
}
}
...
}
この実装でFuture内でなんらかの例外処理が発生した場合は結果がFailureとなるのでUI側で簡単に処理を分岐することができます。
しかしこの例外発生をまったく考慮していないAsyncTaskLoaderのインターフェースはどうにかならないのかな・・・