Android设计架构 — 进化

758 查看

嘿!一段时间(收到很多的反馈意见)后,我认为是时候回到这个主题。这篇文章将给你另一种尝试,一种在我看来是设计现代移动应用架构的好方法(这里指的是Android 平台)。

在开始之前,假定你已经读过我的前面推送的文章Android设计架构 — 简洁之道 。如果没有读过,这是一个阅读的好机会,有助于更好地理解接下来的文章内容。

架构进化

进化(Evolution)代表一个渐进的过程,事物在这个过程演变成不同的形式,通常是更复杂或更好的形式。

如此说来,软件会随着时间改变,最终进化成为架构。其实一个好的软件设计必须保持其健壮性来帮助我们成长,拓展我们的解决方案,而不是重写代码(虽然有些情况下,重写代码是更好的方式,但是这应该是另一篇文章的主题。所以相信我,我们应该更多关注前面指出的问题)。

在这篇文章中,我将会带你检阅我认为必要和重要的关键点,以保持我们 Android 代码库的健壮性。记住这张图,我们开始吧。

响应式方法:RxJava

我不准备在此讨论 RxJava 的好处(我想你早已经尝试过它了, RxJava)。该技术已经有很多文章大牛,他们做了出色的工作!而我将指出它在 Android 应用开发方面的有趣之处,以及它是怎样帮助我在简洁的架构上踏出第一步

首先,我通过转化用例(use cases, 在简洁构架命名规则中称作“交互器”, interactors)选择了一个响应式模型返回 Obervables<T>。这意味着所有底层也将随着调用链返回 Obervables<T>。

 

如你所见,所有的用例都继承自这个抽象类并且实现了抽象方法 buildUseCaseObservable() ,这个方法将设定一个 Observable<T> 用以处理复杂逻辑并返回所需的数据。

值得一提的是 execute() 方法,我们要确保 Observable<T> 在一个单独的线程里执行,因此极大程度上避免了我们 Android 主线程的阻塞。结果由 Android 主线程调度器发送给主线程。

