【融云分析】融云 SealTalk 优化之路- Android JetPack
内容简介:
本文结合融云 SealTalk Demo, 介绍了如何利用新一代的 Android JetPack 组件构建架构清晰,低耦合度 APP 的过程。融云 SealTalk 支持应用内社交等场景,体验单群聊、聊天室、音视频通话、红包、小视频、动态表情等通讯能力,供大家下载体验。
架构选择
融云开源项目 SealTalk 最近进行了重构,旧版 SealTalk 是采用的 MVC 架构, 而新版本我们采用了MVVM 架构。
为什么选择 MVVM
旧版存在的问题
SealTalk在旧版中采用了 MVC 架构, Activity 和 Fragment 充当了 Controller 层,因此在旧版 SealTalk 中存在以下几点问题。
在 Controller 层(即 Activity 和 Fragment ) 会需要编写大量的异步操作( 网络请求或数据库查询等) 使Activity 和 Fragment 代码逻辑量大,非常臃肿。
在 Activity 和 Fragment 需做大量的异步请求,需对请求做管理操作,确保页面销毁时理清操作,否则可能会造成内存泄漏。
多地方使用相同数据时,当数据( 例如个人信息或好友信息、群信息等, 多个页面都有使用到)有改动, 就必须主动去同步,造成逻辑大量重复并增大了逻辑难度。
复用性比较差, 耦合度比较高。
为什么不选择 MVP?
MVP 架构是在 MVC 架构基础上的优化,把 Activity 和 Fragment 的逻辑操作、异步请求等都提取出来, 作为 Presenter 层。 而 Activity 和 Fragment 则作为了 View 层负责界面的展示刷新。 Presenter 层通过定义回调接 口,把数据的操作返回给 View 层。通过结构图可以看出, View 和 Model 没有直接的交互, 都是通过 Presenter 层进行操作, 实现了 View 层和 Model 层的解耦。
假如 SealTalk 选用 MVP 结构,将网络请求和逻辑放到对应的 Presenter 中处理, 减少Activity 和 Fragment 的负担。通过配合使用 Rxjava 绑定生命周期,也可避免页面销毁造成的内存泄漏问题。但是 Presenter 层和 View 交互是通过定义接口来进行的,这可能意味着需要定义大量接口,灵活性和复用性比较差。并且对于多处使用相同信息这种情况,其和 MVC 模式 一样, 也需主动拉取数据进行同步,并且 Presenter 层还持有了 View 的 this , 耦合度较 高。
根据 SealTalk 的业务情况,MVP 相对于 MVC 的架构模式会比较好 一些, 但是还是有数据同步等问题没有很好的解决。
MVVM 的优势
MVVM 分为 View, Model 和 ViewModel 三层。
MVVM 最大的特色是双向绑定,并且是以数据来进行驱动。View 层可以通过 ViewModel 和数据绑定,当 Model 发生变化时,ViewModel 则会把最新数据发送给 View, 从而解决同一数据发生改变其他地方需要主动拉取数据的问题。
同一个 ViewModel 可以复用到多个 View 中, View 也可以使用多个 ViewModel。并且ViewModel只需要关注数据和业务逻辑,不需要和UI或者控件打交道。 UI 可以更具自己的需求去订阅不同的数据,这就进一步实现了View 和 ViewModel 的解耦, 并且提高了复用性。
选择 MVVM 的另一个重要原因是 Google 官方推出了 Architecture 组件, 给快速搭建 MVVM 提供一套规范,节省了开发成本。
Android 官方提供的 MVVM 规范
Android JetPack
Android JetPack 是 Google 提供的一套依赖库、工具集和开发指南。主要是为帮助开发者更轻松的编写高质量量的应用程序。
其组件都包含在 androidx.* 库中. androidx.* 从平台中分离出来作为单独的库来维护。这意味着 androidx.* 可向后兼容,它可不随着平台而发布,可独立更新升级,这有利于开发者能快速使用上更新的、更稳定的依赖库。
使用条件:
AndroidStudio 版本需升级到 3.2 及以上版本
SDK targetVersion 版本需至少为 26
Architecture 组件
Google 在 Android JetPack 整合了之前的 Architecture 组件。 Architecture 组件中, 提供了在 Android 开发应用使用 MVVM 的规范。
Google推荐的架构图
Activity/Fragment
即为 View 层, 主要是负责数据的展示刷新和交互事件触发。
ViewModel
ViewModel 类的设目的是以生命周期意识的方式存储和管理与 UI 相关的数据。ViewModel 类允许数据在配置更改(如屏幕旋转)中生存。在配置更改期间,ViewModel对象会自动保留,以便它们保存的数据立即可用于下一个 Activity 或 Fragment 实例。
注意: ViewModel 决不能引用视图、生命周期或任何可能包含对活动上下文的引用的类。
如果 ViewModel 中需要使用系统 Service 服务等,可以继承扩展 AndroidViewModel 类,此类有一个带有 Application 类型参数的构造方法。
LiveData
LiveData是一个可观察的数据持有者类。与常规 Observable 不同,LiveData 是生命周期感知的,这意味着它尊重其他应用程序组件的生命周期,例如 Activity,Fragment 或 Service。 此感知确保 LiveData 仅处在其他组件生命周期活跃的状态下进行数据更新(当生命周期处于 STARTED 或 RESUMED 状态,LiveData 会将其视为处于活动状态);非活跃状态时,则不会接受数据;当对象生命周期结束时, LiveData 也会随之结束。
使用 LiveData 有以下几个优点:
确保 UI 界面显示正确的数据状态
无内存泄漏
LiveData 的观察者是绑定在对象的生命周期上,当对象销毁,观察者也会随之进行清理。
可避免发生未因停止的 Activity 导致崩溃
当 Activity 停 止,生命周期处于非活跃状态, LiveData 则不会进行数据返回操作。
不需要手动去处理生命周期
总是能更新最新数据
如果生命周期由非活动状态变为活动状态时, LiveData 会接收最新数据。例如,位于后台的活动在返回前台后立即接收最新数据。
当页面配置发生更改, 仍可获取正确数据(例如屏幕旋转)
共享资源
Repository
仓库, 主要是用于各种数据的业务请求操作( 网络请求和数据库查询), 可以把它看作 Model 层的 一部分。
Room
Room 是在 Sqlite 之上添加的一个抽象层, 以便实现更加强大的数据库访问,其可直接返回 LiveData, 用于监听数据返回。下面是使用 ViewModel 、 LiveData 和 Room 进行数据库查询操作的示意图。
Retrofit
网络请求框架。它可以返回 LiveData 对象, 用于监听数据返回。
使用上面的规范可快速搭建 MVVM 架构。
新版 SealTalk 架构
SealTalk 2.0 重构了内部逻辑实现,整体代码将更清晰易读。使用 LiveData + ViewModel + Retrofit 2.0 + Room 等框架基于 MVVM 模式进行开发。
由于 DataBinding 存在调试难, 并要在 XML 编写等问题, 所以经过讨论之后, 决定弃用 DataBinding。
新版本架构图
Ativity/Fragment 作为 View 层, 负责界面显示和事件交互。
UserInfoViewModel 等为 ViewModel 层,连接 View 和 Model 的桥梁,数据通过 LiveData 返回。ViewModel 可通过调用不同的 Task 来获取不同的数据源。
Task 层即为 Repository, 根据不同的接口或数据属性, 分别封装了不同的 Task, 例如关于 User 的数据操作就封装在了 UserTask 中,这样功能模块职能清晰并复用性高。
Service 和 Dao 是请求网络数据和数据库数据操作,分别使用了Retrofit 和 Room 的依赖库。
由于请求数据操作移到 Task 层,从而解决了旧版 Activity/Fragment 中代码臃肿的问题, 并无需重复编写同一数据类型的请求逻辑,提高了代码复用。
由于 LiveData 是依附于 Activity / Fragment 的生命周期的特性, 在Activity 或Fragment 销毁时, 则其不会返回数据并会清理, 所以无需担心其内存泄漏的问题。并且 LiveData 在 Activity/Fragment 重新进入Resume 状态时, 会检测数据有没有更新,有的话会主动把新数据传递给 View, 使用同一数据的地方就无需主动同步数据。
数据请求流程
在新版中也对数据的请求机制做了信息设计处理,请求数据分为三种:
网络请求需要缓存的
网络请求且不需要缓存的
数据库直接查询的
需要网络请求并需要缓存的
- Task 层首先会查询数据库,然后返回当前数据库中的缓存数据,此数据用于请求网络时, 页面友好展示。然后再请求服务器,获取最新数据,获取数据成功后会把新数据保存至数据库。最后再进行一次数据库查询, 获取数据库中的最新数据。此机制虽然烦琐,但极大的保证了界面展示的数据与最新数据的一致性。
网络请求且不需要缓存的
Task 直接进行网络请求并返回数据。
数据库直接查询的
新版架构完美的解决了旧版存在的问题,更多细节可参考新版SealTalk github 源码: https://github.com/sealtalk/sealtalk-android