Android应用开发的架构建议

一:移动应用与桌面应用的不同

通常的情况下桌面应用,即运行在window或者macOS上的应用会有单个的入口点,然后作为整体运行。但是Android应用却不是这样。典型的Android应用会有多个组件,包括Activity、fragment、service、内容提供器和广播接收器。

为何会有这样不同的设计呢?

我们可以想象下这样的场景:
操作一:
通过微信聊天的时候,我们想把今天的自己的好状态(自拍!)分享给朋友张三,此时我们可能会点主页键回到桌面,然后点击美颜相机icon来触发美颜照相机的intent,Android系统会启动照美颜相机应用(为什么不用微信自带的照相功能,因为那个木有美颜功能)。拍照完后,我们点击分享按钮,这时候再次触发一个intent启动微信并把拍下的照片发送给张三。
操作二:
我们再想象下这个过程中,可能微信打开并回到后台,美颜相机在前台。这时候有电话打进来,是张三叫我们上号打王者,我们拍照没拍完,我们又不想分享了。点击home键回到launcher,打开王者开始冲分。这时候我们已经打开三个应用,微信和美颜相机都在后台,而像王者这样的游戏内存占用不会很小的。当我们手机内存有点不足,系统随时可能终止相机或者微信以为王者应用提供更多的资源。
分析上面的场景,我们可以得知移动应用和桌面应用的不同之处在于,移动应用需要来回切换,并且随时销毁任何一个应用。这是和桌面平台应用的最大区别。基于这些区别,我们需要着重去应对这些点。

Android开发中需要注意的点
鉴于上述环境条件,操作系统或者用户都可以随时终止它们。而这些事件并不受到开发人员的控制,因此移动开发人员应该注意以下的两点:

  • 不应该在任何应用组件中存储应用数据或状态。
  • 应用组件间不应该相互依赖。
二:常见的架构原则

分离关注点
我们来看下不遵循这个原则的反例。
一个典型的错误就是在一个activity或者fragment中编写所有的代码。为什么这样做有问题?
主要有以下问题:

  • 这些基于界面的类应该仅仅包含处理界面和操作交互的逻辑。否则会导致代码耦合太高,难以维护
  • activity和fragment是Android操作系统和Android应用之间的粘合类,操作系统可能会根据用户的操作或者资源不足随时销毁它们。

通过模型来驱动界面
模型是负责处理应用数据的组件,模型独立于应用中的View对象和应用组件,因此它不受到应用的生命周期和上述系统销毁的影响。
其中模型最好是持久性模型,原因如下:

  • Android系统销毁应用的时候,用户数据不会丢失
  • 网络不稳定或不可用时候,应用还能继续工作。
三:推荐的应用架构

应用架构不存在最好的选择,每种架构都是适合的应用场景。
以下是架构是Google推荐的架构:
在这里插入图片描述

其中上图展示了应用的所有模块的交互依赖关系。
其中值得注意的是每一个组件仅依赖于下一级的组件。
其中存储区包括了本地数据和后台数据,这样的设计能打造良好的用户体验,无论用户上次使用时间是几个月前还是几分钟前,再次回到应用时候都会立刻看到应用本地保留的用户信息。如果数据过时了,应用数据会在后台进行更新。

下面通过一个例子来分析架构应该如何设计的问题。
场景是:我们写一个用户信息的功能。功能包括获取用户数据,并且在view上展示出来。其中数据包括:用户id、用户对象。
下面是我们创建出来的文件:

  • UserProfileViewModel:控制数据
  • UserProfileFragment:界面控制器
  • User_Profile:屏幕的界面布局定义。

界面的构建
使用LiveDataViewModel连接到view。
到这里我们会有个问题,UserProfileViewModel中设置的user字段需要一种方法来通知界面。这里我们可以通过fragment来进行传递。但是这里有种更好的方法,那就是使用LiveData。

LiveData
LiveData是一种可观察的数据存储器。应用中的组件可以使用LiveData监控对象的更改而无需在它们之间创建明确且严格的依赖。同时LiveData遵循组件的声明周期状态,如Activity、Fragment、Service,也包括清理逻辑来防止过多的内存消耗和内存泄漏。

