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
}
在特定情况下,我对 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
}