Wanandroid is a large open source android knowledge website, including the latest blog posts, the latest projects, common tools, public account articles collection and other functions, but also open source all API interface, convenient for everyone to build their own Wanandroid client. Java, Kotlin, Flutter, Mvp, MVMM, all kinds of Clients are available on Github, but I haven’t seen the Kotlin+MVVM+LiveData+ coroutine version yet. Plus I am looking at MVVM and LiveData recently, I started to transform the Mvp version of Wanandroid I wrote before into MVVM, the project address. Note that the Mater branch is an Mvp version that has been in disrepair and is not guaranteed to work. The MVM-Kotlin branch is the latest code.

About MVVM, we should also be more familiar with the previous MVVM classic architecture diagram:

Model-view-viewmodel, View refers to the green Activity/Fragment, mainly responsible for interface display, not responsible for any business logic and data processing. Model refers to the part of Repository that is responsible for fetching data and organizing local databases or remote servers. The ViewModel refers to the blue part of the figure, which is responsible for business logic and data processing. It does not hold the View layer reference itself and sends data to the View layer through LiveData. Repository consolidates data entry, whether it comes from a database or a server, and packages it to the ViewModel. I did not use a database in my project, but a cache instead.

In addition to MMVM, I used coroutines instead of RxJava. Regardless of the relative merits of coroutines and RxJava, it’s just that if you’re used to RxJava, coroutines really do give you a fresh feel for writing asynchronous code in a synchronous manner. There is no concept of coroutines in Java. Kotlin implements coroutines at compile time, via a state machine-like implementation. Coroutines can be considered lightweight threads, without the performance penalty of context switching, and theoretically more efficient than threads.

Take a look at the data flow using the login page LoginActivity as an example.

Model

@POST("/user/login")
fun login(@Field("username") userName: String, @Field("password") passWord: String): Deferred<WanResponse<User>>
Copy the code

This is the login Api.

class LoginRepository : BaseRepository(a){

    suspend fun login(userName: String, passWord: String): WanResponse<User> {
        return apiCall { WanRetrofitClient.service.login(userName, passWord).await() }
    }
    
}
Copy the code

LoginRepository defines the specific logon logic and calls the logon interface through Retrofit to return WanResponse

. Note that you want to use it in coroutines, so you define it as the suspend method.

ViewModel

class LoginViewModel : BaseViewModel(a){
    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 })
        }
    }
}
Copy the code

LoginViewModel holds LoginRepository and performs specific login logic through it, which is performed using coroutines. The return result is handled by the executeResponse() method, which I wrapped myself:

suspend fun executeResponse(response: WanResponse<Any>, successBlock: suspend CoroutineScope.() -> Unit,
                                errorBlock: suspend CoroutineScope.(a) -> Unit) {
        coroutineScope {
            if (response.errorCode == -1) errorBlock()
            else successBlock()
        }
    }
Copy the code

Kotlin has some functional programming language features that make development easier. ExecuteResponse () provides uniform response error handling.

View

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

        errMsg.observe(this@LoginActivity, Observer { dismissProgressDialog() it? .run { toast(it) } }) }Copy the code

Finally, there is the View layer represented by LoginActivity. The View layer and ViewModel layer are bound by LiveData. MLoginUser and errMsg in the above code are the data “emitted” from the ViewModel layer. For DataBinding, I don’t use DataBinding, this is purely a personal preference, I just don’t like the fact that DataBinding makes code hard to read.

Compared to Mvp’s various interfaces, I feel that Mvvm’s data flow is much clearer. With the use of Kotlin and coroutines, the code is further simplified. Here are some screenshots of the project:

Project address point this: portal, remember to switch to mVVM-Kotlin branch, welcome to bring star and issue drop in!

One of my other apps, Box, my developer assistant, added the ability to view LogCat.

Finally, also welcome everyone to pay attention to my public number bingxin said, the number of public attention has not nuggets, the follow-up will continue to “into the JDK series” and Android related knowledge sharing, welcome everyone to scan code attention! Any questions about Java/Android can also be added to my personal wechat.