4.Android应用架构指南:概览

概览

移动应用用户体验

典型的android应用包含多个应用组件,包括ActivityFragmentService内容提供程序广播接收器。需要在应用清单中声明其中的大多数应用组件。Android操作系统随后会使用此文件来决定如何将你的应用集成到设备的整体用户体验中。鉴于典型的android应用可能包含多个组件,并且用户经常会在短时间内与多个应用进行互动,因此应用需要适应不同类型的用户驱动型工作流和任务。
请注意,移动设备的资源也很有限,因此操作系统可能随时终止某些应用进程以便为新的进程腾出空间。
鉴于这种环境条件,应用组件可以不按顺序地单独启动,并且操作系统或用户可以随时销毁它们。由于这些事件不受控制,因此不应在内存中存储或保留任何应用数据或状态,并且应用组件不应相互依赖。

常见的架构原则

随着android应用大小不断增加,定义的架构务必要能允许应用具有伸缩性、稳健性并且方便对应用进行测试。
应用架构定义了应用的各个部分之间的界限以及每个部分应承担的职责。为了满足上述需求,应该按照某些特定原则设计应用架构。

分离关注点

要遵循的最重要的原则是分离关注点。 一种常见的错误是在一个ActivityFragment中编写所有代码。这些基于界面的类应该仅包含处理界面和操作系统交互的逻辑。使这些类尽可能保持精简,这样可以避免许多与组件生命周期相关的问题,并提高这些类的可测试性。
请记住,你并不拥有ActivityFragment的实现;相反,这些只是代表android操作系统和你的应用程序之间关系的粘合类。操作系统可以在任何时候根据用户交互或系统条件(如低内存)销毁它们。为了提供令人满意的用户体验和更易于管理的应用程序维护体验,最好尽量减少对它们的依赖。

通过数据模型驱动界面

另一个重要原则是应该通过数据模型驱动界面(最好是持久性模型)。数据模型代表应用的数据。它们独立于应用中的界面元素和其他组件。这意味着它们与界面和应用组件的生命周期没有关联,但仍会在操作系统决定从内存中移除应用的进程时被销毁。
持久性模型是理想之选,原因如下:

  • 如果android操作系统销毁应用以释放资源,用户不会丢失数据。
  • 当网络连接不稳定或不可用时,应用会继续工作。

如果你的应用架构以数据模型类为基础,应用会更便于测试、更稳定可靠。

推荐的应用架构

基于上一部分提到的常见架构原则,每个应用应至少有两个层:

  • 界面层:在屏幕上显示应用数据。
  • 数据层:包含应用的业务逻辑并公开应用数据。

可以额外添加一个名为网域层(domain layer)的架构层,以简化和重复使用界面层与数据层之间的交互。

在这里插入图片描述

界面层

界面层的作用是在屏幕上显示应用数据。每当数据发生变化时,无论是因为用户互动(例如按了某个按钮),还是因为外部输入(例如网络响应),界面都应随之更新,以响应这些变化。
界面层由以下两部分组成:

  • 在屏幕上呈现数据的界面元素。可以使用Viewjetpack compose函数构建这些元素。
  • 用于存储数据、向界面提供数据以及处理逻辑的状态容器(如ViewModel类)。

在这里插入图片描述

数据层

应用的数据层包含业务逻辑。业务逻辑决定应用的价值,它包含决定应用如何创建、存储和更改数据的规则。
数据层由多个代码库组成,其中每个代码库可包含零到多个数据源。应该为应用中处理的每种不同类型的数据分别创建一个存储库类。例如,可以为与电影相关的数据创建一个MoviesRepository类,或者为与付款相关的数据创建一个PaymentsRepository类。

在这里插入图片描述

代码库类负责以下任务:

  • 向应用的其余部分公开数据。
  • 集中处理数据变化。
  • 解决多个数据源之间的冲突。
  • 对应用其余部分的数据源进行抽象化处理。
  • 包含业务逻辑。

每个数据源类应仅负责处理一个数据源,数据源可以是文件、网络或本地数据库。数据源类是应用与数据操作系统之间的桥梁。

网域层

网域层是位于界面与数据层之间的可选层。
网域层负责封装复杂的业务逻辑,或者由多个ViewModel重复使用的简单业务逻辑。此层是可选的,因为并非所有应用都有这类需求。请仅在需要时使用该层,例如处理复杂逻辑或支持可重用性。

在这里插入图片描述

