我正在为我的应用开发网络。所以我决定试试Square的Retrofit。我看到它们支持简单的回调

@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而不是简单的回调?

更新

使用改造比上面的例子简单得多,就像@Niels在他的回答中或Jake Wharton的例子项目U2020中所展示的那样。但本质上的问题是不变的——什么时候应该使用一种方式或另一种方式?


当前回答

在getUserPhoto()的情况下,RxJava的优势不是很大。 但让我们再举一个例子,当你为一个用户获取所有照片时,但只有当图像是PNG时,你不能访问JSON来在服务器端进行过滤。

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) {
            // on main thread; callback for each photo, add them to a list or something.
            list.add(photo)
        }
    }, 
    new Action1<Throwable>() {
    @Override
        public void call(Throwable throwable) {
            // on main thread; something went wrong
            System.out.println("Error! " + throwable);
        }
    }, 
    new Action0() {
        @Override
        public void call() {
            // on main thread; all photo's loaded, time to show the list or something.
        }
    });

现在JSON返回Photo的列表。我们将把它们平面映射到单独的项目。通过这样做,我们将能够使用过滤器方法来忽略非PNG的照片。之后,我们将订阅,并为每张照片获取一个回调,一个errorHandler,以及当所有行都完成时的回调。

TLDR 这里的点是;回调只返回成功和失败的回调;RxJava Observable允许你做映射,减少,过滤和更多的事情。

其他回答

使用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并调用photommodel . setuserid (EditText.getText());

在Fragment或activity的onCreate方法中,你订阅了返回photomode . 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.
                  });

此外,如果photommodel是一个单例,例如,您不需要担心方向更改,因为不管您何时订阅,BehaviorSubject都会发出最后一个服务器响应。

通过这几行代码,我们实现了即时搜索和处理方向更改。 你认为你可以用更少的代码实现回调吗?我对此表示怀疑。

我们通常遵循以下逻辑:

如果它是一个简单的单响应调用,那么Callback或Future更好。 如果一个调用有多个响应(流),或者不同调用之间有复杂的交互(参见@Niels的回答),那么Observables更好。

当你为乐趣、宠物项目、POC或第一个原型创建应用时,你会使用简单的android/java核心类,如回调、异步任务、循环器、线程等。它们使用简单,不需要任何第三方lib集成。当类似的事情可以立即完成时,仅仅为了构建一个小型的不可更改的项目而进行大型库集成是不合逻辑的。

然而,这些就像一把非常锋利的刀。在生产环境中使用这些工具总是很酷,但也会产生一些后果。如果不熟悉Clean编码和SOLID原则,就很难编写安全的并发代码。您必须维护一个适当的体系结构,以促进未来的变更和提高团队生产力。

另一方面,像RxJava、co -routine等并发库已经被尝试和测试了超过十亿次,以帮助编写可用于生产的并发代码。现在再次强调,并不是说使用这些库就不能编写并发代码或抽象出所有并发逻辑。你现在还是。但是现在,它是可见的,并且在整个代码库中,更重要的是在整个开发团队中,执行了一个清晰的并发代码编写模式。

这是使用并发框架而不是处理原始并发的普通旧核心类的主要好处。不过,别误会我的意思。我非常相信限制外部库的依赖关系,但在这种特定情况下,你必须为你的代码库构建一个自定义框架,这是一项耗时的任务,只有在有了丰富的经验之后才能完成。因此,与使用回调等普通类相比,并发框架更受欢迎。


TL 'DR

如果你已经在整个代码库中使用RxJava进行并发编码,只需使用RxJava Observable/Flowable即可。一个明智的问题是我应该使用可观察对象还是可流动对象。 如果不是,继续使用可调用对象。

通过其他答案中的样本和结论,我认为对于简单的一两步任务来说,没有太大的区别。然而,Callback是简单而直接的。RxJava比较复杂,对于简单的任务来说太大了。还有第三种解决办法:算盘常用。让我用这三种解决方案实现上面的用例:Callback, RxJava, CompletableFuture(abacsus -common)和Retrolambda:

从网络获取照片并保存/显示在设备上:

// 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
});

我个人更喜欢使用Rx来获得api响应的情况下,我必须做过滤,映射或类似的数据或在情况下,我必须做另一个api调用基于之前的调用响应