异步操作的附加抽象层
Additional layer of abstraction for asynchronous operations
我的问题将更多地与设计或架构相关。只需要更多有经验的工程师的一些建议。
我现在正在学习 RxJava/RxAndroid,并且想根据所学的经验在应用程序中替换我的异步逻辑。
我有一个基于 MVP 模式的应用程序,几乎每个 Presenter 都与 Repository 通信。为了避免 ANR,我在存储库 class 中使用 some 异步逻辑(实际上使用什么机制并不重要:ThreadPoolExecutor、AsyncTask 或 JobScheduler)。
所以我决定用一组 RxJava classes 替换异步逻辑,但我遇到了一个有趣的问题。 从架构的角度来看,我应该在哪里实现异步逻辑?
恕我直言,有两种可能的选择(可能还有其他选择):
- 重写每个存储库并将所有自定义异步逻辑替换为新的 Reactive 逻辑;
- 从存储库中删除所有异步逻辑(使 class 完全同步)并将其移动到某个外部实体(我们将其命名为 "RepositoryAsyncRunner")。
如果我遵循案例 #1,那么需要为每个存储库更改接口,它将以相同的方式工作。看起来像是不必要的更改,因为逻辑没有改变。唯一改变的是异步执行的方式。
如果我遵循案例#2,那么我的 Presenter 将与该异步包装器而不是普通存储库进行通信,并且所有异步逻辑都将封装在一个单独的 class.
坦率地说我喜欢第二种方法。如果我稍后改变主意,想用另一个很酷的库替换 RxJava,我只需要更改 AsyncRunner 而无需修改 Repository class,它的接口和相关的单元测试(甚至可能 Presenter 的测试都不会改变了)。同样使用这种方法,我将有两个不同的 classes 用于两个不同的目的('S' in SOLID :)
很高兴得到一些帮助来解决这种情况。
如果您使用 RxJava 但您的存储库不公开 Rx 原语,如 Observable
、Single
等。您将错过 Rx[=40 的一些好处=] 比如可链接性、管道中的转换、无嵌套回调等。
因此,如果您要通过使用 RxJava 获得最佳结果,您的存储库可能应该具有 return Rx 原语的方法。
但是,如果您担心必须转移到另一个异步组合框架的可能性,您始终可以使用 Java 异步任务原语,即 Callable
。这些很常见,很容易在各种框架之间转换。
示例:
public class CallableFactory {
private final RetrofitService retrofitService;
public CallableFactory(RetrofitService retrofitService) {
this.retrofitService = retrofitService;
}
public Callable<String> getCallable(final Integer id) {
return new Callable<String>() {
@Override
public String call() throws Exception {
return retrofitService.getById()
.enqueue();
}
};
}
}
那么 Rx 存储库将如下所示:
class RealRepository implements RxRepository {
private final CallableFactory callableFactory;
@Inject
RealRepository(CallableFactory callableFactory) {
this.callableFactory = callableFactory;
}
@Override
public Observable<String> retrieveById(final Integer id) {
return Observable.fromCallable(callableFactory.getCallable(id));
}
}
并且如果你必须移动到,比如说,Google Guava 并使用 ListenableFuture
而不是 Rx 你可以 re-use Callable:
class RealGuavaRepository implements GuavaRepository {
private final CallableFactory callableFactory;
@Inject
RealRepository(CallableFactory callableFactory) {
this.callableFactory = callableFactory;
}
@Override
public ListenableFuture<String> retrieveById(final Integer id) {
return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor())
.submit(callableFactory.getCallable(id));
}
}
最后一个示例中对 Executor
的硬依赖并不理想,但它是一种简化 - 您可以很容易地注入这些。
编辑:如果您谈论的是 Android 项目,官方 Google Android 架构蓝图 here
中有一个很好的示例
我的问题将更多地与设计或架构相关。只需要更多有经验的工程师的一些建议。
我现在正在学习 RxJava/RxAndroid,并且想根据所学的经验在应用程序中替换我的异步逻辑。
我有一个基于 MVP 模式的应用程序,几乎每个 Presenter 都与 Repository 通信。为了避免 ANR,我在存储库 class 中使用 some 异步逻辑(实际上使用什么机制并不重要:ThreadPoolExecutor、AsyncTask 或 JobScheduler)。
所以我决定用一组 RxJava classes 替换异步逻辑,但我遇到了一个有趣的问题。 从架构的角度来看,我应该在哪里实现异步逻辑?
恕我直言,有两种可能的选择(可能还有其他选择):
- 重写每个存储库并将所有自定义异步逻辑替换为新的 Reactive 逻辑;
- 从存储库中删除所有异步逻辑(使 class 完全同步)并将其移动到某个外部实体(我们将其命名为 "RepositoryAsyncRunner")。
如果我遵循案例 #1,那么需要为每个存储库更改接口,它将以相同的方式工作。看起来像是不必要的更改,因为逻辑没有改变。唯一改变的是异步执行的方式。
如果我遵循案例#2,那么我的 Presenter 将与该异步包装器而不是普通存储库进行通信,并且所有异步逻辑都将封装在一个单独的 class.
坦率地说我喜欢第二种方法。如果我稍后改变主意,想用另一个很酷的库替换 RxJava,我只需要更改 AsyncRunner 而无需修改 Repository class,它的接口和相关的单元测试(甚至可能 Presenter 的测试都不会改变了)。同样使用这种方法,我将有两个不同的 classes 用于两个不同的目的('S' in SOLID :)
很高兴得到一些帮助来解决这种情况。
如果您使用 RxJava 但您的存储库不公开 Rx 原语,如 Observable
、Single
等。您将错过 Rx[=40 的一些好处=] 比如可链接性、管道中的转换、无嵌套回调等。
因此,如果您要通过使用 RxJava 获得最佳结果,您的存储库可能应该具有 return Rx 原语的方法。
但是,如果您担心必须转移到另一个异步组合框架的可能性,您始终可以使用 Java 异步任务原语,即 Callable
。这些很常见,很容易在各种框架之间转换。
示例:
public class CallableFactory {
private final RetrofitService retrofitService;
public CallableFactory(RetrofitService retrofitService) {
this.retrofitService = retrofitService;
}
public Callable<String> getCallable(final Integer id) {
return new Callable<String>() {
@Override
public String call() throws Exception {
return retrofitService.getById()
.enqueue();
}
};
}
}
那么 Rx 存储库将如下所示:
class RealRepository implements RxRepository {
private final CallableFactory callableFactory;
@Inject
RealRepository(CallableFactory callableFactory) {
this.callableFactory = callableFactory;
}
@Override
public Observable<String> retrieveById(final Integer id) {
return Observable.fromCallable(callableFactory.getCallable(id));
}
}
并且如果你必须移动到,比如说,Google Guava 并使用 ListenableFuture
而不是 Rx 你可以 re-use Callable:
class RealGuavaRepository implements GuavaRepository {
private final CallableFactory callableFactory;
@Inject
RealRepository(CallableFactory callableFactory) {
this.callableFactory = callableFactory;
}
@Override
public ListenableFuture<String> retrieveById(final Integer id) {
return MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor())
.submit(callableFactory.getCallable(id));
}
}
最后一个示例中对 Executor
的硬依赖并不理想,但它是一种简化 - 您可以很容易地注入这些。
编辑:如果您谈论的是 Android 项目,官方 Google Android 架构蓝图 here
中有一个很好的示例