青训营大作业架构设计介绍 | 青训营笔记

221 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第6天

架构设计

模块架构

整体多模块架构如下:

在app模块中常进行一些第三方库的初始化操作,除此之外,本项目还将DouYinEntryActivity放置在app模块,接收用户登录成功后的信息。

业务模块即普通业务所在的模块,module_main中放的是最外层的MainActivity,module_login主要是登录页面相关业务,module_rank主要是榜单页面相关业务,module_personal主要是个人中心页面相关业务。

服务模块为业务模块提供相应的服务功能,如lib_account模块向需要使用到用户账号功能的模块提供相关api接口。

通用模块存放的是本项目中多个模块可能会共同使用到的一些资源文件、自定义控件等等,而基础模块存放的则是多个项目可能会共同使用到的类,如Base系列文件和一些常用的ext扩展函数。

代码架构

整个项目的业务模块几乎全部采用“半MVVM-半MVI”架构,分为View-ViewModel-Repository层,View观察ViewModel层的可唯一信数据源进行UI的更新,ViewModel从Repo层获取数据并对数据的观察者对象进行更新。

以榜单列表界面为例,仓库层采用如下图所示的设计,在RankRepository中对本地和远程数据源进行处理,并将结果暴露给ViewModel层,而本地数据和远程数据的具体操作逻辑则分别由RankLocalDataSource和RankRemoteDataSource内处理,它们只负责对上层暴露相应的封装好的方法:

对于榜单列表的数据,我们的处理策略是:同时加载本地和网络数据,如果无网且本地数据为空则显示数据为空的界面;如果无网但本地有数据,则加载本地数据,并弹出网络异常的toast;如果有网但Api请求错误,则显示Api请求错误界面;如果有网但请求结果为空,则显示数据为空界面;如果有网但返回结果为失败,则显示网络请求结果失败界面;如果有网且结果不空且请求成功,则加载数据列表。

在ViewModel层,我们收集从repo层得到的流,并结果更新到存储有榜单列表数据的StateFlow中,并将该StateFlow转换为LiveData暴露给View层观察:

对于榜单版本列表界面,由于需要分页加载,使用上方的加载方式便不再合适,因此我们最终选取这样的加载策略:先加载本地数据库中的数据,然后进行网络请求获取远程数据,如果远程数据获取失败则直接加载本地数据,若此时本地数据也为空则显示数据为空界面;如果远程数据获取成功,则如果是需要刷新(即刚加载时),先删除本地数据库中的数据再插入,如果不需要刷新(即添加新的数据到List中时)直接将新数据插入数据库中。ViewModel层则直接观察Room返回的Flow数据,这样做实现了官方架构文档中推荐的数据层可信来源Truth Source,即ViewModel不关心数据的来源,它只需要从本地数据库中观察数据的变化即可:

其他业务模块的架构大致如上。