268

アプリのネットワーキングに取り組んでいます。そこで、Square のRetrofitを試してみることにしました。彼らはシンプルをサポートしていることがわかります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単純なコールバックよりも優先する/する必要があるのはいつですか?

アップデート

@Niels が彼の回答または Jake Wharton のサンプル プロジェクトU2020で示したように、レトロフィットの使用は上記の例よりもはるかに簡単です。しかし、基本的に質問は同じままです - いつどちらの方法を使用する必要がありますか?

4

9 に答える 9

358

単純なネットワーキングの場合、Callback に対する 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 バリアントはまだ小さくはありませんが、Lambda を使用すると、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 はわずかな調整しか必要としませんが、Callback バリアントは複数の場所で調整する必要があります (コールバックごとに、すべてのデータが取得されたかどうかを確認する必要があります)。

もう一つの例; Retrofit を使用してデータをロードするオートコンプリート フィールドを作成します。EditText に TextChangedEvent があるたびに Web コールを実行する必要はありません。すばやく入力する場合は、最後の要素のみが呼び出しをトリガーする必要があります。RxJava では、デバウンス演算子を使用できます。

inputObservable.debounce(1, TimeUnit.SECONDS).subscribe(new Action1<String>() {
            @Override
            public void call(String s) {
                // use Retrofit to create autocompletedata
            }
        });

Callback バリアントは作成しませんが、これははるかに手間がかかることを理解していただけるでしょう。

結論: データがストリームとして送信される場合、RxJava は非常に優れています。Retrofit Observable は、ストリーム上のすべての要素を同時にプッシュします。Callback と比較して、これ自体は特に有用ではありません。しかし、複数の要素がストリームに異なるタイミングでプッシュされ、タイミング関連の作業を行う必要がある場合、RxJava はコードをより保守しやすくします。

于 2015-04-28T11:34:19.233 に答える
68

Observable は Retrofit で既に行われているため、コードは次のようになります。

api.getUserPhoto(photoId)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<Photo>() {
         @Override
            public void call(Photo photo) {
                //save photo?
            }
     });
于 2014-03-04T11:35:51.690 に答える
28

rxjava を使用すると、より少ないコードでより多くのことを実行できます。

アプリにインスタント検索を実装するとします。コールバックを使用すると、以前のリクエストのサブスクライブを解除して新しいリクエストをサブスクライブし、向きの変更を自分で処理することを心配していました...コードが多く、冗長すぎると思います。

rxjava の使用は非常に簡単です。

public class PhotoModel{
  BehaviorSubject<Observable<Photo>> subject = BehaviorSubject.create(...);

  public void setUserId(String id){
   subject.onNext(Api.getUserPhoto(photoId));
  }

  public Observable<Photo> subscribeToPhoto(){
    return Observable.switchOnNext(subject);
  }
}

インスタント検索を実装したい場合は、TextChangeListener をリッスンして呼び出すだけです。photoModel.setUserId(EditText.getText());

Fragment または activity の onCreate メソッドで、photoModel.subscribeToPhoto() を返す Observable をサブスクライブすると、最新の Observable(request) によって発行されたアイテムを常に発行する Observable が返されます。

AndroidObservable.bindFragment(this, photoModel.subscribeToPhoto())
                 .subscribe(new Action1<Photo>(Photo photo){
      //Here you always receive the response of the latest query to the server.
                  });

また、たとえば PhotoModel がシングルトンの場合、方向の変更について心配する必要はありません。サブスクライブする時期に関係なく、BehaviorSubject が最後のサーバー レスポンスを発行するためです。

このコード行で、インスタント検索を実装し、向きの変更を処理します。より少ないコードでコールバックを使用してこれを実装できると思いますか? 疑わしい。

于 2014-06-12T13:33:15.423 に答える
1

他の回答のサンプルと結論では、単純な 1 つまたは 2 つのステップのタスクには大きな違いはないと思います。ただし、Callback は単純明快です。RxJava はより複雑で、単純なタスクには大きすぎます。AbacusUtilによる 3 番目のソリューションがあります。Retrolambda を使用した Callback、RxJava、CompletableFuture(AbacusUtil) の 3 つのソリューションすべてで上記のユース ケースを実装させてください

ネットワークから写真を取得し、デバイスに保存/表示:

// By Callback
api.getUserPhoto(userId, new Callback<Photo>() {
    @Override
    public void onResponse(Call<Photo> call, Response<Photo> response) {
        save(response.body()); // or update view on UI thread.
    }

    @Override
    public void onFailure(Call<Photo> call, Throwable t) {
        // show error message on UI or do something else.
    }
});

// By RxJava
api.getUserPhoto2(userId) //
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(photo -> {
            save(photo); // or update view on UI thread.
        }, error -> {
            // show error message on UI or do something else.
        });

// By Thread pool executor and CompletableFuture.
TPExecutor.execute(() -> api.getUserPhoto(userId))
        .thenRunOnUI((photo, error) -> {
            if (error != null) {
                // show error message on UI or do something else.
            } else {
                save(photo); // or update view on UI thread.
            }
        });

ユーザーの詳細と写真を並行して読み込む

// By Callback
// ignored because it's little complicated

// By RxJava
Observable.zip(api.getUserDetails2(userId), api.getUserPhoto2(userId), (details, photo) -> Pair.of(details, photo))
        .subscribe(p -> {
            // Do your task.
        });

// By Thread pool executor and CompletableFuture.
TPExecutor.execute(() -> api.getUserDetails(userId))
          .runOnUIAfterBoth(TPExecutor.execute(() -> api.getUserPhoto(userId)), p -> {
    // Do your task
});
于 2017-06-03T18:31:59.550 に答える