使用Kotlin构建更适合Android的MVVM应用程序

lateinit var articleId:Int
val loading=ObservableBoolean(false)
val content = ObservableField()
val title = ObservableField()

//binding//
fun loadArticle():Single

=
repo.getArticleDetail(articleId)
.async()
.doOnSuccess { t: Article? ->
t?.let {
title.set(it.title)
content.set(it.content)

}
}
.doOnSubscribe { startLoad()}
.doAfterTerminate { stopLoad() }

fun startLoad()=loading.set(true)
fun stopLoad()=loading.set(false)
}

/**

  • 页面描述:ArticleDetailActivity,处理和用户的交互(点击事件),以及处理
  • viewModel层回调的数据,附加一些显示Loading,空状态和绑定生命周期等等的操作
  • Created by ditclear on 2017/11/17.
    */
    class ArticleDetailActivity : BaseActivity() {

override fun getLayoutId(): Int = R.layout.article_detail_activity

@Inject
lateinit var viewModel: ArticleDetailViewModel

//init
override fun initView() {
//统一都是KEY_DATA,别自己瞎命名
val articleID: Int? = intent?.extras?.getInt(Constants.KEY_DATA)
if (articleID == null) {
toast(“文章不存在”, ToastType.WARNING)
finish()
}

getComponent().inject(this)

mBinding.vm = viewModel.apply {
this.articleID = articleID
}
}

//加载数据
override fun loadData() {

viewModel.loadData()
.compose(bindToLifecycle())
// .doOnSubcribe{ showLoadingDialog() }
// .doAfterTerminate{ hideLoadingDialog() }
.subscribe({},{ dispatchFailure(it) })

}

}

他们是如何工作的呢?

在进入到ArticleDetailActivity页面之后

  1. init()方法->先进行数据的初始化,将viewModel和xml文件进行绑定
  2. loadData()方法->调用viewModel的方法

进入ArticleDetailViewModel

  1. 调用Model层获取详情方法获取数据源
  2. 根据需要使用RxJava操作符对数据进行转换,通过DataBinding更新UI
  3. 返回可观测的Single对象给View

回到ArticleDetailActivity页面

  1. 绑定生命周期,避免内存泄漏
  2. 对返回的可观测对象进行订阅
  3. 处理成功和失败的情况

至此,V-VM之间如何协作就清楚了。

M—VM

现在我们把View和ViewModel联系了起来,但是ViewModel该如何获取数据呢?

我们使用Retrofit来从后端获取网络数据。

interface ArticleService{
//文章详情
@GET(“article_detail.php”)
fun getArticleDetail(@Query(“id”) id: Int): Single


}

使用Room数据库来进行持久化

@Dao
interface ArticleDao{

@Query(“SELECT * FROM Articles WHERE articleid= :id”)
fun getArticleById(id:Int):Single

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertArticle(article :Article)

}

然后使用ArticleRepository.kt对网络和本地操作进行一层封装

/**

  • 页面描述:ArticleRepository
  • 提供数据给ViewModel层 , 处理网络数据和本地缓存之间的关系
  • Created by ditclear on 2017/11/17.
    */
    class ArticleRepository @Inject constructor
    (private val remote: ArticleService, private val local: ArticleDao) {

/* 文章详情

  • 先查看本地是否有缓存,如果没有那么再去请求网络,成功后更新本地缓存
    */
    fun getArticle(articleId: Int): Single
    =
    local.getArticleById(articleId).onErrorResumeNext {
    if (it is EmptyResultSetException) {
    remote.getArticleDetail(articleId).doOnSuccess { t -> t?.let { local.insertArticle(it) } }
    } else throw it
    }

}

先查看本地是否有缓存,如果没有那么再去请求网络,成功后更新本地缓存。

封装成Repository的原因是ViewModel不需要知道它的数据具体是从哪来的,这不是ViewModel这一层需要关心的事情。

即使你的项目没有进行数据缓存,总是从网络拉取数据,也建议封装成Repository,这意味着你的网络层是可以替换的,意义有点类似于封装一个ImageLoadUtil。

总体的流程就这么多,其实弄懂就很简单了。关键点是各层之间职责明确,以及解耦(Dagger2)和使用DataBinding时需要一个统一的规范。

而再细分,优化,也就是进行模块化、组件化的工作,深入些的插件化、热修复等等。不过万丈高楼平地起,我们的地基打的严实,以后的工作才会相对容易。

本文的代码都可以在github.com/ditclear/Pa…中找到

一些建议

建议一:在Activity或Fragment里处理点击事件

使用Presenter来继承View.OnClickListener

interface Presenter:View.OnClickListener{
override fun onClick(v: View?)
}

然后在BaseActivity/BaseFragment里实现它

abstract class BaseActivity : RxAppCompatActivity(),Presenter{

}

这样当我们要设置点击事件时,只需要

class ArticleDetailActivity : BaseActivity() {

//…
//init
override fun initView() {

mBinding.let{
it.vm=mViewModel
it.presenter=this
}
}
}

在xml中使用时,则统一使用presenter.onClick(view)方法

<android.support.design.widget.CoordinatorLayout>

<android.support.design.widget.FloatingActionButton
android:id=“@+id/stow_fab”
android:onClick=“@{(v)->presenter.onClick(v)}”
/>
</android.support.design.widget.CoordinatorLayout>

真正处理则放在相应的Activity/Fragment里

class ArticleDetailActivity : BaseActivity() {

//…
@SingleClick
override fun onClick(v: View?) {
when (v?.id) {
R.id.stow_fab -> stow()
//more …
R.id.other_action -> other()
}
}
//其它
private fun stow() {
}

//收藏
private fun stow() {
viewModel.stow().compose(bindToLifecycle())
.subscribe({ toastSuccess(it?.message?:“收藏成功”) }
, { toastFailure(it) } })

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

分享一份工作1到5年以上的Android程序员架构进阶学习路线体系,希望能对那些还在从事Android开发却还不知道如何去提升自己的,还处于迷茫的朋友!

  • 阿里P7级Android架构师技术脑图;查漏补缺,体系化深入学习提升(详细点击我的GitHub领取)

  • **全套体系化高级架构视频;**七大主流技术模块,视频+源码+笔记

需要架构学习笔记导图的可以点击我的GitHub领取

群内有许多技术大牛,高手如云,有任何问题,欢迎广大网友一起来交流,群内还不定期免费分享高阶Android学习视频资料和面试资料包~

外链图片转存中…(img-ZYazMoAc-1710686112246)]

  • **全套体系化高级架构视频;**七大主流技术模块,视频+源码+笔记

[外链图片转存中…(img-GhxRpM7L-1710686112246)]

需要架构学习笔记导图的可以点击我的GitHub领取

群内有许多技术大牛,高手如云,有任何问题,欢迎广大网友一起来交流,群内还不定期免费分享高阶Android学习视频资料和面试资料包~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值