Android Jetpack 庫架構組件 ViewModel+LiveData 基礎使用

ViewModel 是什麼

ViewModel 類旨在以注重生命週期的方式 存儲和管理 界面相關的數據。ViewModel 類讓數據可在發生屏幕旋轉等配置更改後繼續留存。

爲什麼ViewModel 類中的數據可在發生屏幕旋轉等配置更改後繼續留存?
因爲ViewModel的生命週期長於組件(Activity/Fragment)的生命週期

下圖是左側給出了Activity 經歷屏幕旋轉而後結束的過程,所處的各種生命週期狀態。而右側是ViewModel的生命週期。
從中可以看出ViewModel的生命週期是長於組件(Activity)的生命週期。
在這裏插入圖片描述
屏幕旋轉後數據得以保存僅是ViewModel的其中一個優勢,生命週期比組件長的優勢還可以:

  • 減少資源浪費:避免因配置更改而重新創建對象
  • 避免內存泄露:界面控制器經常需要做異步調用,而異步調用需要在一段時間後才返回結果。如果界面關閉之後數據在還沒返回,且其中的一些引用還存在,那麼內存泄露就不可避免了。通常我們會在頁面關閉的時候手動清除引用。在生命週期中我們可以看到ViewModel還有一個方法onCleared(),我們可在該方法手動清除引用。
    在這裏插入圖片描述
    谷歌在生命感知組件的最佳做法中,推薦我們使用ViewModel+LiveData的組合。
    在這裏插入圖片描述
    既然如此,那我們先來了解何爲LiveData

LiveData 是什麼

LiveData 是一種 可觀察的數據存儲器類。與常規的可觀察類不同,LiveData 具有生命週期感知能力,所以它能在不同生命週期處理不同的操作。

由於LiveData是可觀察的數據存儲類且有生命週期感知能力,因此它有具備如下優勢:

  • 確保界面符合數據狀態:LiveData 遵循觀察者模式。當生命週期改變數據也會刷新

  • 不會發生內存泄露:觀察者會綁定到 Lifecycle 對象,並在其關聯的生命週期遭到銷燬後進行自我清理。

  • 不會因 Activity 停止而導致崩潰:如果觀察者的生命週期處於非活躍狀態,則它不會接收任何 LiveData 事件。

  • 不再需要手動處理生命週期:LiveData 將自動管理所有這些操作

  • 數據始終保持最新狀態:如果生命週期變爲非活躍狀態,它會在再次變爲活躍狀態時接收最新的數據。

  • 適當的配置更改:如果由於配置更改(如設備旋轉)而重新創建了 Activity 或 Fragment,它會立即接收最新的可用數據。

  • 共享數據:數據存儲在ViewModel中,需要相應資源的任何(Activity/Fragment)等觀察者只需觀察 LiveData 對象

ViewModel+LiveData 使用步驟

示例:
(1)實現倒計時功能
(2)ViewModel傳參獲取數據庫的數據展示

  1. Module -> build.gradle的引入

版本依賴查看:https://developer.android.google.cn/jetpack/androidx/releases/lifecycle#declaring_dependencies

	def lifecycle_version = "2.2.0"

	// ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
	// 還有一些可選項,可查看上面的版本依賴鏈接
  1. 創建Viewmodel(推薦創建BaseViewModel,之後再創建具體的ViewModel繼承BaseViewModel)
/**
 * ViewModel 基類
 */
open class BaseViewModel : ViewModel() {
    override fun onCleared() {
        super.onCleared()
        Log.e("CommonViewModel","onCleared")
    }
}

(示例1:倒計時功能)

class MineViewModel : BaseViewModel() {
    private val periodTime = 1000L
    private val mElapsedRealTime = MutableLiveData<Long>()
    private var mInitialTime: Long = 0

    // Create a LiveData with a Long
    val countDownTime: MutableLiveData<Long> by lazy {
        mElapsedRealTime
    }
    /**
     * 開啓倒計時
     */
    fun getTime(){
        mInitialTime = SystemClock.elapsedRealtime()
        val timer = Timer()
        val timeTask = object : TimerTask() {
            override fun run() {
                val newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000
                if (newValue < 10){
                    mElapsedRealTime.postValue(newValue)
                }else{
                    timer.cancel()
                }
            }
        }
        timer.scheduleAtFixedRate(timeTask,periodTime,periodTime)
    }
}


(示例2:ViewModel傳參獲取數據庫的數據展示)

class HomeViewModel(context: Context): BaseViewModel() {
    companion object {
        private const val PAGE_SIZE = 15
        private const val ENABLE_PLACEHOLDERS = false
    }
    val mContext = context
    val dao = StudentDb.get(mContext).studentDao()

    val allStudents = LivePagedListBuilder(dao.getAllStudent(), PagedList.Config.Builder()
        .setPageSize(PAGE_SIZE)                         //配置分頁加載的數量
        .setEnablePlaceholders(ENABLE_PLACEHOLDERS)     //配置是否啓動PlaceHolders
        .setInitialLoadSizeHint(PAGE_SIZE)              //初始化加載的數量
        .build()).build()
}
  1. 創建 Activity(推薦Fragment)中使用
(示例1:倒計時功能)
class MineFragment : BaseFragment(),MineContract.View {

    private lateinit var viewModel: MineViewModel

    override fun initData() {
    	// 創建實例
        viewModel = ViewModelProviders.of(this).get(MineViewModel::class.java)
        // 觀察數據
        viewModel.countDownTime.observe(this, Observer<Long> { aLong ->
            //Update UI
            tv_name.text = "time = " + aLong!!
        })
    }
    override fun initView() {
        // 訂閱事件
        lifecycle.addObserver(minePresenter)
        // 獲取數據
        btn_get_data.setOnClickListener {
            viewModel.getTime()
        }
    }
      // 省略部分代碼,具體可看下方源碼鏈接
}
    
(示例2:ViewModel傳參獲取數據庫的數據展示)
class HomeJetpackFragment : BaseFragment() {
    private val TAG = HomeJetpackFragment::class.java.simpleName

    private val viewModel by lazy(LazyThreadSafetyMode.NONE) {
        ViewModelProviders.of(this, object : ViewModelProvider.Factory {
            // 傳遞 context
            override fun <T : ViewModel?> create(modelClass: Class<T>): T = HomeViewModel(
                BaseApplication.context as Application
            ) as T
        }).get(HomeViewModel::class.java)
    }

    override fun initView() {
        val adapter = StudentAdapter()
        val layoutManager = LinearLayoutManager(activity)
        rv_list.layoutManager = layoutManager
        rv_list.adapter = adapter
        // 將數據的變化反映到UI上
        viewModel.allStudents.observe(this, Observer {
            adapter.submitList(it)
        })
    }
    // 省略部分代碼,具體可看下方源碼鏈接
}

Tip:
initDatainitView 執行順序如下:
在這裏插入圖片描述

詳細使用代碼請參見:YGragon/FrameDemo

總結

使用ViewModel+LiveData的模式,可以很好的在組件的不同生命週期處理數據。這是由於LiveData是生命週期感知型類所帶來的特性。ViewModel中專注於處理數據,和UI組件徹底解耦。

而且在使用AndroidStudio創建Fragment的時候提供了創建ViewModel選項,極大的方便了開發。
在這裏插入圖片描述

參考

上車

佛系原創號主
在這裏插入圖片描述

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