MVVM之Architecture Components

Android Architecture Components 是谷歌在Google I/O 2017發佈一套幫助開發者解決Android 架構設計的方案。裏面包含了兩大塊內容:

1.生命週期相關的 Lifecycle-aware Components

2.數據庫解決方案 Room

 

本文主要爲如何使用Lifecycle && LiveData && ViewModel方式搭建MVVM

一、Lifecycle

對整個Activity生命週期進行監聽需要繼承自LifecycleObserver接口

class MainLifecycleObserver : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun onCreate(owner: LifecycleOwner){

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onStart(owner: LifecycleOwner) {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onResume(owner: LifecycleOwner) {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun onPause(owner: LifecycleOwner) {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onStop(owner: LifecycleOwner) {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy(owner: LifecycleOwner) {

    }

    /**
     * 所有生命週期
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    fun onAny(owner: LifecycleOwner, event: Lifecycle.Event) {

    }

}

在activity或者fragmetn中註冊

lifecycle.addObserver(MainLifecycleObserver())

二、LiveData+ViewModel

1.LiveData

LiveData 是一種持有可被觀察數據的類。和其他可被觀察的類不同的是,LiveData 是有生命週期感知能力的,這意味着它可以在 activities, fragments 或者 services 生命週期是活躍狀態時更新這些組件。那麼什麼是活躍狀態呢?lifecycle 中提到的 STARTED 和 RESUMED就是活躍狀態,只有在這兩個狀態下LiveData 是會通知數據變化的。

使用 LiveData,必須配合實現了 LifecycleOwner 的對象。當對應的生命週期對象 DESTORY 時,才能移除觀察者。對於 activities 和 fragments 非常重要,因爲他們可以在生命週期結束的時候立刻解除對數據的訂閱,從而避免內存泄漏等問題。

 

2.ViewModel

ViewModel設計的目的就是存放和處理和UI相關的數據,並且這些數據不受配置變化(Configuration Changes,例如:旋轉屏幕,組件被系統回收)的影響。

注意:ViewModel 絕對不可以引用 view、Lifycycle 或者任何持有 context 的類。

2.1viewModelScope

AndroidX Lifecycle v2.1.0 在 ViewModel 中引入 viewModelScope,當 ViewModel 被銷燬時它會自動取消協程任務

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-rc02'

使用示例

class MainViewModel : ViewModel() {

    fun launchDataLoad() {
        viewModelScope.launch {
            sortList()
        }
    }

    suspend fun sortList() = withContext(Dispatchers.Default) {
        
    }
}

 

3.示例

MutableLiveData + ViewModel基類

abstract class MutableViewModel<T> : ViewModel() {

    private var bean = MutableLiveData<T>()

    fun getLiveData(): MutableLiveData<T> {
        return bean
    }

    fun setLiveData(t: T) {
        bean.value = t
    }
    
    /**
     * 異步使用
     */
    fun postLiveData(t: T) {
        bean.postValue(t)
    }
}

單個數據ViewModel

class MainViewModel : BaseViewModel<String>() {}

使用ViewModelProvider(this).get(MainViewModel::class.java)需要引入implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-rc02',當然你也可以自定義Factory

class MainActivity : AppCompatActivity() {

    private var i = 0

    private lateinit var mainViewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_architecture)
        //綁定組件的聲明週期
        lifecycle.addObserver(MainLifecycleObserver())

        //獲取ViewModel,implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-rc02'
        mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        //監聽
        mainViewModel.getLiveData().observe(this, Observer {
            textView.text = it
        })

        button.setOnClickListener {
            mainViewModel.setLiveData("${i++}")
        }
    }

}

多個數據ViewModel

data class MainBean(var msg:String,var frequency:Int) {}

class MainViewModel : BaseViewModel<MainBean>() {}

使用MutableLiveData,那麼整個實體類或者數據類型變化後才通知更新,不會因爲某個字段變化而更新

//監聽
mainViewModel.getLiveData().observe(this, Observer {
    textView.text = it.msg
})
button.setOnClickListener {
    mainViewModel.setLiveData(MainBean("第${i++}",i++))
}

 

三、MVVM

上面簡單介紹了一下Architecture Components模式的MVVM,接下來是登陸的使用示例

class LoginActivity : BaseActivity<LoginViewModel>(){

    override fun getLayoutResID(): Int {
        return R.layout.activity_login
    }

    override fun addObserver(): LifecycleObserver? {
        return null
    }

    override fun getViewModel(): Class<LoginViewModel> {
        return LoginViewModel::class.java
    }

    override fun initView() {
        viewModel.userAccount.observe(this, Observer {
            viewModel.getCode()
        })
        viewModel.code.observe(this, Observer {
            if (viewModel.login()){
                startActivity(PermissionsActivity::class.java)
            }
        })

    }

    override fun initEvent() {
        setEditTextMutableLiveData(et_userAccount,viewModel.userAccount)
        setEditTextMutableLiveData(et_code,viewModel.code)
    }

}

爲了簡化代碼封裝了一個MVVM之BaseActivity,使用方式很簡單,傳入一個佈局id和一個ViewModel,LifecycleObserver是爲了監聽組件的生命週期,可以傳null

setEditTextMutableLiveData是封裝的一個方法,目的是EditText的值動態賦予MutableLiveData

    /**
     * EditText的值動態賦予MutableLiveData
     */
    fun setEditTextMutableLiveData(et: EditText, liveData: MutableLiveData<String>) {
        et.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                liveData.value = s.toString()
            }
        })
    }

接下來看ViewModel的代碼

class LoginViewModel : ViewModel() {

    //用戶賬號
    var userAccount = MutableLiveData<String>()
    //驗證碼
    var code = MutableLiveData<String>()

    init {
        userAccount.value = ""
        code.value = ""
    }

    /**
     * 獲取驗證碼
     */
    fun getCode(){
        if (userAccount.value.equals("13800000001")){
            showLongToast("獲取到驗證碼")
        }
    }

    /**
     * 登陸
     */
    fun login():Boolean{
        if (userAccount.value.equals("13800000001") && code.value.equals("123456")){
            return true
        }
        return false
    }
}

最後是測試代碼

class LoginActivityTest{
    private lateinit var activity: LoginActivity

    @get:Rule
    var mActivityRule = ActivityScenarioRule(LoginActivity::class.java)

    //在Test前初始化
    @Before
    fun init() {
        mActivityRule.scenario.onActivity {
            activity = it
        }
    }

    @Test
    fun login(){
        val et_userAccount = Espresso.onView(ViewMatchers.withId(R.id.et_userAccount))
        val et_code = Espresso.onView(ViewMatchers.withId(R.id.et_code))

        et_userAccount.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
        et_code.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))

        //typeText是在原先的基礎上輸入文本 ,replaceText是替換文本
        et_userAccount.perform(ViewActions.typeText("1"), ViewActions.closeSoftKeyboard())
        Thread.sleep(1000)

        et_userAccount.perform(ViewActions.typeText("3"), ViewActions.closeSoftKeyboard())
        Thread.sleep(1000)

        et_userAccount.perform(ViewActions.typeText("13800000001"), ViewActions.closeSoftKeyboard())
        Thread.sleep(1000)

        et_userAccount.perform(ViewActions.replaceText("13800000001"), ViewActions.closeSoftKeyboard())
        Thread.sleep(1000)

        et_code.perform(ViewActions.replaceText("12345789"), ViewActions.closeSoftKeyboard())
        Thread.sleep(1000)

        et_code.perform(ViewActions.replaceText("123456"), ViewActions.closeSoftKeyboard())
        Thread.sleep(1000)
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章