[解決済み】AndroidでRxJava ObservableとシンプルなCallbackを使うべきタイミングは?
質問
私のアプリのネットワーク構築に取り組んでいます。そこで、Squareの
レトロフィット
. シンプルな
Callback
@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);
とRxJavaの
Observable
@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);
どちらも一見するとよく似ていますが、実装になると面白いことに......。
単純なコールバックの実装では、このような感じになります。
api.getUserPhoto(photoId, new Callback<Photo>() {
@Override
public void onSuccess() {
}
});
という、非常にシンプルでわかりやすいものです。そして
Observable
は、すぐに冗長になり、かなり複雑になります。
public Observable<Photo> getUserPhoto(final int photoId) {
return Observable.create(new Observable.OnSubscribeFunc<Photo>() {
@Override
public Subscription onSubscribe(Observer<? super Photo> observer) {
try {
observer.onNext(api.getUserPhoto(photoId));
observer.onCompleted();
} catch (Exception e) {
observer.onError(e);
}
return Subscriptions.empty();
}
}).subscribeOn(Schedulers.threadPoolForIO());
}
そして、それだけではありません。まだこんなことをしなければならないのです。
Observable.from(photoIdArray)
.mapMany(new Func1<String, Observable<Photo>>() {
@Override
public Observable<Photo> call(Integer s) {
return getUserPhoto(s);
}
})
.subscribeOn(Schedulers.threadPoolForIO())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Photo>() {
@Override
public void call(Photo photo) {
//save photo?
}
});
何か見落としているのでしょうか?それとも、このケースは
Observable
s?
どのような場合に
Observable
を単純なCallbackに置き換えるか?
更新情報
retrofitの使用方法は、@Nielsの回答やJake Whartonのサンプルプロジェクトにあるように、上記の例よりもずっとシンプルです。 U2020 . しかし、本質的な疑問は変わりません - いつ、どちらかの方法を使うべきなのでしょうか?
どのように解決するのか?
単純なネットワーキングの場合、コールバックに対するRxJavaの利点は非常に限られています。単純なgetUserPhotoの例です。
RxJavaです。
api.getUserPhoto(photoId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Photo>() {
@Override
public void call(Photo photo) {
// do some stuff with your photo
}
});
コールバック
api.getUserPhoto(photoId, new Callback<Photo>() {
@Override
public void onSuccess(Photo photo, Response response) {
}
});
RxJavaのバリアントはCallbackのバリアントよりあまり良くはありません。とりあえず、エラー処理については無視しましょう。 写真のリストを取ってみよう。
RxJavaです。
api.getUserPhotos(userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<List<Photo>, Observable<Photo>>() {
@Override
public Observable<Photo> call(List<Photo> photos) {
return Observable.from(photos);
}
})
.filter(new Func1<Photo, Boolean>() {
@Override
public Boolean call(Photo photo) {
return photo.isPNG();
}
})
.subscribe(
new Action1<Photo>() {
@Override
public void call(Photo photo) {
list.add(photo)
}
});
コールバック
api.getUserPhotos(userId, new Callback<List<Photo>>() {
@Override
public void onSuccess(List<Photo> photos, Response response) {
List<Photo> filteredPhotos = new ArrayList<Photo>();
for(Photo photo: photos) {
if(photo.isPNG()) {
filteredList.add(photo);
}
}
}
});
さて、RxJavaのバリエーションはまだ小さくはありませんが、Lambdasを使えばCallbackのバリエーションに近くなります。 さらに、JSONフィードにアクセスできるのであれば、PNGだけを表示しているときにすべての写真を取得するのはちょっと変です。PNGだけを表示するようにフィードを調整すればよいのです。
最初の結論
正しいフォーマットで用意した簡単なJSONを読み込むだけでは、コードベースは小さくならない。
さて、もう少し面白いことをやってみましょう。userPhotoを取得したいだけでなく、Instagramクローンを持っていて、2つのJSONを取得したいとします。 1. getUserDetails() 2. getUserPhotos()
この2つのJSONを並行して読み込み、両方が読み込まれたらページを表示させるようにします。 コールバックの変形は少し難しくなります。2つのコールバックを作成し、データをアクティビティに保存し、すべてのデータが読み込まれたら、ページを表示する必要があります。
コールバック
api.getUserDetails(userId, new Callback<UserDetails>() {
@Override
public void onSuccess(UserDetails details, Response response) {
this.details = details;
if(this.photos != null) {
displayPage();
}
}
});
api.getUserPhotos(userId, new Callback<List<Photo>>() {
@Override
public void onSuccess(List<Photo> photos, Response response) {
this.photos = photos;
if(this.details != null) {
displayPage();
}
}
});
RxJavaです。
private class Combined {
UserDetails details;
List<Photo> photos;
}
Observable.zip(api.getUserDetails(userId), api.getUserPhotos(userId), new Func2<UserDetails, List<Photo>, Combined>() {
@Override
public Combined call(UserDetails details, List<Photo> photos) {
Combined r = new Combined();
r.details = details;
r.photos = photos;
return r;
}
}).subscribe(new Action1<Combined>() {
@Override
public void call(Combined combined) {
}
});
どこかで見たことがあるような気がします。RxJavaのコードは、コールバック・オプションと同じ大きさになりました。RxJavaのコードはより堅牢になりました。 もし、3つ目のJSONを読み込む必要があったらどうなるか考えてみてください(最新の動画のような)。RxJavaはわずかな調整で済みますが、コールバックバリアントは複数の場所で調整する必要があります(各コールバックで、すべてのデータが取得されたかどうかをチェックする必要があります)。
別の例として、Retrofitを使用してデータを読み込むオートコンプリートフィールドを作成したいと思います。 EditText に TextChangedEvent が発生するたびに webcall を実行するのは面倒です。速く入力するときは、最後の要素だけが呼び出しのトリガーになればよいのです。 RxJavaでは、debounce演算子を使うことができます。
inputObservable.debounce(1, TimeUnit.SECONDS).subscribe(new Action1<String>() {
@Override
public void call(String s) {
// use Retrofit to create autocompletedata
}
});
コールバックのバリアントは作りませんが、これはもっと大変な作業であることはご理解いただけると思います。
結論 RxJavaは、データがストリームとして送信される場合に非常に優れています。Retrofit Observableは、ストリーム上のすべての要素を同時にプッシュします。 これ自体はCallbackと比較して特に便利というわけではありません。しかし、ストリームにプッシュされる要素が複数あり、時間もまちまちで、タイミングに関することをする必要があるとき、RxJavaを使うとコードのメンテナンス性がぐっと上がります。
関連
-
[解決済み】Android Studioでパラメータ化されたユニットテストを実行すると、指定されたインクルードに対するテストが見つからないエラーが発生する
-
[解決済み】Android Studioの初回起動。Android SDKアドオンリストにアクセスできない
-
[解決済み】Android Studio。Android.support.design.widget.FloatingActionButton クラスを膨らませるのにエラーが発生する。
-
[解決済み] サポートされていないメソッドです。BaseConfig.getApplicationIdSuffix()がサポートされていません。
-
[解決済み] Android SDK の場所には空白を含めないでください。NDK ツールで問題が発生するためです。
-
[解決済み] 複数のデバイスを接続しているときにADB Shellを使用するには?error: more than one device and emulator "で失敗します。
-
[解決済み] setBackgroundDrawable() 非推奨
-
[解決済み] Android Log.v(), Log.d(), Log.i(), Log.w(), Log.e() - それぞれをいつ使うか?
-
[解決済み】AndroidでRxJavaを使用するタイミングとAndroid Architectural ComponentsのLiveDataを使用するタイミングは?
-
[解決済み] RxJavaでmapとflatMapはいつ使うの?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】パッケージ名(Google Analytics)に一致するクライアントが見つからない - 複数のproductFlavorsとbuildTypes
-
[解決済み】Android Intent コンストラクタを解決できない
-
[解決済み】AndroidのSSL接続でトラストアンカーが見つからない
-
[解決済み】com.android.ide.common.process.ProcessException: aaptの実行に失敗しました! どうすればいいですか?
-
[解決済み] カスタムアダプタからnotifyDataSetChangeが機能しない
-
[解決済み】Android Studio。Android.support.design.widget.FloatingActionButton クラスを膨らませるのにエラーが発生する。
-
[解決済み】IllegalStateException: ViewPager で onSaveInstanceState の後にこのアクションを実行できません。
-
[解決済み] サポートされていないメソッドです。BaseConfig.getApplicationIdSuffix()がサポートされていません。
-
[解決済み] android.support.design.widget.FloatingActionButton クラスの展開に失敗しました。
-
[解決済み] android.intent.action.MAINの意味は何ですか?