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

秉心說TM發表於2019-04-15

Wanandroid 是鴻洋鴻大大的安卓開源知識網站,包含最新博文,最新專案,常用工具,公眾號文章收錄等等功能,同時也開源了所有 API 介面,方便大家打造自己的 Wanandroid 客戶端。Github 上關於 Wanandroid 的客戶端也層出不窮,Java的,Kotlin 的,Flutter 的,Mvp 的,MVMM 的,各種各樣,但是還沒看到 Kotlin+MVVM+LiveData+協程 版本的,加上最近正在看 MVVM 和 LiveData,就著手把我之前寫的 Mvp 版本的 Wanandroid 改造成 MVVM,專案地址 。注意,mater 分支是年久失修的 Mvp 版本,不一定保證可以執行。mvvm-kotlin 分支是最新程式碼。

關於 MVVM,大家應該也比較熟悉了,上一張 MVVM 經典架構圖:

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

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 和協程的使用,進一步簡化程式碼。下面是一些專案截圖:

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

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

專案地址點這個: 傳送門,記得切換到 mvvm-kotlin 分支 ,歡迎帶來 star 和 issue 丟過來 !

推薦一下我的另一個應用,Box —— 我的開發助手,新增了檢視 logcat 的功能。

最後,也歡迎大家關注我的公眾號 秉心說,話說公號關注人數還沒掘金多,後續會繼續 《走進 JDK 系列》以及 Android 相關知識的分享,歡迎大家掃碼關注!有任何關於 Java/Android 的問題也可以加我的個人微信 bingxinshuo_

文章首發微信公眾號: 秉心說 , 專注 Java 、 Android 原創知識分享,LeetCode 題解,歡迎關注!

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

相關文章