iT邦幫忙

2021 iThome 鐵人賽

DAY 14
0
Mobile Development

Andoroid - Kotlin筆記 (新)系列 第 14

[Day14] Android - Kotlin筆記:LiveData在fragment重建時會重新呼叫兩次的解決方法

原因:

Activity中綁定一個ViewModel
Fragments間共用這個ViewModel
使用Naviagtion來切換Fragment間的頁面時,
每次切換都會重走一次Fragment的生命週期,
也就是處於“STARTED 或 RESUMED 狀態”,
導致從其他頁面切換回來之後,
會觸發LiveData的數據回調(observer被觸發)。


舉例:

Activity中保存一個ViewModel
使用NavigationFragment實現頁面的切換,
AFragment獲取ActivityViewModel並註冊LiveDataobserver
此時使用setValueAFragment收到一次LiveData數據,
然後切換到BFragmentAFragment被銷毀),
之後切回AFragment,會發現重新註冊LiveData數據,
AFragment再次收到LiveData數據。


圖源:https://bbs.huaweicloud.com/blogs/detail/256402


解決方法:

新增一個Event class

open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}


使用方法

Event包裝你原本的data。

  • ViewModel
    val selectedDate: LiveData<Event<String?>>
        get() = _selectedDate

    private val _selectedDate = MutableLiveData<Event<String?>>()
    fun setSelectedDate(date: String?) {
        _selectedDate.value = Event(date)
    }
  • Fragment
    viewModel.selectedDate.observe(viewLifecycleOwner, {
            it.getContentIfNotHandled()?.apply { result ->
                tv_selectedDate.text = result
            }
        })    

透過getContentIfNotHandled取得Event中原本的LiveData
能夠抓取正確值,
而不會一直被Fragment間切換影響而重複調用。


參考:


上一篇
[Day13] Android - Kotlin筆記:Parcelable & Serializable 與 SafeArgs的傳遞
下一篇
[Day15] Andoroid - Kotlin筆記: MVVM簡介
系列文
Andoroid - Kotlin筆記 (新)18

尚未有邦友留言

立即登入留言