Go to list of users who liked
Share on X(Twitter)
Share on Facebook
More than 5 years have passed since last update.
ほとんどのAndroidアプリで書くことになる非同期処理。
非同期処理を順番に実行したい場合や並列に実行させたい事も当然あるわけだけど、これが普通に書くと結構読みにくい。
Androidアプリを作ってる人は同じような悩みを持ってるだろうと色々探してたらいい感じのやつ見つけた。
- jdeferred 【GitHub】
似たようなやつだとこういうのもあるみたいだけど個人的にはjdeferredが好き
- android-promise【GitHub】
こういう感じだといいなーって思ってた要素
- 非同期処理をブロック化してコード上に書きたい
- 例) 非同期処理→3つの非同期を待ち合わせ→2つの非同期処理を待ち合わせ→非同期処理
- こんな複雑な非同期処理しないと思うけど、こういう単位で書けるようにしたい
- 一つ目の非同期が終わった後にその結果を使って二つ目の非同期を実行する処理を簡潔に書ける
- 複数の非同期処理を待ち合わせ処理を簡潔に書ける
- Androidに依存してないもの
- Futureなどからキャンセル処理を行える(非同期処理の制御が行える)
特徴
- JavaでもAndroidでも使える
- 導入が簡単
- 非同期処理をメソッドチェーンで書ける
- 複数の非同期処理を一つにまとめられるので、"この処理はどの非同期処理を組み合わせて最終的にどんな事をしたいのか"といった流れが読みやすくなる
導入
ここではMaven向けで書いてるけどGradleでも使えるよ!
Java・Android共通
<dependency><groupId>org.jdeferred</groupId><artifactId>jdeferred-core</artifactId><version>1.2.1</version></dependency>Androidの場合は追加でどちらかをプロジェクトに合わせて入れる
apklib
<dependency><groupId>org.jdeferred</groupId><artifactId>jdeferred-android</artifactId><version>1.2.1</version><type>apklib</type></dependency>aar
<dependency><groupId>org.jdeferred</groupId><artifactId>jdeferred-android-aar</artifactId><version>1.2.1</version><type>aar</type></dependency>使い方
// Androidプロジェクトで使う場合はAndroidDeferredManagerを使う// DefaultDeferredManagerを使うとDoneCallback内がUIスレッドで呼ばれないので落ちるよnewAndroidDeferredManager().when(newDeferredAsyncTask<Void,Progress,Result>(){@OverrideprotectedResultdoInBackgroundSafe(finalVoid...params)throwsException{return// 処理する内容}}).done(newDoneCallback<Result>(){@OverridepublicvoidonDone(finalResultresult){// UIスレッドで実行されるSystem.out.println(result);}}).fail(newFailCallback<Throwable>(){@OverridepublicvoidonFail(finalThrowabletr){// 例外が投げられると実行されるSystem.out.println(tr.getMessage());}}).always(newAlwaysCallback<Result,Throwable>(){@OverridepublicvoidonAlways(finalPromise.Statestate,finalResultresolved,finalThrowablerejected){// 成功・失敗何かしら返ってきた時に実行される}});このライブラリが便利なとこは成功した時の処理と失敗した時の処理を分けて書けるとこ。
AsyncTaskを使った処理だと分けて書くの少しめんどいのでこのライブラリ使う方が綺麗に書ける。
因みにFail時の引数の型はThrowable固定というわけではなく、別のDeferredクラスを使えば指定できたりする。
非同期処理を繋げたい場合
- 一つめの非同期を実行
- その結果を使って二つ目の非同期を実行
- さらに三つめの非同期を実行
whenメソッドに対してthenでメソッドを繋げていくと処理が連鎖していく
ExecutorServiceexecutorService=Executors.newSingleThreadExecutor();newAndroidDeferredManager(executorService).when(newDeferredAsyncTask<Void,Progress,Result>(){@OverrideprotectedResultdoInBackgroundSafe(finalVoid...params)throwsException{return// 一つ目で処理する内容}}).then(newDonePipe<Result,Result2,Failed,Progress>(){@OverridepublicPromise<Result2,Throwable,Progress>pipeDone(finalResultresult){return// 一つ目の結果を用いて二つ目の処理を実行}}).then(newDonePipe<Result2,Result3,Failed,Progress>(){@OverridepublicPromise<Result3,Throwable,Progress>pipeDone(finalResult2result2){return// 一つ目の結果を用いて三つ目の処理を実行}}).done(newDoneCallback<Result3>(){@OverridepublicvoidonDone(finalResult3result3){System.out.println(result3);// 三つ目の処理結果を出力する}});別メソッドに切りださなくてもAsyncTaskのようにネストがどんどん深くならないのが良い。
複数の非同期処理を待ち合わせさせたい場合
こんな感じでメンバ変数に完了した値を覚えさせる処理は必要なし
導入前
publicMyActivityextendsActivity{// これいらないprivateintfinished=0;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){for(inti=0;i<10;i++){newAsyncTask<Void,Progress,Result>(){@OverrideprotectedResultdoInBackground(finalVoid...params){return// 処理}@OverrideprotectedvoidonPostExecute(finalResultresult){finished++;// 全部終わった時if(finished==10){// 処理}}}.execute();}}}こんな感じに書ける
DeferredAsyncTask<Void,Progress,Result>[]tasks=newDeferredAsyncTask[10];for(inti=0;i<10;i++){tasks[i]=newDeferredAsyncTask<Void,Progress,Result>(){@OverrideprotectedResultdoInBackgroundSafe(finalVoid...params)throwsException{return// 処理}};}newAndroidDeferredManager().when(tasks).done(newDoneCallback<MultipleResults>(){@OverridepublicvoidonDone(finalMultipleResultsresults){System.out.println(results.get(0));}}).fail(newFailCallback<OneReject>(){@OverridepublicvoidonFail(finalOneRejectreject){// 何番目の失敗か, 失敗した時に渡されたオブジェクト(Throwableなど)System.out.println(reject.getIndex()+", "+getReject());}});配列で渡せば後は全部終わった時どうするかと失敗した時どうするかを書くだけ!
今回紹介した機能中では個人的に一番嬉しかった機能
もちろんwhenに配列で渡した時もPromiseで返ってくるので、doneの代わりにthenで繋げて別の非同期処理に繋げることも可能!
newAndroidDeferredManager().when(tasks).then(newDonePipe<MultipleResults,Result,Failed,Progress>(){@OverridepublicPromise<Result,Failed,Progress>pipeDone(finalMultipleResultsresult){return// 別の非同期処理}}).done(...).fail(...);Futureを用いたDeferredの使い方
Androidに依存しない処理でも非同期処理を書きたい場合もあるけど、そういう時はFutureを使って記述する事ができる。
Futureに限らずCallableなど他にも使えるけど、ここでFutureを使うのはActivityの終了に合わせて非同期処理をキャンセルさせたいため。
DeferredFutureTask<Result,Progress>future=newDeferredFutureTask<Result,Progress>(newCallable<Result>(){@OverridepublicResultcall()throwsException{return// 処理}}){@Overridepublicbooleancancel(finalbooleanmayInterruptIfRunning){// 処理のキャンセル(httpリクエストならdisconnectなど)returnsuper.cancel(mayInterruptIfRunning);}};AsyncTask.SERIAL_EXECUTOR.execute(future);newAndroidDeferredManager().when(future).done(...).fail(...);// Activity#onDestroyなど呼ばれた時の処理future.cancel(true);Futureを使うことでホームボタンで戻っても裏で通信が走り続けないようにできる(他のやり方でもできるのかな?)
ラムダ式を使う
Retrolambdaを使うともっと簡潔に書くことができる。
実際に使うのであれば、Retrolambdaを用いて書いた方が可読性高く表現できるようになる。
ExecutorServiceexecutorService=Executors.newSingleThreadExecutor();newAndroidDeferredManager(executorService).when(()->return/* 一つ目で処理する内容 */).then(result->return/* 一つ目の結果を用いて二つ目の処理を実行 */).then(result2->return/* 二つ目の結果を用いて三つ目の処理を実行 */).done(result3->System.out.println(result3));/*三つ目の処理結果を出力する */感想
今回の記事はAndroid向けだったけど、Javaプロジェクトでも使えるので積極的に使っていきたい。
注意点として、AndroidDeferredObjectの生成場所をバックグラウンドでやるとDoneCallbackの中身がAndroidDeferredObjectを生成したスレッドで実行されてしまって落ちる可能性があるので、対応されるまで気をつけた方が良いと思う。
因みに紹介した通りに使っている分にはまずこの問題は起きないので大丈夫。
この問題はこのissueで解決されるはず。
https://github.com/jdeferred/jdeferred/pull/32
非同期処理の簡潔化の選択肢は他だとAndroidAnnotationsを使った方法もあるので、良いと思う方を試してみるといいかも。
このライブラリはjQueryのDeferredとPromiseを参考にしたライブラリなので、これ使って設計する時はそこらへん把握してから使う方が分かりやすい。
DeferredとPromise使って何が嬉しいのかについてはここらへんが読みやすいと思う。
http://qiita.com/yuku_t/items/1b8ce6bba133a7eaeb23
追記(2016-04-06)
現在はRxJavaに移行しました。個人的にはRxJavaの方が好みかな。
たまにストック数が増えるので、現在の好みを追記しておこうかなと。
とは言えjdefferedが非推奨というわけでもなく、RxJavaよりとっつきやすいとは思うので、
チーム方針だったりそこらへん相談しながら入れるといいんじゃないかなーと思います。
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme