我正在参与「启航计划」

对app来说,即使将一切代码都怼一同也能运转,但代码难以维护,随着时间的累积,代码逐步堆积成屎山。为了防止架构腐坏,先后有MVC、MVP、MVVM等各种架构都被提出,但他们或多或少都有一些不足之处,随着android系统的开展及其开发生态越来越老练,google推荐的运用架构也渐趋完善,正好最近学习了官方架构,作为学习作用,今天就来对其做个总结。

架构准则

规划架构便是划分不同模块的责任以及他们的边界,并应遵从如下准则:

  • 别离关注点
  • 经过数据模型驱动界面
  • 单一数据源
  • 单向数据流

运用架构

依据别离关注点准则,app能够划分为UI层和Data层,当存在多处公用的杂乱事务逻辑时,能够在其间添加domain层进行处理,整体架构图如下:

Google应用架构设计流程

UI层

UI层是app的中心部分,大部分功用都和UI相关,所以UI层的规划十分重要。UI层由UI元素、状况以及处理状况的逻辑组成,因而能够将其分为如下两部分:

  • UI元素(UI Elements):对于app屏幕中的各种UI组件,首要担任制作作业
  • 状况容器(State holders):供给UI状况,并进行相应的逻辑处理。

Google应用架构设计流程

UI状况及事情

一般情况下UI元素由View或许Component组件组成,他们依据UI状况进行更新,因而要依据运用需求将UI状况界说好 。UI状况界说完成后,需求考虑用户操作发生的事情,将其归类,挑选适当的位置处理这些事情。

界说UI状况

前面提到UI元素依据状况更新界面,即界面由UI元素UI状况组成,不同的UI状况代表着不一起间UI元素显现的作用,如下图:

Google应用架构设计流程

考虑一款电影票售票运用,在选座购票时,选中和没选中便是座位的两个不同状况,其状况界说如下:

data class SeatUiState(
  val selected: Boolean = false
)

一般来说,在界说UI状况时要保证其不行变性, 这样能保证UI元素功用的纯粹性:仅依据状况更新界面,这样有利于对界面层进行单元测验,仅需求对状况容器行为及其发出的状况进行校验即可,然后省去许多杂乱低效的UI测验。这意味着不能再界面中修正UI状况,这导致同一个状况有多重来历(状况容器和界面元素),不仅违反了单一数据源准则,也简单发生数据不共同的问题。

事情决策树

UI事情便是用户操作引证所发生的各种事情,能够分为与事务逻辑相关事情和纯界面逻辑事情:

  • 事务逻辑一般指处理运用数据的逻辑,如付款或存储用户设置,一般由domain和data层担任处理此逻辑。在android运用中,ViewModel 是处理事务逻辑的特色处理方案。
  • UI行为逻辑(即UI逻辑)是指处理UI制作及显现的逻辑,如操控Button的显现或隐藏。

我们能够运用决策树的办法对UI事情进行分类:

Google应用架构设计流程

总体规矩便是:假如用户事情与修正UI元素的状况(如可翻开项的状况)相关,界面便能够直接处理这些事情。假如事情需求履行事务逻辑(如改写屏幕上的数据),则运用由 ViewModel 处理此事情。

状况容器

状况容器担任完成具体的事务逻辑,完成状况容器前,需求了解app的状况流向、容器分类以及他们各自的责任。

状况办理

一般运用单项数据流 (UDF)办理状况,这有助于完成不同层之间的责任别离。由于界面和 ViewModel 类的互动在基本能够理解为事情输入及其状况输出,因而这种联系能够用下图表示:

Google应用架构设计流程

所谓单项数据流便是状况向下活动、事情向上活动,数据流向如下:

  • ViewModel 会存储并揭露UI要运用的状况,UI状况是经过 ViewModel 转换的运用数据。
  • 界面会向 ViewModel 发送用户事情通知。
  • ViewModel 会处理用户操作并更新状况。
  • 更新后的状况将反馈给界面进行制作。
  • 系统会对导致状况更改的一切事情重复上述操作。

要注意ViewModel中不能包括UI逻辑(如:获经过Resources获取文本、点击时跳转、翻开弹窗等逻辑)。假如界面变的过于杂乱,依据关注点别离准则:需求创立一个简略类作为状况容器,对内部逻辑进行二次划分。状况容器尽量不要依靠Android SDK,而在Activity、Fragment、View中创立的类能够依靠。

UI状况生成流水线

状况容器中心作业便是生成UI状况,发送给界面进行处理,因而存在一条状况流水线,但介绍UI状况生成流水线之前需求先总结其间的一些术语。

UI状况

UI状况是描绘界面的特点。界面状况有两种类型:

  • 屏幕UI状况是需求在屏幕上显现的内容。
  • UI元素状况是指界面元素的固有特点,这些特点会影响界面元素的显现办法。界面元素或许处于显现或隐藏状况,而且或许具有特定的字体、字体大小或字体色彩。在 Android View 中,View 会自行办理此状况(由于它自身是有状况的),并揭露用于修正或查询其状况的办法。例如,TextView 类的 getset 办法用于显现该类的文本。

逻辑

运用中的逻辑能够是事务逻辑或界面逻辑:

  • 事务逻辑用于处理domain或data层的数据。
  • UI逻辑则代表UI组件的操控逻辑。

其与android生命周期的关联如下:

不依靠于界面生命周期 依靠于界面生命周期
事务逻辑 界面逻辑
UI状况

状况流水线一般依照如下顺序生成:

  • 直接在UI逻辑中生成和办理的UI状况,当数据不是来自data层,且无需将数据传递到外层时就能够才用这种办法办理UI状况,例如,一个简略且可重复运用的基本计数器。
  • UI逻辑 → UI。例如,操控答运用户跳转到列表顶部的按钮的显现或隐藏。
  • 事务逻辑 → UI。比方获取用户头像并展现。
  • 事务逻辑 → UI逻辑 → 界面。比方:给定UI状况之后,需求滑动到指定位置显现。

总体流程如下:

Google应用架构设计流程

一般情况下,假如一起用到事务逻辑和UI逻辑,有必要先运用事务逻辑,再运用UI逻辑,假如先运用UI逻辑,再运用事务逻辑,则意味着事务逻辑依靠于界面逻辑,这样破坏了UI的分层联系,增加代码耦合性,难以测验。

状况容器分类

由于app中的逻辑分为事务逻辑和UI逻辑两种,因而规划两种状况容器分别处理事务逻辑和UI逻辑:

  • 事务逻辑状况容器。
  • UI逻辑状况容器。

事务逻辑状况容器有如下特性:

特点 详细信息
生成界面状况 事务逻辑状况容器担任为其界面供给界面状况。此界面状况一般是处理用户事情以及从网域层和数据层读取数据的成果。
在 activity 从头创立后保存下来 事务逻辑状况容器会在 Activity 从头创立后保存其状况和状况处理流水线,然后帮助供给无缝的用户体会。假如会从头创立(一般是在进程终止后)状况容器,但无法保存其状况,则状况容器有必要能够轻松地从头创立最近一个状况,以确保共同的用户体会。
具有长期存在的状况 事务逻辑状况容器一般用于办理导航目的地的状况。因而,它们往往会在导航发生改变时保存其状况,直到从导航图中移除它们为止。
对界面来说独一无二,且不行重复运用 事务逻辑状况容器一般会针对某个运用函数(如 TaskEditViewModelTaskListViewModel)生成状况,因而仅适用于该运用函数。同一状况容器能够支持在不同外形标准的设备上运用这些运用函数。例如,运用的手机版别、电视版别和平板电脑版别都能够重复运用同一个事务逻辑状况容器。

事务逻辑状况容器一般用 ViewModel 完成,不过能够依据app的实际情况运用一般类。

