原有代码如何Agera化

1年前 阅读 198 评论 0 赞 0

Incrementally Agerifying legacy code

Agera引入的代码风格也许适合从零开始的新建app项目。这篇包括一些提示,来帮助想在遗留代码中使用Agera的开发者,如此往下做。

Upgrading legacy observer pattern

--升级原有观察者模式

观察者模式有很多种实现方式,但不是所有的都可以通过简单的放入迁入到Agera中。下面是一个例子:演示将一个监听(listenable)类添加到Observable接口的一种方法。

MyListenable类可以增加(addListener)和删除(removeListener)Listener,作为额外完整的演示,它继承了SomeBaseClass

该实例使用UpdateDispatcher来解决Java的单继承约束,使用一个内部类(Bridge)来做桥接, 保持其完整的原始API,同时也使Agera可见。

  1. public final class MyListenable extends SomeBaseClass implements Observable {
  2. private final UpdateDispatcher updateDispatcher;
  3. public MyListenable() {
  4. // Original constructor code here...
  5. updateDispatcher = Observables.updateDispatcher(new Bridge());
  6. }
  7. // Original class body here... including:
  8. public void addListener(Listener listener) { }
  9. public void removeListener(Listener listener) { }
  10. @Override
  11. public void addUpdatable(Updatable updatable) {
  12. updateDispatcher.addUpdatable(updatable);
  13. }
  14. @Override
  15. public void removeUpdatable(Updatable updatable) {
  16. updateDispatcher.removeUpdatable(updatable);
  17. }
  18. private final class Bridge implements ActivationHandler, Listener {
  19. @Override
  20. public void observableActivated(UpdateDispatcher caller) {
  21. addListener(this);
  22. }
  23. @Override
  24. public void observableDeactivated(UpdateDispatcher caller) {
  25. removeListener(this);
  26. }
  27. @Override
  28. public void onEvent() { // Listener implementation
  29. updateDispatcher.update();
  30. }
  31. }
  32. }

Exposing synchronous operations as repositories

--揭秘repository的同步操作

Java本质是一种同步语言,如:在Java中最低级别的操作都是同步方法。 当操作可能会花一些时间才能返回(耗时操作),这种方法通常称为阻塞方法,而且禁止开发者在主线程(UI Thread)调用。

假设app的UI需要从阻塞的方法获得数据。Agera可以很容易的通过后台线程完成调用,然后UI可以在主线程中接收数据。首先,这个阻塞操作需要封装成一个Agera操作,像这样:

  1. public class NetworkCallingSupplier implements Supplier<Result<ResponseBlob>> {
  2. private final RequestBlob request = …;
  3. @Override
  4. public Result<ResponseBlob> get() {
  5. try {
  6. ResponseBlob blob = networkStack.execute(request); // blocking call
  7. return Result.success(blob);
  8. } catch (Throwable e) {
  9. return Result.failure(e);
  10. }
  11. }
  12. }
  13. Supplier<Result<ResponseBlob>> networkCall = new NetworkCallingSupplier();
  14. Repository<Result<ResponseBlob>> responseRepository =
  15. Repositories.repositoryWithInitialValue(Result.<ResponseBlob>absent())
  16. .observe() // no event source; works on activation
  17. .onUpdatesPerLoop() // but this line is still needed to compile
  18. .goTo(networkingExecutor)
  19. .thenGetFrom(networkCall)
  20. .compile();

上面的代码段假定了,在Repository.compile()之前这个请求是已知且永远不变的。

这个很容易升级成为一个动态请求,甚至在repository同样的激活周期期间。

要可以修改请求,简单的方式是使用MutableRepository。 此外,为了在第一次请求为完成之前就可以提供数据,可以在Result中一个提供初始值:absent()

MutableRepository这种用法类似于是一个可变的变量(可为null),故命名为requestVariable

  1. // MutableRepository<RequestBlob> requestVariable =
  2. // mutableRepository(firstRequest);
  3. // OR:
  4. MutableRepository<Result<RequestBlob>> requestVariable =
  5. mutableRepository(Result.<RequestBlob>absent());

然后, 不是在supplier中封装阻塞方法,使用function实现动态请求:

  1. public class NetworkCallingFunction
  2. implements Function<RequestBlob, Result<ResponseBlob>> {
  3. @Override
  4. public Result<ResponseBlob> apply(RequestBlob request) {
  5. try {
  6. ResponseBlob blob = networkStack.execute(request);
  7. return Result.success(blob);
  8. } catch (Throwable e) {
  9. return Result.failure(e);
  10. }
  11. }
  12. }
  13. Function<RequestBlob, Result<ResponseBlob>> networkCallingFunction =
  14. new NetworkCallingFunction();

