世界杯主题曲歌词 / 2025-07-14 14:40:17

安卓app架构与示例

架构是什么?原文

编程原则原文

层级架构原文

Clean 架构原文

示例原文

这5篇文章的目标是想介绍开发安卓主流app主流的架构。

首先简要介绍什么是架构?计算机和电子工业以IEEE的定义作为开发标准:

The fundamental concepts or properties of a system in its environment

embodied in its elements, relationships, and in the principles of its design and evolution. — IEEE

这个定义的关键字有:系统的基本概念和基本属性,系统的环境,系统的设计和演变进化,元素,关系和原则。对于安卓app开发,可以把定义的范围缩小到Android平台下,面向对象语言(Java/Kotlin, c/c++)。

Architecture can be simply described as the placement of classes in an app and how they communicate.

We draw an overview of roles and responsibilities of these classes while grouping them.

App架构简化描述为类和它们之间通信,并在总结类分组时给它们赋予它们的职责与角色。说到架构时,只关心类的职责与角色,小心不要掉入类的实现细节的诱惑中。架构要看它的先决条件,它的优点好处,也要考虑它的成本代价。

大型公司团队会有一堆专职的架构师,对大部分在小公司或自由开发者,一个好的架构要基本适用所有app,所以它的学习主要都是开发人员应该遵守的好的实践。App架构的要点就是应对app将来的变化,致力搞一个优雅地容纳这些变化的结构,好的架构就是帮助我们系统性地吸收掉这些变化。绝大部分编程原则都是基于分离/解耦把变化限制在最小的范围内。架构的代价主要出现在实现和维护的时候:1. 设计和架构本身要花时间精力,实现任何功能之前都要花时间和精力考虑它对架构的影响;2. 消耗团队全员的精力,每位成员都需要培训,需要对架构有感知;3.需要严明的纪律,一处不恰当的修改就可以把架构的完整性破坏掉。实现架构的额外代码对性能会有微小的影响,但性能不能作为绕开架构的的借口(除非特定场合)

其次,编程原则S.O.L.I.D是Robot C. Martin在2000年提出的一套面向对象编程的原则。学习怎么用工具是一回事,学习用它来创建有品质的产品又是另一回事。我们的工具有Android Studio, Java/Kotlin, Graddle, Dagger, RxJava, 等等,它们只能帮助我们得出方案,至于方案最终的质量,并不是工具决定的。方案的质量需要经由编程原则的指导,编程原则指引我们用正确的方式做事。结合编程原则地使用工具可以帮助我们得到最佳的方案。所有编程原则的初衷都是关注点分离(Separation of concerns),这聚焦于软件的某个方面,可以在函数/类或者更高层面应用。举个例子说,一个读取磁盘数据的类,是不应该关注怎么使用数据的。它就只管读和写数据,应用这些数据应该由其它类负责。

我们需要这么做,需要分享关注点,究其原因就是易经里说的"惟易不易",世界上惟一不变的就是变化,只要客户和用户在,app的变化早晚都会发生。所有的编程原则和我们采用的最佳实践都是为变化作准备。不是"变化"不好,而是变化常给app引入错误, 有bug潜入了或者app变得不稳定了。变化不可避免,能做的就是把它的影响范围限制到最小范围里。变化被限制在一个类甚至一个方法里,显然比布满整个app的范围要好多了。因此通过方法/类/模块来分解变化的影响范围。编程原则都直接或间接运用这样的分离。

这里有一篇专门介绍软件设计的关注点分离,不局限于安卓app开发。Software Design - Separation Of Concerns - with examples

Every element of a software application - a component, a layer, a package, a class or a method

should have one concern and implement it well.

Single Responsibility Principle (SRP)

Open/Closed Principle (OCP)

Liskov Substitution Principle (LSP)

Interface Segregation Principle (ISP)

Dependency Inversion Principle (DIP)

这几个编程原则不再展开,要更深入了解,可自行Google搜索。这篇博文说的还可以敏捷开发之原则 五大原则 SRP OCP LSP DIP ISP前3个原则针对一个责任下的继承体系内的类而后2个原则是针对不同责任的类间关系。

