MVVM

Github:github.com/wfqdroid/mv…

That technology used: LiveData, ViewModel, kotlin, kotlin coroutines, DataBinding, etc

A brief introduction to the above

  • LiveData: For responsive programming, we all know how powerful RxJava is, but LiveData, by contrast, doesn’t care about the life cycle. The application will send data to the page simultaneously while the page is “alive”, and the data will interrupt the response when the page is not visible or destroyed. Refer to the project code for specific use
  • ViewModel’s official description is that it holds the data needed for the page and does not destroy it when the phone rotates. It is also the VM layer of the MVVM architecture
  • Needless to say, almost everyone who does Android is learning. After Kotlin 1.3, the coroutine is stable and we can use it with confidence. With coroutines, we almost don’t need to worry about threads, which are lighter than threads, and with LiveData, when the network responds to our data, we don’t need to use Handler for thread switching, and we don’t need to use RxJava operators for thread switching. Our previous development is used to interface callback data, whereas kotlin coroutine can use synchronous way to do asynchronous operations, the code is simple and efficient
  • DataBinding talks about MVVM. A lot of people can’t live without DataBinding. MVVM is an idea, and Databing is just a tool Google gave us for reactive programming, bidirectional binding.

Here is an introduction to the architecture of the project, starting with our commonly used Activity

UI -> ViewModel -> Repository -> NetWork/Dao arrows are unidirectional, that is, ViewModel holds a reference to Repository and vice versa, otherwise memory leaks are likely. Network requests are made using Retrofit, the database Dao layer is omitted here, I am too busy recently, I will add it when I have time. The ViewModel holds a reference to the Repository, that is, it can hold data from the Repository itself and then feed it to the UI layer. That’s the general process. Let’s talk about the details of the project:

Let’s start with a question, okay? In the project, we use coroutines. How do we cancel requests when the page is destroyed? Here we use the viewModelScope.launch that comes with ViewModel, which will automatically cancel the request when the page is destroyed, but we have to use AndroidX. We can write a BaseViewModel

open class BaseViewModel:ViewModel() {
    fun<T> launch(block: suspend () -> Unit, error: suspend (Throwable) -> Unit, liveData: StateLiveData<T>, isShowLoading:Boolean = true) = viewModelScope.launch {
        try {
            if(isShowLoading){
                liveData.postLoading()
            }
            block()
        } catch (e: Throwable) {
            liveData.postError()
            error(e)
        }finally {
            liveData.postSuccess()
        }
    }
}

class ArticleViewModel(private val respository: ArticleRepository) : BaseViewModel() {
    val data = StateLiveData<List<Data>>()

    fun getArticle() { launch({ respository.getArticle()? .let { data.postValueAndSuccess(it) } }, { }, data) } }Copy the code

StateLiveData is a state-by-state LiveData, so we can send loading and other interface interactions directly in the BaseViewModel launch

class StateLiveData<T> : MutableLiveData<T>() {

    enum class State {
        Idle, Loading, Success, Error
    }

    val state = MutableLiveData<State>()

    init {
        initState()
    }

    fun postValueAndSuccess(value: T) {
        super.postValue(value)
        postSuccess()
    }

    private fun initState() {
        state.postValue(State.Idle)
    }

    fun postLoading() {
        state.postValue(State.Loading)
    }

    fun postSuccess() {
        state.postValue(State.Success)
    }

    fun postError() {
        state.postValue(State.Error)
    }
}
Copy the code

How can we do different things based on the status code of the server? Here we extend an await function on Retrofit’s Call object, so that our module’s NetWork directly inherits BaseNetWork

open class BaseNetwork {

    suspend fun <T> Call<T>.await(): T {
        return suspendCoroutine {
            enqueue(object : Callback<T> {
                override fun onFailure(call: Call<T>, t: Throwable) {
                    it.resumeWithException(t)
                }

                override fun onResponse(call: Call<T>, response: Response<T>) {
                    if (response.isSuccessful) {
                        val body = response.body()
                        if(body ! = null) { body as BaseRes<T>if (body.errorCode == 0)
                                it.resume(body)
                            else if(body.errorCode == 100){// Say login timeout, etc.}else{
                                it.resumeWithException(RuntimeException(body.errorMsg))
                            }
                        } else {
                            it.resumeWithException(RuntimeException("response body is null"))}}else{}}})}}Copy the code

Github:github.com/wfqdroid/mv…

Reference: juejin. Cn/post / 684490… Blog.csdn.net/guolin_blog… Developer.android.com/topic/libra…