Android - 如何在重新创建片段 / activity 时省略 liveData 的最后一个值? [解决了]

Android - How to omit the last value of a liveData when the fragment / activity is recreated? [Solved]

在特定情况下,我对 liveData 有疑问。当来自 http 服务的响应是 Code = 2 时,这意味着会话令牌已过期。在那种情况下,我导航到 LoginFragment 以再次登录用户。如果用户登录,那么我 return 到之前的片段,当我开始观察 onViewCreated 函数中的 liveData 时,它给我它的最后一个值是:代码 = 2,因此应用程序导航回登录,这是错误的。

我有一个密封的 Class:

sealed class Resource<T>(
    var data: T? = null,
    val message: String? = null,
    val Code: Int? = null

) {
    class Success<T>(data: T?) : Resource<T>(data)
    class Error<T>(message: String, code: Int? = null) : Resource<T>(message = message, Code = code)
    class Loading<T> : Resource<T>()
}

这是 ViewModel 上的代码:

val mLiveData: MutableLiveData<Resource<Data>> = MutableLiveData()

fun getData() {
    viewModelScope.launch {
        mLiveData.postValue(Resource.Loading())
        try {
            if (app.hasInternetConnection()) {
                // get Data From API
                val response = repository.getData()
                if(response.isSuccessful){
                    mLiveData.postValue(Resource.Success(parseSuccessResponse(response.body())))
                } else {
                    mLiveData.postValue(Resource.Error(parseErrorResponse(response.body())))
                }
            } else {
                mLiveData.postValue(Resource.Error("Connection Fail"))
            }
        } catch (t: Throwable) {
            when (t) {
                is IOException -> mLiveData.postValue(Resource.Error("Connection Fail"))
                else -> mLiveData.postValue(Resource.Error("Convertion Fail"))
            }
        }
    }
}

这是片段上的代码,observeForData()onViewCreated函数中被调用:

private fun observeForData() {
    mLiveData.getData.observe(viewLifecycleOwner, Observer { response ->
        when (response) {
            is Resource.Success -> {
                isLoading = false
                updateUI(response.data)
            }
            is Resource.Error -> {
                isLoading = false
                if (response.Code == 2) {
                    // Token Expired
                    navigateToLogin()
                } else {
                    showErrorMessage(response.message)
                }
            }
            is Resource.Loading -> {
                isLoading = true
            }
        }
    })
}

我该如何解决这个问题? 当片段在导航到 LoginFragment 时被销毁时,有没有办法从 liveData 中删除最后一个值或状态?

谢谢。

要更改实时数据的最后更新值,您可以在 onDestroy() 时将“资源”class 设置为默认空值。

onDestroy(){
//in java ,it will be new Resource instance
Resource resourceWithNull=new Resource();

mLiveData.setValue(resourceWithNull);
}

当您重新启动片段时,实时数据将发出具有空值的资源作为响应。

然后你可以在观察者中编写你的代码

if(response.data!=null)
{
//your code

}

一个经常被推荐的解决方案是 SingleLiveEvent,这是一个简单的 class 您可以复制粘贴到您的项目中。

对于框架解决方案,我建议使用 SharedFlow。一些 Android 开发人员建议无论如何从 LiveData 切换到 Flows 以更好地将数据与视图分离。如果你给 SharedFlow 一个重播值 0,新的 Activity 和观察它的 Fragments 将不会得到以前的值,只有新发布的值。

示例未经测试的 ViewModel 代码:

val dataFlow: Flow<Resource<Data>> = MutableSharedFlow(replay = 0)

init {
    viewModelScope.launch {
        // Same as your code, but replace mLiveData.postValue with dataFlow.emit
    }
}

在片段中:

private fun observeForData() {
    isLoading = true
    lifecycleScope.launch {
        mLiveData.dataFlow
            .flowWithLifecycle(this, Lifecycle.State.STARTED)
            .collect { onDataResourceUpdate(it) }
    }
}

// (Broken out into function to reduce nesting)
private fun onDataResourceUpdate(resource: Resource): Unit = when(resource) {
    is Resource.Success -> {
        isLoading = false
        updateUI(response.data)
    }
    is Resource.Error -> {
        isLoading = false
        if (response.Code == 2) {
            // Token Expired
            navigateToLogin()
        } else {
            showErrorMessage(response.message)
        }
    }
    is Resource.Loading -> isLoading = true
}