深入理解编程原则运用在软件设计上的一个很好的研究对象是设计模式,例如,OCP原则,模板方法或者Strategy这两个设计模式都可实现。

接下来会先后介绍层级架构和Clean架构

Software architecture is the art of drawing lines that I call boundaries — Robert C. Martin

Package包把相关的类组织在一起,表达一个接口。关注点分离是编程指导性原则,当我们把一组类放进一个package,意味着为它们引入一个组件Component,这一组类聚焦在同一个关注点上,就可以称这一组类为一个Layer,它们服务于同一个目标。称一个package里的类为一个Layer,那么可能会产生N层,一个app至少可以分3层:UI Layer, Domain Layer和Data Layer.

Activity, fragment, adapter和其它与用户交互有关的类都属于UI Layer层. 业务逻辑,应用专有的代码都属于Domain Layer层。数据库相关的类以及其它系统服务都算作Data Layer层。

分层的目的就是要为每个层划出边界,每一层都是和而且只和它邻近的层交互,而且是单向的。

UI --> Domain --> Data

UI层并不与Data层交互,这试图把变化的影响锁在某一层内或者某相邻的两层里,即便偶尔会有一些极其开放的层会跨层双向的,绝大部分情况下,我们都应该保持仅有单向的。这样最简单的架构模式使开始开发一个app的精力很少,这也是Google为什么推荐的架构app架构指南

关于层级,越接近于应用的,抽象级别应该越高,而接近系统服务,第3方类库,平台框架,数据库,网络等等这些细节级别越低。高级别的类不应该直接依赖低级别的具体实现类,要遵循面向接口编程,由接口阻断变化穿透架构层,影响蔓延到不应该出现的层级里。

接下来到Clean架构了,简单地说,架构是给类分组,然后为它们划定边界,管理它们穿越边界通信的方式。回顾前面说过的,好架构要让变化的影响范围最小化。一个办法就是最小化类的依赖,依赖越小,被修改的原因就越少,比如类B含有类A的对象实例,如果能把A的对象从类B中拿掉,这时就说B对 A没有了依赖,A无论有什么变化,类B都不受影响。

然而,每个类都没有依赖又不可能的,因此就有了要依赖接口而不是具体实现类。这样有2个明显的好处,1. 形成合约并且只暴露必要的方法 2. 在接口背后的具体类可以替换。架构中类跨越边界的依赖一定不要依赖具体实现类,而是依赖接口或抽象类。

Bob大叔是程序员中的老炮,他这个架构图对众多流行架构有着巨大影响。类所处的层级越高,它的依赖越少,越抽象,在图中越靠圆心。依赖由外向内,单向。这些类的通信遵循依赖倒置原则DIP.

结合安卓工程的3层结构,UI和Data层中的类级别低,Domain层中类的级别低,它们的依赖关系应该是:UI -use-> Domain <-use- Data.

UI层依赖Domain层的类向它操作输入或者取数据很直观,问题在于Domain层不依赖Data层的话,如何从Data层保存或读取数据?这就是DIP原则,依赖倒置。类似第2部分的例子,高层次的类不依赖低层次的类,它们都依赖于接口(高层次的类使用接口,低层次的类实现接口)

分层的代价有时蛮讨厌的,比如UI层只与Domain层取数据(单向依赖相邻的层),而数据又是在Data层,Domain在这只是简单透传数据。但好处也在这里,每一层都有机会注入拦截器,按需求修改数据,这是对将来的变化是个甜头。

最后结合一个具体示例app进行阐述

给类分层有2种方式,一种是使用android studio的module,另一种是使用package包。前一种方式边界清晰,后一种更紧凑。(有些应用除了层级外还有另一个组件维度,每个组件里又有分层的需要,那就需要用package来分层,module来表示组件),package下还可以用子package给更细粒度的类分组。

您应该关闭集成显卡吗?如何在系统上禁用集成显卡?
«行尸走肉»中的各大社区的组织结构