有了LiveData,当UserProfileViewModel更新数据的时候,系统便会通知UserProfileFragment,同时由于LiveData的声明周期感知能力,因此当不需要使用他们的时候,会自动清理它们。同时我们使用过程中还需要了解以下几个问题:

  • 由于LiveData具有声明周期感知能力,当fragment处于onStart(),但是未收到onStop()方法时候,LiveData不会停止观察。当不需要引用的时候,不需要自动清理它们。当调用onDestroy()方法时,LiveData会自动移除观察者。
  • 当用户旋转设备屏幕,ViewModel会在更改后自动恢复,所以一旦创建了新的Fragment,它会接收同样的ViewModel实例。
  • 由于ViewModel对象比View的生命周期更长,所以不应该在实现中包含View对象的直接引用。
四:存储区

获取数据
上面我们通过LiveData把ViewModel连接到了view,那么如何获取数据?
实现数据获取的第一个想法就是直接调用后台服务器数据,然后传递给LiveData对象。这种设计可以行得通,但是会有个问题:ViewModel的时间范围和activity/fragment生命周期有关联。而当它们生命周期结束时候,后台服务的数据便会丢失。

这里我们可以让ViewModel把数据获取指派给一个新的模块,就是存储区。

假设我们的存储区的类名为UserRepository。
存储区用于处理数据操作,会提供干净的API来提供给其它模块检索数据。其中存储区的数据源是灵活的,可以是持久性模块的本地数据库、网络服务和缓存。

管理存储区和其他组件之间的关系
上面需要一个Webservice实例,我们的UserRepository才能获取用户的数据。我们可以让UserRepository通过创建Webservice实例来获取数据,但是需要考虑的问题是UserRepository不会是唯一需要用到webservice的类,假如每个类都创建一个新的webservice将会非常耗费资源。并且需要复制代码。

Google给我们提供了两个解决方案:

  • 依赖注入模式。可以定义webservice的依赖性而不用构造它们。
  • 服务定位器模式。提供一个注册表,类可以从中获取其依赖性而不用构造它们。

其中Google建议在Android应用中采用依赖项注入模式并使用Hilt库。

五:连接ViewModel和存储区

上面我们UserRepository的实现会抽象对Webservice对象的调用,但是只依赖于一个数据源不够灵活。UserRepository从后台获取数据后,并没有把数据存储在任何位置。想象这样的场景,如果用户离开了UserProfileFragment后再返回这个类,就要重新获取数据,即使获取的还是一样的数据。问题如下:

  • 浪费了网络流量
  • 用户必须重新等待加载数据

缓存数据
为解决上述问题,我们可以向UserRepository添加新的数据源,用来将User对象缓存在内存中。
有了数据缓存,用户无需每次离开应用都重新加载一次网络数据。同时用户离开应用并立即返回,存储区会从内存中的缓存中获取数据,这样就可以是界面立刻可见,无需重新异步加载。

保留数据
依赖目前为止我们的实现。假如用户离开应用,隔了几个小时再回来会发生什么?Android系统前已经终止了应用的进程。并且需要重新加载数据。
这种情况就有持久化存储的用武之地。Google推荐使用Room库。
Room是一个对象映射库。

单一可信来源
我们通过API获取数据的时候,同一的数据的获取来自后台不同的API,这是很常见的现象。例如获取头像可能是来自两个不同的API,它们甚至是不同的粒度级别。这样会导致界面显示混乱的信息。
我们可以把UserRepository实现为将网络响应保存在数据库中,这样的话数据库的更改可以出发LiveData对象的回调。这样一来数据库就能充当单一可靠来源。

六:测试每个组件

在分离关注点的原则中,遵循此原则的一大好处就是可测试性。

七:最佳做法总结

编程是一个创造性活动,Android应用开发不例外。其中开发问题的解决办法有很多种。所以谷歌推荐以下的做法并不是强制性的,但是从长远来看,遵循这些做法会使代码库更强大、更易于维护和可测试线。

  1. 避免将应用的组件作为指定数据源,例如Activity、Service和广播接收器。因为每个组件存在的时间都很短暂。
  2. 在应用的各个模块之间设定明确的职责界限。
  3. 尽量少的公布每个模块中的代码
  4. 考虑如何使得每个模块都可以独立测试
  5. 专注于应用的核心。避免一次又一次地编写样板代码,让Android架构组件和其它库去实现这些样本代码,将精力和时间放到应用的核心上面去。
  6. 保留尽量多的相关数据和最新数据。并非所有用户都能使用到稳定且高速的网络。
  7. 将一个数据源指定为单一可信来源。

参考资料:https://developer.android.google.cn/jetpack/guide
仅用于学习使用,侵删。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林树杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值