这一层中的类通常被称为用例(use cases)或交互器(interactors)。每个用例都应该对单个功能负责。例如,如果多个ViewModel依赖于时区在屏幕上显示正确的消息,你的应用程序可以有一个GetTimeZoneUseCase类。

管理组件之间的依赖关系

你的应用程序中的类依赖于其他类来正常工作。可以使用下面的设计模式来收集特定类的依赖项:

  • 依赖注入(DI):依赖注入使类能够定义其依赖项而不构造它们。在运行时,另一个类负责提供这些依赖项。
  • 服务定位器:服务定位器模式提供了一个注册表,类可以从中获取其依赖项而不构造它们。

借助这些模式来扩展代码,因为它们可以提供清晰的依赖项管理模式(无需复制代码,也不会增添复杂性)。此外,还可以借助这些模式在测试和生产的实现之间快速切换。
建议在android应用中采用依赖项注入模式并使用Hilt库。Hilt通过遍历依赖项的树自动构造对象,为依赖项提供编译时保证,并为android框架类创建依赖项容器。

常见的最佳做法

编程是一个创造性的领域,构建android应用也不例外。 无论是在多个ActivityFragment之间传递数据,检索远程数据并将其保留在本地以在离线模式下使用,还是复杂应用遇到的任何其他常见情况,解决问题的方法都会有很多种。
虽然以下建议不是强制性的,但在大多数情况下,遵循这些建议会使你的代码库更强大、可测试性更高且更易维护:

  • 不要将数据存储在应用组件中。请避免将应用的入口点(如ActivityService和广播接收器)指定为数据源。相反,它们应该只与其他组件协调,以检索与该入口点相关的数据子集。每个应用组件存在的时间都很短暂,具体取决于用户与其设备的交互情况以及系统当前的整体运行状况。
  • 减少对android类的依赖。应用组件应该是唯一依赖于android框架SDK API(例如ContextToast)的类。将应用中的其他类与这些类分离开来有助于改善可测试性,并减少应用中的耦合。
  • 在应用的各个模块之间创建定义良好的职责边界。例如,请勿在代码库中将从网络加载数据的代码散布到多个类或软件包中。同样,也不要将不相关的职责(如数据缓存和数据绑定)定义到同一个类中。
  • 尽量少公开每个模块中的代码。例如,不要试图创建一个从模块中公开内部实现细节的快捷方式。可能会在短期内获得好处,但随着代码库的发展,由此会引发更多的问题。
  • 专注于应用的独特核心,以使其从其他应用中脱颖而出。不要一遍又一遍地重复编写相同的样板代码。相反地,你应该把时间和精力放在如何让你的应用变得独特上,让jetpack库和其他推荐库来处理重复的样板文件。
  • 考虑如何使应用的每个部分可独立测试。例如,使用定义良好的API从网络获取数据,可以更容易地测试将数据持久化到本地数据库中的模块。相反,如果将这两个模块的逻辑混合在一起,或者在整个代码库中分散网络获取数据相关的代码,那么有效测试将变得更加困难。
  • 类型负责其并发策略。如果某种类型正在执行长时间运行的阻塞工作,则应负责将该计算移至正确的线程。该特定类型知道它正在执行的计算类型及其应在哪个线程中执行。类型应该具有主线程安全性,这意味着,可以安全地从主线程调用这些类型而不会阻塞。
  • 保留尽可能多的相关数据和最新数据。这样,即使用户的设备处于离线模式,他们也可以使用应用的功能。请记住,并非所有用户都能享受到稳定的高速连接,即使有时可以使用,在比较拥挤的地方网络信号也可能不佳。

架构的优势

在应用中实现良好的架构会为项目和团队带来诸多好处:

  • 提高整个应用的可维护性、质量和稳健性。
  • 允许应用伸缩。尽可能减少代码冲突,使更多人和更多团队可以为同一代码库做贡献。
  • 有助于新人上手。架构能使项目保持一致性,让团队中的新成员可以快速上手,并在更短时间内提高效率。
  • 更易于测试。良好的架构鼓励使用更简单的类型,这些类型通常更易于测试。
  • 可以使用明确定义的流程有条理地调查 bug。

在架构方面的投入也会对用户产生直接积极的影响。用户能从更稳定的应用中获益;同时,由于团队效率的提高,用户还可以享受更多的功能。但是,架构也需要前期时间的投入。建议阅读更多的案例研究,了解其他公司在应用中使用良好架构的成功案例,这有助于向所在公司解释前期时间投入的必要性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值