至此,我们的 Observable<T> 已经启动并且运行了。但是如你所知,必须有人关注由此发送的数据序列。为了实现这个功能,我把展示器(presenters, 表示层 presentation Layer 的 MVP 模式中的一部分)发展成 订阅者(Subscribers)。它将“响应(react”这些用例发出的信息,用以更新用户界面。

如下代码所示即为订阅者:

每个订阅者是都是嵌套在各自的展示器里的内部类,并且实现了 DefaultSubscriber<T>,用于默认错误处理。

在所有的组件都就绪后,你可以通过下图来了解整个想法:

让我们列举一下基于 RxJava方法的一些好处:

  • 低耦合的 观察者和 订阅者提高了【可】维护性,并使测试更简单;
  • 简化的异步任务(asynchronous tasks):在多于单个层面的异步执行是必须的情况下, Java 中的 Thread 和Future 就变得维护复杂、难以同步。通过调度程序我们可以轻松地在后台和主线程之间跳转(不需要额外的功夫),在需要更新 UI 的时候更是格外简单。同时也避免了我们说的“回调地狱(Callback Hell)”,它将使我们的代码无法阅读并且难以跟进。
  • 数据转化与组成:在不影响客户端的情况下可以组合多个 Observables<T>,这让我们的方案更具有扩展性。
  • 错误处理:当任何 Observable<T> (的实现类)出现错误时,都有一个信号发送给消费者。

我的观点里有一个缺陷,事实上这个方案需要付出的代价——那就是不熟悉这个概念的开发者必须要付出努力完成学习曲线。然而,你将从中获得无价的东西。响应式方法必胜!(译者注:学习曲线 Learning curve,表示了经验与效率之间的关系)

依赖(dependency)注入:Dagger 2

我不准备过多讨论依赖注入的问题,因为我已经写了一篇完整的文章了。我非常推荐你读这篇文章,这样你就能跟上这里讨论的内容。

值得一提的是,通过实现一个像 Dagger 2 这样的依赖注入框架,我们可以得到:

  • 组件复用, 因为依赖注入和配置独立于组件之外。
  • 得益于对象的初始化驻留在一个孤立且耦合性低的位置。当注入抽象方法的时候,我们只需要修改对象的实现方法,而不用大改代码库。
  • 依赖可以注入到一个组件中:完全可以注入这些依赖的模拟实现,这使测试更加简单了。

Lambda 表达式:Retrolambda

没有人会抱怨利用 Java 8 的 Lambad 表达式,尤其当他们简化了代码并且摆脱了很多模式的时候。可以看看以下代码:

然而,我却对此有复杂的感情,我会对此进行解释。原因是我和 @SoundCloud 有过一个关于Retrolambda讨论,主要围绕是否使用Retrolambda,结果如下:

  1. 优点:
    • Lambda 表达式和方法的引用;
    • Try with语句可以直接在资源上使用;
    • 开发的业报(Dev karma)。
  2. 缺点:
    • Java 8 API使用的频率很低;
    • 第三方库非常具有侵入性;.
    • 依赖第三方 Gradle 插件使之工作在 Android 上。

嘿!一段时间(收到很多的反馈意见)后,我认为是时候回到这个主题。这篇文章将给你另一种尝试,一种在我看来是设计现代移动应用架构的好方法(这里指的是Android 平台)。

在开始之前,假定你已经读过我的前面推送的文章Android设计架构 — 简洁之道 。如果没有读过,这是一个阅读的好机会,有助于更好地理解接下来的文章内容。

架构进化

进化(Evolution)代表一个渐进的过程,事物在这个过程演变成不同的形式,通常是更复杂或更好的形式。

如此说来,软件会随着时间改变,最终进化成为架构。其实一个好的软件设计必须保持其健壮性来帮助我们成长,拓展我们的解决方案,而不是重写代码(虽然有些情况下,重写代码是更好的方式,但是这应该是另一篇文章的主题。所以相信我,我们应该更多关注前面指出的问题)。

在这篇文章中,我将会带你检阅我认为必要和重要的关键点,以保持我们 Android 代码库的健壮性。记住这张图,我们开始吧。

响应式方法:RxJava

我不准备在此讨论 RxJava 的好处(我想你早已经尝试过它了, RxJava)。该技术已经有很多文章大牛,他们做了出色的工作!而我将指出它在 Android 应用开发方面的有趣之处,以及它是怎样帮助我在简洁的架构上踏出第一步

首先,我通过转化用例(use cases, 在简洁构架命名规则中称作“交互器”, interactors)选择了一个响应式模型返回 Obervables<T>。这意味着所有底层也将随着调用链返回 Obervables<T>。

 

如你所见,所有的用例都继承自这个抽象类并且实现了抽象方法 buildUseCaseObservable() ,这个方法将设定一个 Observable<T> 用以处理复杂逻辑并返回所需的数据。

值得一提的是 execute() 方法,我们要确保 Observable<T> 在一个单独的线程里执行,因此极大程度上避免了我们 Android 主线程的阻塞。结果由 Android 主线程调度器发送给主线程。

至此,我们的 Observable<T> 已经启动并且运行了。但是如你所知,必须有人关注由此发送的数据序列。为了实现这个功能,我把展示器(presenters, 表示层 presentation Layer 的 MVP 模式中的一部分)发展成 订阅者(Subscribers)。它将“响应(react”这些用例发出的信息,用以更新用户界面。

如下代码所示即为订阅者:

每个订阅者是都是嵌套在各自的展示器里的内部类,并且实现了 DefaultSubscriber<T>,用于默认错误处理。

在所有的组件都就绪后,你可以通过下图来了解整个想法:

让我们列举一下基于 RxJava方法的一些好处:

  • 低耦合的 观察者和 订阅者提高了【可】维护性,并使测试更简单;
  • 简化的异步任务(asynchronous tasks):在多于单个层面的异步执行是必须的情况下, Java 中的 Thread 和Future 就变得维护复杂、难以同步。通过调度程序我们可以轻松地在后台和主线程之间跳转(不需要额外的功夫),在需要更新 UI 的时候更是格外简单。同时也避免了我们说的“回调地狱(Callback Hell)”,它将使我们的代码无法阅读并且难以跟进。
  • 数据转化与组成:在不影响客户端的情况下可以组合多个 Observables<T>,这让我们的方案更具有扩展性。
  • 错误处理:当任何 Observable<T> (的实现类)出现错误时,都有一个信号发送给消费者。

我的观点里有一个缺陷,事实上这个方案需要付出的代价——那就是不熟悉这个概念的开发者必须要付出努力完成学习曲线。然而,你将从中获得无价的东西。响应式方法必胜!(译者注:学习曲线 Learning curve,表示了经验与效率之间的关系)

依赖(dependency)注入:Dagger 2

我不准备过多讨论依赖注入的问题,因为我已经写了一篇完整的文章了。我非常推荐你读这篇文章,这样你就能跟上这里讨论的内容。

值得一提的是,通过实现一个像 Dagger 2 这样的依赖注入框架,我们可以得到:

  • 组件复用, 因为依赖注入和配置独立于组件之外。
  • 得益于对象的初始化驻留在一个孤立且耦合性低的位置。当注入抽象方法的时候,我们只需要修改对象的实现方法,而不用大改代码库。
  • 依赖可以注入到一个组件中:完全可以注入这些依赖的模拟实现,这使测试更加简单了。

Lambda 表达式:Retrolambda

没有人会抱怨利用 Java 8 的 Lambad 表达式,尤其当他们简化了代码并且摆脱了很多模式的时候。可以看看以下代码:

然而,我却对此有复杂的感情,我会对此进行解释。原因是我和 @SoundCloud 有过一个关于Retrolambda讨论,主要围绕是否使用Retrolambda,结果如下:

  1. 优点:
    • Lambda 表达式和方法的引用;