升级后的repository可以像这样compiled:

  1. Result<ResponseBlob> noResponse = Result.absent();
  2. Function<Throwable, Result<ResponseBlob>> withNoResponse =
  3. Functions.staticFunction(noResponse);
  4. Repository<Result<ResponseBlob>> responseRepository =
  5. Repositories.repositoryWithInitialValue(noResponse)
  6. .observe(requestVariable)
  7. .onUpdatesPerLoop()
  8. // .getFrom(requestVariable) if it does not supply Result, OR:
  9. .attemptGetFrom(requestVariable).orEnd(withNoResponse)
  10. .goTo(networkingExecutor)
  11. .thenTransform(networkCallingFunction)
  12. .compile();

这部分代码段还演示了一点:通过给操作一个特殊的名字,让repository的编译表达式更易读。

Wrapping asynchronous calls in repositories

--repository的异步调用封装

现在的很多Library都有异步API和内置的线程,但是客户端不能控制或禁用。

app中有这样的Library的话,引入Agera将是一个具有挑战性的工作。 一个直接的办法就是找到Library中同步选择的点,采用[[如上段所述|Incrementally-Agerifying-legacy-code#exposing-synchronous-operations-as-repositories]]方法。

另一个方式(反模式):切换后台线程执行异步调用并等待结果,然后同步拿结果。上面方式不可行时,这一节讨论一个合适的解决方法。

异步调用的一个循环模式是请求-响应 结构。下面的示例假定这样结构:未完成的工作可以被取消,但是不指定回调的线程。

  1. interface AsyncOperator<P, R> {
  2. Cancellable request(P param, Callback<R> callback);
  3. }
  4. interface Callback<R> {
  5. void onResponse(R response); // Can be called from any thread
  6. }
  7. interface Cancellable {
  8. void cancel();
  9. }

下面repository例子,使用AsyncOperator提供数据, 完成响应式请求(一个抽象的supplier类)。

这段代码假定AsyncOperator已经有足够的缓存,因此重复的请求不会影响性能。

  1. public class AsyncOperatorRepository<P, R> extends BaseObservable
  2. implements Repository<Result<R>>, Callback<R> {
  3. private final AsyncOperator<P, R> asyncOperator;
  4. private final Supplier<P> paramSupplier;
  5. private Result<R> result;
  6. private Cancellable cancellable;
  7. public AsyncOperatorRepository(AsyncOperator<P, R> asyncOperator,
  8. Supplier<P> paramSupplier) {
  9. this.asyncOperator = asyncOperator;
  10. this.paramSupplier = paramSupplier;
  11. this.result = Result.absent();
  12. }
  13. @Override
  14. protected synchronized void observableActivated() {
  15. cancellable = asyncOperator.request(paramSupplier.get(), this);
  16. }
  17. @Override
  18. protected synchronized void observableDeactivated() {
  19. if (cancellable != null) {
  20. cancellable.cancel();
  21. cancellable = null;
  22. }
  23. }
  24. @Override
  25. public synchronized void onResponse(R response) {
  26. cancellable = null;
  27. result = Result.absentIfNull(response);
  28. dispatchUpdate();
  29. }
  30. @Override
  31. public synchronized Result<R> get() {
  32. return result;
  33. }
  34. }

这个类可以很容易地升级到可以修改请求参数,而这个过程就类似于前面的讨论:让repository提供请求参数,并让AsyncOperatorRepository观察请求参数变化。

在激活期间,观察请求参数的变化,取消任何正在进行的请求,并发出新的请求,如下所示:

  1. public class AsyncOperatorRepository<P, R> extends BaseObservable
  2. implements Repository<Result<R>>, Callback<R>, Updatable {
  3. private final AsyncOperator<P, R> asyncOperator;
  4. private final Repository<P> paramRepository;
  5. private Result<R> result;
  6. private Cancellable cancellable;
  7. public AsyncOperatorRepository(AsyncOperator<P, R> asyncOperator,
  8. Repository<P> paramRepository) {
  9. this.asyncOperator = asyncOperator;
  10. this.paramRepository = paramRepository;
  11. this.result = Result.absent();
  12. }
  13. @Override
  14. protected void observableActivated() {
  15. paramRepository.addUpdatable(this);
  16. update();
  17. }
  18. @Override
  19. protected synchronized void observableDeactivated() {
  20. paramRepository.removeUpdatable(this);
  21. cancelOngoingRequestLocked();
  22. }
  23. @Override
  24. public synchronized void update() {
  25. cancelOngoingRequestLocked();
  26. // Adapt accordingly if paramRepository supplies a Result.
  27. cancellable = asyncOperator.request(paramRepository.get(), this);
  28. }
  29. private void cancelOngoingRequestLocked() {
  30. if (cancellable != null) {
  31. cancellable.cancel();
  32. cancellable = null;
  33. }
  34. }
  35. @Override
  36. public synchronized void onResponse(R response) {
  37. cancellable = null;
  38. result = Result.absentIfNull(response);
  39. dispatchUpdate();
  40. }
  41. // Similar process for fallible requests (typically with an
  42. // onError(Throwable) callback): wrap the failure in a Result and
  43. // dispatchUpdate().
  44. @Override
  45. public synchronized Result<R> get() {
  46. return result;
  47. }
  48. }
你的支持将鼓励作者继续创作

评论(0)

(无)