UI逻辑状况容器对UI自身数据的操作逻辑,他或许依靠UI元素状况或UI数据源(如权限API或Resources),一般具有如下特点:

  • 生成UI状况并办理界面元素状况
  • Activity 从头创立后不再有用:保管在UI逻辑中的状况容器一般依靠于UI自身的数据源,在装备发生改变后保存对应数据简单导致内存走漏。
  • 引证了UI范围的数据源:生命周期 API 和资源(Resources)等数据源能够安全地引证和读取,由于UI逻辑状况容器与UI具有相同的生命周期。
  • 可在多个不同的界面中重复运用:同一UI逻辑状况容器的不同实例能够在运用的不同部分中重复运用。

UI逻辑状况容器一般运用一般类完成,当UI逻辑足够杂乱,能够移出界面时,会运用一般类状况容器。否则,UI逻辑能够在界面中以内嵌办法完成。

线程处理和并发

ViewModel中履行异步操作时应该运用Kotlin协程,假如是domain或许data层需履行耗时操作,应由他们自行担任线程切换作业。

UI层架构经过UI状况将事务逻辑与UI完成项别离,这样规划提高了事务逻辑的可测验性,而且下降在手机、平板、电视这些不同设备之间的适配难度。

Domain层

Domain层是一个可选层,担任封装杂乱的事务逻辑,或许由多个 ViewModel 重复运用的简略事务逻辑,假如运用比较简略,能够无需规划这一层。

Google应用架构设计流程

domain层具有以下长处:

  • 防止代码重复。
  • 提高代码可读性:由于domain层将较为杂乱的逻辑封装好了,使得引证domain的类更为简练。
  • 改进运用的可测验性。
  • 明确责任,防止呈现大型类。

domain层的类一般称为UseCase,即用例。用例一般用于组合data层逻辑,然后完成更为杂乱的逻辑,比方将新闻列表和对应的作者数据组合:

Google应用架构设计流程

用例没有自己的生命周期,所以能够在UI层的类、Service以及Application中调用用例。

用例类有必要具备主线程安全性,假如用例类存在长时间堵塞的操作,由他们自即将任务切到子线程中处理。

Data层

Data层包括运用数据和事务逻辑,它由多个库房组成,其间每个库房都能够包括零到多个数据源。应该为每种不同类型的数据分别创立一个存储库类。

Google应用架构设计流程

存储库类担任以下任务:

  • 向运用的其余部分揭露数据。
  • 集中处理数据改变。
  • 处理多个数据源之间的冲突。
  • 对运用其余部分的数据源进行抽象化处理。
  • 包括事务逻辑。

每个数据源类应仅担任处理一个数据源,数据源能够是文件、网络来历或本地数据库。数据源类是运用与数据操作系统之间的桥梁。

层次结构中的其他层绝不能直接拜访数据源;数据层的进口点始终是存储库类。状况容器类(请参阅界面层攻略)或用例类(请参阅网域层攻略)绝不能将数据源作为直接依靠项。假如运用库房类作为进口点,架构的不同层便能够独立扩缩。

该层揭露的数据应该是不行变的,这样就能够防止数据被其他类篡改,然后防止数值不共同的危险。不行变数据也能够由多个线程安全地处理。

依照依靠项注入的思想,存储库应在其构造函数中将数据源作为依靠项。

多层存储库

在某些涉及更杂乱事务要求的情况下,存储库或许需求依靠于其他存储库。这或许是由于所涉及的数据是来自多个数据源的数据聚合,或许是由于相应责任需求封装在其他存储库类中,如下:

Google应用架构设计流程

线程处理

调用数据源和存储库应该是主线程安全的,在履行长时间运转的堵塞操作时,这些类担任将其逻辑的履行移至适当的线程。

数据

一般情况下,app的显现数据和接口数据都存在差别,这种情形下需求别离模型类,所谓别离模型类便是依据app的显现需求界说新的模型类,删减掉接口中的部分数据。别离模型类能够带来以下好处:

  • 将数据削减到只包括需求的内容,然后节省运用内存。
  • 依据运用所运用的数据类型来调整外部数据类型 – 例如,运用能够运用不同的数据类型来表示日期。
  • 更好地别离关注点 – 例如,假如预先界说了模型类,大型团队的成员便能够在功用的网络层和界面层单独开展作业。

引证

运用架构攻略