android plaid,Plaid 开源库学习
Plaid 库是 google 之前的一个 demo 库,近期利用 kotlin 进行了重写.
某种程度上,是 Kotlin 和 Jetpack 的一个实践。
以下内容从三个方面来说:
Plaid 项目划分
Plaid 的代码结构
Plaid 的代码实现 - coroutines 协程实现
1. Plaid 项目划分
Plaid 模块化结构图:
plaid 代码结构模块化图
属于多模块化的设计, core 是继承模块,其他模块是业务模块。
2. Plaid 每个模块代码设计结构:
官方的设计类图如下:
plaid 设计类图
分为三层:
UI 层
Domain 层
Data 层
可简单看作 MVP 的一种延伸。
2.1 Data 层 -> model 层
根据数据来源分为两部分,本地数据LocalDataSource 和 网络接口数据 RemoteDataSource.
其他层次不关系 data 数据内部数据是来自哪,所以,在 data 层里面有个 Repository 类,外部只需要去 Repository 获取数据和存储数据,而不关心数据来自哪。
比如代码中的:UserRepository 和 UserRemoteDataSource.
Repository 中可以实现一部分的数据缓存,避免不必要的流量浪费和用户体验。
2. 2Domain 层
presenter 层
在这里使用了 UseCase 这个概念。
实际上是把一些小型的轻量级并且可以复用的逻辑单独放入一个类「UseCase」里面,
这些类将基于实际的业务逻辑开处理数据。
比如说回复评论,获取回答等单独的任务。
例如:获取回答列表,有太多地方在使用这个接口去获取, 查找问题时也不是很方便,如果统一,确实会有些帮助
例如:PostReplyUseCase
个人理解:弱化了 ViewModel 的作用,把一些在 ViewModel 里面处理的逻辑划分给了 UseCase。
现在 ViewModel 只负责拿到数据后的 UI 逻辑处理.
这也是为什么在上面官方给出的图中,把 ViewModel 划分在 UI 层的一个原因。
2.3 UI 层
在这个设计中,包含了 View 层「Activity, fragment, xml」和 Presenter 逻辑层「ViewModel 被弱化了」。
在这一层中,ViewModel主要是为了 UI 提供数据并根据「用户操作触发不同的逻辑执行」, 依赖着 UseCase 去获取数据,然后把数据通过 LiveData 的形式输出给 Activity 「View 层」。
LiveData 是 ViewModel 对外部输出的唯一数据。
2.4 总结以下代码结构上的逻辑
由上面的可得到, 代码执行的逻辑是:
Activity->ViewModel: 执行某个逻辑
ViewModel->XXXUseCase: 执行某个复杂逻辑
XXXUseCase->XXXRepository: 去 data 中拿取数据
XXXRepository->XXXDataSource: 真正拿数据的地方
XXXRepository-->XXXUseCase: 在 UseCase 中处理一下
XXXUseCase-->ViewModel: 返回数据给 viewModel
ViewModel-->Activity: liveData 反馈给 Activity
代码流程图
3. 从代码层面看一看
想要分享这个库的原因之一,它使用了 kotlin 和 Jetpack 实现。
kotlin ,当然这里使用 coroutine 实现。
Jetpack ,使用了 LiveData, Room , Data Binding
使用前提:引入协程库。
代码
首先在 View 层的 Activity或者 Fragment 中获取到 ViewModel;
手动调用 ViewModel.getXXX() 去获取数据
对一些需要的数据利用 LiveData 观察变化,而获取数据和做 UI 改变
下面看一些具体的代码实现:
3.1 获取到 ViewModel
在 Plaid 中使用的是 Dragger 实现注入的。
代码大致如下:
Provides
fun provideLoginViewModel(
factory: DesignerNewsViewModelFactory
): LoginViewModel =
ViewModelProviders.of(activity, factory).get(LoginViewModel::class.java)
上述代码省去了 Inject 的注入过程。
嗯……因为个人原因,不太喜欢使用 Dragger.
在Activity 中观察 liveData 代码:
// 在 activity 中的 observer
viewModel.uiState.observe(this, Observer {
val uiModel = it ?: return@Observer
// balabala 的 UI 上的操作
....
})
3.2 ViewModel 发起数据请求
代码示例如下:
// 在 ViewModel 代码中
private fun getComments() = viewModelScope.launch(dispatcherProvider.computation) {
val result = getCommentsWithRepliesAndUsers(story.links.comments)
if (result is Result.Success) {
// 切换到主线程
withContext(dispatcherProvider.main) {
//通过 liveData 抛给 Activity 的 observer
emitUiModel(result.data)
}
}
}
代码中 viewModelScope 来自 liftcycle-viewmodel-ktx-2.2.0 ,是 ViewModel 的一个扩展属性,源码如下:
/**
* [CoroutineScope] tied to this [ViewModel].
* This scope will be canceled when ViewModel will be cleared, it.e [ViewModel.onCleared] is called
*
* This scope is bound to [Dispatchers.Main]
*/
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main))
}
返回的是一个 CloseableCoroutineScope.
同时,这里会有一个 setTagIfAbsent(xxx) 在 mBagOfTags 这里存储了 CloseableCoroutineScope 的实例 ,会在 ViewModel 被销毁时回收掉。
参考 viewModelScope 的销毁
3.3 UseCase 调度接口
上述代码中的 getCommentsWithRepliesAndUsers 其实是 GetCommentsWithRepliesAndUsersUseCase 的一个实例, 最终,在这里调用的方法为:
// get the users
val usersResult = userRepository.getUsers(userIds)
调用路径为:
代码2
3.4 Repository 的实现
其实这一层的需要不需要,完全看开发。
在这个例子中 UserRepository 的实现,里面有一个成员变量 cachedUsers, 用做缓存,减少不必要的网络访问。一些需求是不需要这样的逻辑的,可完全抛弃掉 Repository。
class UserRepository(private val dataSource: UserRemoteDataSource) {
private val cachedUsers = mutableMapOf()
suspend fun getUsers(ids: Set): Result> {
...
}
}
Repository 的作用:
做一些缓存,减少不必要的接口再次访问;
处理一下数据,精简逻辑和数据,dataSource 返回的数据,需要经过它的处理再返回给 ViewModel
数据来源为两方面 local 和 remote ,需要经过 Repository 的合并或者筛选再返回给 ViewModel
3.5 DataSource 的实现
往往我们会认为 DataSource 是来自网络的,而忽视了本地的数据,所以应该把 DataSource 分为两类,一种是 local 数据,一种是 remote 数据。
代码实现:
// safeApiCall() 是一个高阶函数,本质上是做了 try catch 操作「最小程度代码块的 try catch」
suspend fun getUsers(userIds: List) = safeApiCall(
call = { requestGetUsers(userIds) },
errorMessage = "Error getting user"
)
//请求数据
private suspend fun requestGetUsers(userIds: List): Result>{
....
service.getUser(userIds)
...
}
一定要让 DataSource 尽可能纯粹,它只负责请求数据,返回数据,而不对数据进行处理。
对于 safeApiCall() 和 Result 的实现,感兴趣的可以私下看一看。
总结
其实在这部分代码中,很多 kotlin 的小细节都值得学习,因为太过详细,这里不再介绍,真心推荐一下,源码还是不错的,虽然使用了 Dragger ,在阅读体验上并不是很好,但还是特别值得学习的一个代码。
当然上面是个人的一些浅显理解,有错误的地方还请指出。
版本号参考:
lifecycle-viewmodel 版本号:2.2.0
lifecycle-viewmodel-ktx 版本号:2.2.0
使用 coroutines 要求
引入 org.jetbrains.kotlinx:kotlinx-coroutines-core 和 org.jetbrains.kotlinx:kotlinx-coroutines-android
引入 retrofit
- 2.6.0 以下版本,需要使用 https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter 兼容;
- 2.6.0 以上版本,不需要兼容, 支持 suspend
android plaid,Plaid 开源库学习相关推荐
- Android常用热门开源库汇总(持续更新)
原文转载:https://www.yundashi168.com/344.html 请及时关注原文网站,因为后续持续更新都在原网站更新.请多多点赞和关注. 前言 收集了一些比较常见的开源库,特此记录( ...
- Android 优秀的开源库
前言 收集了一些比较常见的开源库,特此记录(已收录350+).另外,本文将持续更新,大家有关于Android 优秀的开源库,也可以在下面留言. 一 .基本控件 TextView HTextView 一 ...
- Android开发常用开源库
Android 开发常用开源库 一.网络请求 二.图片加载 三.数据库 四.通讯 五.注解 六.JSON解析 七.性能优化 八.性能优化之布局分析 九.工具类 十.状态栏 十一.扫码库 十二.播放器 ...
- Android开发:开源库集合
开源库大全 目录 抽屉菜单 ListView WebView SwitchButton 按钮 点赞按钮 进度条 TabLayout 图标 下拉刷新 ViewPager 图表(Chart) 菜单(Men ...
- android 开源图表动画,Android 图表开发开源库MPAndroidChart-Go语言中文社区
上面是APP中实现的效果图(点击可以放大查看) 图1的效果不是用这个实现的,如果感兴趣可以参考我这篇文章 Android渐变圆环 总体来说,MPAndroidChart可能是目前Android 开发 ...
- android 沉浸式开源库,Android沉浸式状态栏
关于Android沉浸式状态栏, 网上已经有很多开源库, 虽然开源库可以解决某些特定布局下的沉浸式状态栏问题, 但是遇到比较特殊的布局就无法解决了, 所以了解一下沉浸式状态栏如何实现是有必要的. 无论 ...
- Android 本地缓存开源库 Reservoir 替代SharedPreferences
一.Android SharedPreferences 简介 众所周知,SharedPreferences是一种轻型的Android数据存储方式,它的本质是基于XML文件存储key-value键值对数 ...
- Android最佳的开源库(四)
2019独角兽企业重金招聘Python工程师标准>>> 网络 OkHttp:Android的HTTP客户端库. AndroidAsync:异步通信库. 通知推送 PubNub:用来处 ...
- 积累的 Android 开发各种开源库
activity场景切换动画 TextView跨度行为,支持惊人的recyclelerView功能的流式布局行为 RxJava Essentials 中文翻译版 Rx和RxJava文档中文翻译项目 R ...
- android 地址选择器_48种Android开发的开源库汇总
一.安全 1.SQLCipher Sqlite加密工具 项目地址: https://github.com/sqlcipher/sqlcipher 帮助文档: http://sqlcipher.net/ ...
最新文章
- Android开发资料学习(转载/链接)
- 用 Flask 来写个轻博客 (28) — 使用 Flask-Assets 压缩 CSS/JS 提升网页加载速度
- 文件管理器android实现,Android开发之简单文件管理器实现方法
- oracle 复制组删除,利用copy在ASM磁盘组之间迁移
- xampp打开mysql的admin访问被拒绝_U盘插入电脑提示无法访问?别怕,我找到解决办法了...
- Flex 学习笔记------FLACC Crossbridge
- POJ1163 数字三角形
- android 捕获Home键和ACTION_TIME_TICK广播
- 教你如何在Python中读,写和解析CSV文
- android+统计功能,React Native 轻松集成统计功能(Android 篇)
- Codeforces Round #341 (Div. 2)
- 实现Fragment在ViewPager中滑动
- Cocos2dx---之粒子系统
- Eclipse创建的JSP文件链接SQLServer2012的方法(附图)
- MFC控件重叠显示问题
- 电影天堂python分页爬取
- 零基础做一个微信答题小程序(二)
- 基于U盘传播的简单病毒
- Java 图片加水印
- IDEA 启动项目报错 Error running 'XXXApplication': No jdk for module 'XXX'
热门文章
- 使用threading+queue队列,发送get请求,输出状态码
- php5.6 mongo 扩展,docker php5.6镜像创建,包括常用扩展安装
- 线上IIS应用程序池自动关闭
- getBoundingClientRect方法获取元素在页面中的相对位置
- 2017.10.2 计算机算法分析----0-1背包问题
- iOS打包后收不到推送信息
- BestCoder Round #66 (div.2)B GTW likes gt
- decimal保留千分位
- java多线程通信生产者和消费者简单例子
- Zookeeper开源客户端curator