【融云分析】融云 SealTalk 优化之路- Android JetPack

【融云分析】融云 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

       

标签: , ,