Try   HackMD

Android ViewModel和LiveData的基本用法

先來講一點廢話,講一下為啥我會開始用ViewModel。

以前我寫single activity application還沒有用navigation的時候,都是自己寫個listener的interface用observer pattern來實現activity和fragment的溝通。我記得這其實是原本官方建議的方式,但我現在找不到了,記得好像蠻硬性地規定要有個callback method叫做onFragmentInteraction(uri: Uri)來做activity跟fragment的溝通。observer pattern的方式我用了很久,直到後來因為覺得各個fragment之間的轉換用寫的實在是有點亂,就用了navigation,沒想到一試成主顧,真的太好用而且很簡單(我就不特別筆記navigation了因為官方教學十分清楚,照著做就好),但用了navigation後,整個MainActivity的程式架構變了很多,也讓我重新思考activity、fragment和service之間到底要怎麼溝通比較好,於是讓我有這個機會試著用用看ViewModel和LiveData。然而它們就跟navigation一樣,太好用又太簡單了,用完之後回不去了。

所以這篇筆記所寫的可能跟一般大家常用ViewModel和LiveData的理由不同,一般是拿來存取UI元件的資料用的,而我這邊想講一下怎麼用ViewModel和LiveData來取代Observer pattern來做activity和fragment之間(fragment和fragment之間也可以)的溝通,而且相較之下是一個更好用更簡單的方案。

基本介紹

  • ViewModel ViewModel是一個用來把資料跟activity、fragment等view類型的元件分離的東西,而且它的lifecycle比activit和fragment的還要長,是一個很適合放資料的地方。它的存在跟一個叫做MVVM的design pattern有很大的關係。主要就是view跟model之間,有了這個viewmodel來讓資料的取得與更新有一個獨立的地方來專門處理。

  • LiveData LiveData其實就是一個放資料的容器,讓資料在有更新的時候可以通知資料的觀察者們(沒錯這邊是用observer pattern),常放在ViewModel裡面搭配使用。

簡單了解以上兩個東西後,就會可以知道:
首先,資料取得與更新的相關程式(可能要透過http get或是跟database拿之類的)會寫在ViewModel中,每個資料都有自己的ViewModel,相當清楚,讓程式碼更好維護。再來,當我們把資料放進LiveData後,該資料的觀察者們便可以隨時掌握資料的最新狀態,而一個ViewModel裡面可能不只有一個LiveData。最後,ViewModel的lifecycle很長,所以資料不會因為rotate或是哪個fragment結束後就消失,可以很安心地把資料的處理交給ViewModel。

相較於observer pattern是1對n的關係,ViewModel和LiveData的搭配是個n對m的關係,所以如果是有雙向溝通的需求的話,就不需要寫兩個interface然後互相addListener這麼麻煩了。

準備一個ViewModel

就是準備一個實作ViewModel的class,以下程式碼直接來:

import ...

class MsgViewModel: ViewModel() {
    val msg = MutableLiveData<Uri>()
}

這邊我也不寫什麼getter跟setter了,要用就直接拿msg來用,比較簡單。而這裡會用Uri其實是因為當初onFragmentInteraction(uri: Uri)就是用Uri,其實還蠻好用的,因為可以把訊息變得很結構化,而且比自己定義一個String的格式還方便多了因為它有內建的method可以讀出Uri中各個區段的資料。

Activity中的讀與寫

class MainActivity : AppCompatActivity(){

    private val model: MsgViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // observe
        model.msg.observe(this, Observer {
            println(it)
        })
        // set value
        model.msg.value = Uri.parse("msg://main_activity/test")
    }
    
    ...
}

其實重點就是一開始要用by viewModels()來取得這個VewModel,接著寫一下當你這個觀察者收到資料的更新的通知時要做什麼處理,以及放一個Uri到LiveData中推送給所有觀察者。
這邊直得注意的是,set value那邊,你放一個資料到LiveData後,你自己在observe那邊也會收到。
通常我會習慣在Uri.authority放來源的名字,例如"test_fragment",然後在observe這邊放一個when(it.authority)去依據不同的來源做相對應的處理。

Fragment中的讀與寫

class TestFragment : Fragment() {

    private val model: MsgViewModel by activityViewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        model.msg.observe(this, Observer {
            println(it)
        })
    }
    
    ...
}

這邊跟Activity的幾乎一模一樣,唯一不同的就是要用by activityViewModels()來取得ViewModel,剩下都一樣我就不說了。

好的以上就是今天的筆記,收工!