阅读 5564

真香!Kotlin+MVVM+LiveData+协程 打造 Wanandroid!

关注 秉心说,公众号后台回复 玩安卓 即可获取 Wanandroid 安装包。

Wanandroid 是鸿洋鸿大大的安卓开源知识网站,包含最新博文,最新项目,常用工具,公众号文章收录等等功能,同时也开源了所有 API 接口,方便大家打造自己的 Wanandroid 客户端。Github 上关于 Wanandroid 的客户端也层出不穷,Java的,Kotlin 的,Flutter 的,Mvp 的,MVMM 的,各种各样,但是还没看到 Kotlin+MVVM+LiveData+协程 版本的,加上最近正在看 MVVM 和 LiveData,就着手把我之前写的 Mvp 版本的 Wanandroid 改造成 MVVM,项目地址 。注意,mater 分支是年久失修的 Mvp 版本,不一定保证可以运行。mvvm-kotlin 分支是最新代码。

关于 MVVM,大家应该也比较熟悉了,上一张 MVVM 经典架构图:

Model-View-ViewModelView 指绿色的 Activity/Fragment,主要负责界面显示,不负责任何业务逻辑和数据处理。Model 指的是 Repository 包含的部分,主要负责数据获取,来组本地数据库或者远程服务器。ViewModel 指的是图中蓝色部分,主要负责业务逻辑和数据处理,本身不持有 View 层引用,通过 LiveDataView 层发送数据。Repository 统一了数据入口,不管来自数据库,还是服务器,统一打包给 ViewModel ,我在项目中并没有使用数据库,而是使用缓存代替。

除了 MMVM 以外,我用 协程 代替了 RxJava。这里先不论协程和 RxJava 孰优孰劣,只是用惯了 RxJava,协程的确会给你耳目一新的感觉,用同步的方式写异步代码。在 Java 中并没有协程的概念,Kotlin 中在编译期实现了协程,通过类似状态机的实现。协程可以看做是轻量级的线程,不会存在上下文切换的带来的性能损耗,理论上是比线程效率更高的。

下面以登录页面 LoginActivity 为例,看一下数据流程。

Model

@POST("/user/login")
fun login(@Field("username") userName: String, @Field("password") passWord: String): Deferred<WanResponse<User>>
复制代码

这是登录 Api 接口。

class LoginRepository : BaseRepository() {

    suspend fun login(userName: String, passWord: String): WanResponse<User> {
        return apiCall { WanRetrofitClient.service.login(userName, passWord).await() }
    }
    
}
复制代码

LoginRepository 中定义具体的登录逻辑,通过 Retrofit 调用登录接口,返回 WanResponse<User>。注意,要在协程中使用,所以定义为 suspend 方法。

ViewModel

class LoginViewModel : BaseViewModel() {
    val mLoginUser: MutableLiveData<User> = MutableLiveData()
    val errMsg: MutableLiveData<String> = MutableLiveData()
    private val repository by lazy { LoginRepository() }

    fun login(userName: String, passWord: String) {
        launch {
            val response = withContext(Dispatchers.IO) { repository.login(userName, passWord) }
            executeResponse(response, { mLoginUser.value = response.data }, { errMsg.value = response.errorMsg })
        }
    }
}
复制代码

LoginViewModel 持有 LoginRepository,并通过它执行具体登录逻辑,这一块使用协程执行。返回结果通过 executeResponse() 方法处理,这是我自己封装的方法:

suspend fun executeResponse(response: WanResponse<Any>, successBlock: suspend CoroutineScope.() -> Unit,
                                errorBlock: suspend CoroutineScope.() -> Unit) {
        coroutineScope {
            if (response.errorCode == -1) errorBlock()
            else successBlock()
        }
    }
复制代码

Kotlin 的一些函数式编程语言特性会给我们的开发带来一些便利。executeResponse() 提供了统一的响应错误处理。

View

 mViewModel.apply {
        mLoginUser.observe(this@LoginActivity, Observer {
            dismissProgressDialog()
            startActivity(MainNormalActivity::class.java)
            finish()
        })

        errMsg.observe(this@LoginActivity, Observer {
            dismissProgressDialog()
            it?.run { toast(it) }
        })
    }
复制代码

最后就是 LoginActivity 代表的 View 层了,View 层和 ViewModel 层通过 LiveData 进行绑定,上面代码中的 mLoginUsererrMsg 就是 ViewModel 层 “发射” 过来的数据。关于数据绑定,我并没有使用 DataBinding,这个纯粹是个人喜好了,我只是不喜欢 DataBinding 带来的代码不易读。

相对 Mvp 繁多的接口来说,个人感觉 Mvvm 的数据流更加清晰。搭配 Kotlin 和协程的使用,进一步简化代码。下面是一些项目截图:

项目地址点这个: 传送门,记得切换到 mvvm-kotlin 分支 ,欢迎带来 star 和 issue 丢过来 !

推荐一下我的另一个应用,Box —— 我的开发助手,添加了查看 logcat 的功能。

最后,也欢迎大家关注我的公众号 秉心说,话说公号关注人数还没掘金多,后续会继续 《走进 JDK 系列》以及 Android 相关知识的分享,欢迎大家扫码关注!有任何关于 Java/Android 的问题也可以加我的个人微信 bingxinshuo_

公众号后台回复 玩安卓 即可获取 Wanandroid 安装包。

文章首发微信公众号: 秉心说 , 专注 Java 、 Android 原创知识分享,LeetCode 题解,欢迎关注!

关注下面的标签,发现更多相似文章
评论