当前位置: 首页 > 科技观察

最实用的Android架构设计原则

时间:2023-03-17 15:42:03 科技观察

在开始之前,假设您已经阅读了我之前的文章“ArchitectingAndroid...Thecleanway?”。如果你还没有读过,不妨借此机会读一读,以便更好地理解:架构演化演化是指从某种状态向不同状态转变的渐进过程,新状态通常更好或更复杂。据此,软件随着时间的推移而发展变化,是一种架构的发展变化。事实上,好的软件设计必须能够帮助我们发展和扩展解决方案,保持其健壮性而不必重写所有内容(虽然在某些情况下重写更好,但这是本文的另一个主题,所以相信我,让我们专注于前面讨论的主题)。在本文中,我将解释我认为必要和重要的点,以保持基本代码的条理和清晰,记住下图,让我们开始吧!反应式方法:RxJava我不打算在这里讨论RxJava的好处,因为已经有很多关于它的文章和做得很好并且令人钦佩的人(我假设你已经有它的经验)。但是,我将指出Android应用程序开发的有趣方面以及它如何帮助我形成第一个清晰的结构。首先,我选择了一个响应式模式,通过将usecase(在这个清晰的架构命名约定中,它被称为interactor)转换为返回Observables,表明所有底层都遵循这条链,同样返回Observables。可以看到,所有的用例都继承了这个抽象类,并实现了抽象方法buildUseCaseObservable()。此方法将创建一个Observables来完成繁重的工作并返回所需的数据。需要强调的是,在execute()方法中,需要保证Observables是在一个独立的线程中执行的,所以要尽可能的降低对android主线程的阻塞程度。结果,主线程会被android主线程调度器推入线程队列的尾部(推回)。到目前为止,我们的Observables已启动并运行。但是,如您所知,必须观察它发出的数据顺序。为此,我将演示者(MVP模式的表示层的一部分)修改为观察者(订阅者),它们通过用例对发出的项目做出“反应”以更新用户界面。观察者是这样的:每个观察者都是每个演示者的内部类,并实现一个Defaultsubscriber接口,该接口创建基本的默认错误处理。将所有部分放在一起后,通过下图,您可以获得完整的概念:让我们列举一些摆脱基于RxJava的方法的好处:Subscribers和Observables之间的解耦:更易于维护和测试。简化异步任务:如果需要多次异步执行,如果需要多级异步执行,Java的线程和future的操作和同步就比较复杂,所以通过使用调度器,我们可以轻松(无需额外工作)跳转在后台和主线程之间,尤其是当我们需要更新UI时。它还避免了“回调坑”——它使我们的代码可读性差且难以遵循。数据转换/组合:我们能够在不影响客户端的情况下组合多个Observables,使解决方案更加灵活。错误处理:当任何Observables内部发生错误时向消费者发出信号。从我的角度来看,它有一点缺点,甚至要为此付出代价,对于还不熟悉这个概念的开发人员来说,学习曲线是一个学习曲线。但是你会从中得到一些很有价值的东西。获得成功的反应!依赖注入:Dagger2是关于依赖注入的,因为我已经写了一整篇关于它的文章,所以我不想说太多。强烈建议您阅读它,以便我们继续进行后续内容。值得一提的是,通过实现像Dagger2这样的依赖注入框架,我们可以获得:组件复用,因为可以在外部注入和配置依赖对象。当作为协作者注入对象时,由于对象的实例存在于一个隔离和解耦的地方,我们可以更改任何对象的实现,而无需对我们的代码库进行太多更改。可以将依赖项注入到组件中:这使得将依赖项注入到模拟实现的对象中成为可能,这使得测试更容易。Lambda表达式:Retrolambda没有人抱怨在他们的代码中使用Java8lambda表达式,在简化和摆脱大量样板代码之后更是如此,正如您在这段代码中看到的:但是,我有复杂的感觉,为什么?我们在@SoundCloud讨论了Retrolambada,主要是要不要用,结果是:1.优点:Lambda表达式和方法引用“try-with-resources”语句使用karma进行开发2.缺点:Java8API意外使用了a非常令人反感的第三方库使用Gradle和Android***的第三方插件,我们认为它不会为我们解决任何问题:您的代码看起来不错且可读性强,但这不是我们所接受的,因为所有最强大的IDE现在包括代码折叠选项,这至少以可接受的方式满足了这种需求。老实说,虽然我可能会在业余项目中使用它,但在这里使用它的主要原因是想尝试和体验一下Android中的Lambda表达式。是否使用它取决于你。我只是在这里展示我的愿景。当然,这个库的作者做出如此出色的工作值得我赞扬。测试方法论在测试方面,与第一个版本示例相关的部分没有太大变化:表示层:UI使用Espresso2和AndroidInstrumentation测试框架进行测试。领域层:JUnit+Mockito——它是Java的标准模块。数据层:用Robolectric3+JUnit+Mockito替换了测试组合。这一层测试过去存在于单独的Android模块中。由于当时(当前示例程序的第一个版本)没有内置单元测试支持,也没有构建像robolectric这样的框架,它很复杂,需要一群黑客的帮助才能运行。幸运的是,这都是过去的一部分,现在都是开箱即用的,所以我可以把它们放回数据模块中,专门针对它的默认测试路径:src/test/java。包组织我认为良好架构的关键之一是代码/包组织:程序员在浏览源代码时首先遇到的是包结构。一切都源于它,一切都取决于它。我们可以辨别出将应用程序打包成包的2种路径:逐层打包:每个包中包含的项目通常彼此不密切相关。这样,包的内聚性低,模块化程度低,包之间的耦合度高。因此,编辑功能需要编辑来自不同包的文件。此外,几乎不可能在一次操作中删除一个特征。按功能打包:包用于体现功能集。将与某个功能相关的所有项目(并且仅与该功能相关)放入一个包中。这种包具有高内聚性,高度模块化,包间耦合度低。密切相关的项目组合在一起。它们并没有分散在整个应用程序中。我的建议是摆脱功能包,主要好处如下:更高的模块化更容易的代码导航最小化功能范围如果与功能团队(如我们的)合作,@SoundCloud正在做的事情)也会非常有趣。代码所有权将更容易组织,也更容易模块化。这在许多开发人员共享代码库的成长型组织中是成功的。如您所见,我的方法看起来像是逐层打包:在这里我可能会犯错误(例如,在“用户”下组织所有内容),但在这种情况下我会原谅自己,因为这是一个学习曲线。为了目的,我想展示的是一个清晰的架构方法的主要概念。得到消息,不要盲目复制:-)。还需要做的:整理建筑逻辑我们都知道房子是从地基开始建造的。软件开发也是如此,我想说,从我的角度来看,构建系统(及其组织)是软件架构的重要组成部分。在Android平台上,我们使用的是Gradle,它其实是一个平台无关的构建系统,功能非常强大。这里的想法是通过一些提示和技巧简化您的组织构建应用程序的方式。在单独的gradle构建文件中按功能对内容进行分组,这样您就可以将带有“applyfrom:'buildsystem/ci.gradle'”的配置插入到任何Gradle构建文件中。不要将所有内容都放在一个build.gradle文件中,否则你会创建一个怪物,吸取教训。如果你想在项目的不同模块中重用相同的组件版本,那么创建一个依赖图是很好的;否则你需要在不同的模块中使用不同版本的组件依赖。还有一点就是你在一个地方控制依赖,组件版本冲突之类的事情一目了然。结束语到此为止说了这么多,总之,切记没有万能药。但是一个好的软件架构将有助于保持代码的清晰和健壮,以及保持代码的可扩展性和易维护性。我想指出几件事。面对软件问题,我们应该报告应该解决的态度:遵守SOLID原则,不过度思考(notover-engineer),务实,尽量减少模块对(Android)框架的依赖尽可能