LiveData 在第一次回调后删除 Observer
LiveData remove Observer after first callback
如何在收到第一个结果后删除观察者?下面是我尝试过的两种代码方式,但即使我已经删除了观察者,它们都继续接收更新。
Observer observer = new Observer<DownloadItem>() {
@Override
public void onChanged(@Nullable DownloadItem downloadItem) {
if(downloadItem!= null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObservers((AppCompatActivity)context);
}
};
model.getDownloadByContentId(contentId).observeForever(observer);
model.getDownloadByContentId(contentId).observe((AppCompatActivity)context, downloadItem-> {
if(downloadItem!= null) {
this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObserver(downloadItem-> {});
} );
你的第一个不起作用,因为 observeForever()
没有绑定到任何 LifecycleOwner
。
你的第二个将不起作用,因为你没有将现有的注册观察者传递给 removeObserver()
。
您首先需要确定是否将 LiveData
与 LifecycleOwner
(您的 activity)一起使用。我的假设是您应该使用 LifecycleOwner
。在这种情况下,使用:
Observer observer = new Observer<DownloadItem>() {
@Override
public void onChanged(@Nullable DownloadItem downloadItem) {
if(downloadItem!= null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObservers((AppCompatActivity)context);
}
};
model.getDownloadByContentId(contentId).observe((AppCompatActivity)context, observer);
根据 CommonsWare 的回答,您可以简单地调用 removeObserver(this)
来仅删除此观察者,而不是调用 removeObservers()
这将删除所有附加到 LiveData 的观察者:
Observer observer = new Observer<DownloadItem>() {
@Override
public void onChanged(@Nullable DownloadItem downloadItem) {
if(downloadItem!= null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObserver(this);
}
};
model.getDownloadByContentId(contentId).observe((AppCompatActivity)context, observer);
注意:removeObserver(this)
中的,this
指的是观察者实例,这只在匿名内部class的情况下有效.如果您使用 lambda,则 this
将引用 activity 实例。
- LiveData class 有 2 种类似的方法来删除观察者。首先是命名,
removeObserver(@NonNull final Observer<T> observer)
(仔细看方法名,是单数),它接收你想从同一个LifecycleOwner的观察者列表中移除的观察者。
- 第二种方法是
removeObservers(@NonNull final LifecycleOwner owner)
(参见复数方法名称)。此方法接收 LifecycleOwner 本身并删除指定 LifecycleOwner 的所有观察者。
现在在你的情况下,你可以通过两种方式移除你的观察者(可能有很多方式),@ToniJoe 在之前的回答中告诉了你一种方式。
另一种方法是在您的 ViewModel 中有一个布尔值的 MutableLiveData,它在第一次被观察时存储 true 并且也只观察该 Livedata。因此,只要它变为真,您就会收到通知,并且您可以通过传递该特定观察者来移除您的观察者。
Kotlin 有一个更方便的扩展解决方案:
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}
此扩展允许我们这样做:
liveData.observeOnce(this, Observer<Password> {
if (it != null) {
// do something
}
})
因此,为了回答您最初的问题,我们可以这样做:
val livedata = model.getDownloadByContentId(contentId)
livedata.observeOnce((AppCompatActivity) context, Observer<T> {
if (it != null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
}
startDownload();
})
原文出处在这里:https://code.luasoftware.com/tutorials/android/android-livedata-observe-once-only-kotlin/
更新:@Hakem-Zaied 是对的,我们需要使用 observe
而不是 observeForever
。
我同意上面的 Vince,但我认为我们要么跳过 lifecycleOwner
并使用 observerForever
,如下所示:
fun <T> LiveData<T>.observeOnce(observer: Observer<T>) {
observeForever(object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}
或者,使用 lifecycleOwner
和 observe
如下:
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}
我喜欢 Vince and Hakem Zaied 的通用解决方案,但对我来说,lambda 版本似乎更好:
fun <T> LiveData<T>.observeOnce(observer: (T) -> Unit) {
observeForever(object: Observer<T> {
override fun onChanged(value: T) {
removeObserver(this)
observer(value)
}
})
}
fun <T> LiveData<T>.observeOnce(owner: LifecycleOwner, observer: (T) -> Unit) {
observe(owner, object: Observer<T> {
override fun onChanged(value: T) {
removeObserver(this)
observer(value)
}
})
}
所以你最终得到:
val livedata = model.getDownloadByContentId(contentId)
livedata.observeOnce((AppCompatActivity) context) {
if (it != null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists")
}
startDownload();
}
我觉得哪个更干净。
此外,removeObserver()
在观察者被调度时被调用 first-thing,这使得它更安全(即处理从用户的观察者代码中抛出的潜在运行时错误)。
@CommonsWare 和@Toni Joe 提出的解决方案并没有解决我的问题,因为我需要在我的 ViewModel 中收到来自 DAO 查询的第一个结果后删除观察者。但是,在 Livedata keeps observer after calling removeObserer 上找到的以下解决方案凭借我自己的一些直觉为我解决了这个问题。
过程如下,根据请求在您的 ViewModel 中创建一个变量,其中存储 LiveData,在进行空检查后,在 activity 中的创建观察者函数调用中检索它,并调用 remove在导入的 class 中调用 flushToDB 例程之前观察者函数。也就是说,我的 ViewModel 中的代码如下所示:
public class GameDataModel extends AndroidViewModel {
private LiveData<Integer> lastMatchNum = null;
.
.
.
private void initLastMatchNum(Integer player1ID, Integer player2ID) {
List<Integer> playerIDs = new ArrayList<>();
playerIDs.add(player1ID);
playerIDs.add(player2ID);
lastMatchNum = mRepository.getLastMatchNum(playerIDs);
}
public LiveData<Integer> getLastMatchNum(Integer player1ID, Integer player2ID) {
if (lastMatchNum == null) { initLastMatchNum(player1ID, player2ID); }
return lastMatchNum;
}
在上面,如果 ViewModel 的 LiveData 变量中没有数据,我调用 initLastMatchNum()
从视图模型中的函数检索数据。从 activity 调用的函数是 getLastMatchNum()
。此例程检索 ViewModel 中变量中的数据(通过 DAO 通过存储库检索)。
我的 Activity
中有以下代码
public class SomeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
.
.
.
setupLastMatchNumObserver();
.
.
.
}
private void setupLastMatchNumObserver() {
if (mGameDataViewModel.getLastMatchNum(Player1ID, Player2ID).hasObservers()) {
Log.v("Observers", "setupLastMatchNumObserver has observers...returning");
return;
}
Log.v("Setting up Observers", "running mGameDataViewModel.get...observer()");
mGameDataViewModel.getLastMatchNum(Player1ID, Player2ID).observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer MatchNumber) {
if (MatchNumber == null ) {
matchNumber = 1;
Log.v( "null MatchNumber", "matchNumber: " + matchNumber.toString());
}
else {
matchNumber = MatchNumber; matchNumber++;
Log.v( "matchNumber", "Incrementing match number: " + matchNumber.toString());
}
MatchNumberText.setText(matchNumber.toString());
}
});
}
private void removeObservers() {
final LiveData<Integer> observable = mGameDataViewModel.getLastMatchNum(Player1ID, Player2ID);
if (observable != null && observable.hasObservers()) {
Log.v("removeObserver", "Removing Observers");
observable.removeObservers(this);
}
}
上面发生的事情是 1.) 我在 activity 的 onCreate
方法中调用 setupLastMatchNumObserver()
例程来更新 class'变量 matchNum
。这会跟踪存储在数据库中的游戏中玩家之间的比赛号码。每组球员在数据库中都会有不同的比赛编号,这取决于他们彼此进行新比赛的频率。这个线程中的第一个解决方案对我来说似乎有点厌倦,因为在 onChanged
中调用 remove observers 对我来说似乎很奇怪,并且会在玩家每次移动的每次数据库刷新后不断更改 TextView
对象。所以 matchNumber
在每次移动后都会增加,因为在第一次移动后数据库中有一个新值(即一个 matchNumber++
值)并且 onChanged
一直被调用,因为 removeObservers
没有按预期工作。 setupLastMatchNumObserver()
检查是否有实时数据的观察者,如果有,则不会在每一轮实例化新调用。如您所见,我正在设置一个 TextView
对象来反映玩家的当前比赛号码。
下一部分是关于何时调用 removeObservers()
的小技巧。起初我想如果我在 onCreate
覆盖 activity 的 setupLastMatchNumObserver()
之后直接调用它,一切都会很好。但是它在观察者可以获取数据之前移除了观察者。我发现如果我在调用之前直接调用 removeObservers()
将 activity 中收集的新数据刷新到数据库(在整个 activity 的单独例程中),它就像一个魅力.即,
public void addListenerOnButton() {
.
.
.
@Override
public void onClick(View v) {
.
.
.
removeObservers();
updateMatchData(data);
}
}
我也在 activity 的其他地方以上述方式调用 removeObservers();
和 updateMatchData(data)
。美妙之处在于 removeObservers()
可以根据需要多次调用,因为如果没有观察者在场,就会检查 return 。
这是其他答案中建议的 observeOnce
方法的 Java 版本(util class 方法而不是 Kotlin 扩展函数):
public class LiveDataUtil {
public static <T> void observeOnce(final LiveData<T> liveData, final Observer<T> observer) {
liveData.observeForever(new Observer<T>() {
@Override
public void onChanged(T t) {
liveData.removeObserver(this);
observer.onChanged(t);
}
});
}
}
您不止一次创建实时数据实例 (model.getDownloadByContentId(contentId)),这就是问题所在。
试试这个:
LiveData myLiveData =model.getDownloadByContentId(contentId);
myLiveData.observe(getViewLifecycleOwner(), downloadItem-> {
if(downloadItem!= null) {
this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
myLiveData.removeObservers(getViewLifecycleOwner());
} );
Vince 和 Hakem Zaied 解决方案运行良好,但就我而言,我试图获取实时数据实例并更新本地数据库,但实时数据首先要从远程 API 更新,因此我得到一个 NullPointer,所以我切换到 observeForever 并且我能够在更新时获取数据,但现在我必须在获取数据后处理观察者,所以我修改了 Vince 解决方案以仅在以下情况下观察和发出数据实时数据包含数据。
fun <T> LiveData<T>.observeOnce(observer: (T) -> Unit) {
observeForever(object : Observer<T> {
override fun onChanged(value: T) {
//Resource is my data class response wrapper, with this i was able to
//only update the observer when the livedata had values
//the idea is to cast the value to the expected type and check for nulls
val resource = value as Resource<*>
if (resource.data != null) {
observer(value)
removeObserver(this)
}}
})
}
这是一个 androidx.lifecycle.Observer Java 示例:
Observer <? super List<MyEntity>> observer = new Observer<List<MyEntity>>() {
@Override
public void onChanged(List<MyEntity> myEntities) {
Log.d(TAG, "observer changed");
MySearchViewModel.getMyList().removeObserver(this);
}
};
MySearchViewModel.getMyList().observe(MainActivity.this, observer);
在我看来,Livedata 旨在持续接收即将到来的数据。如果你只是想让它只执行一次,比如从服务器请求数据来初始化 UI,我建议你这样设计你的代码:
1、将你的耗时方法定义为non-Livedata type inside Viewmodel。您不必在此过程中启动新线程。
2、在Activity中启动一个new Thread,在new Thread中,调用上面定义的方法,然后runOnUiThread()
编写使用请求数据的逻辑。通过这种方式,耗时的方法不会阻塞 UI 线程,而它会阻塞新线程,因此 runOnUiThread() 仅在成功接收到您请求的数据后运行。
因此,如果这是您想要的,请考虑更换 Livedata。
Java 版本的 observeOnce
方法已经被许多用户推荐。但是这里我们将在主要代码中实现。
首先,我们需要创建Util class方法
public class LiveDataUtil {
public static <T> void observeOnce(final LiveData<T> liveData, final Observer<T> observer) {
liveData.observeForever(new Observer<T>() {
@Override
public void onChanged(T t) {
liveData.removeObserver(this);
observer.onChanged(t);
}
});
}}
现在,我们需要在需要 ViewModel 的地方调用它 class。
LiveDataUtil.observeOnce(viewModel.getUserDetails(), response-> {
if(response.isSuccessful()){
//Do your task
}
}
就这些!
我阅读了一些文档并在观察者那里看到了 remove 方法,所以我得出了这个解决方案:
1:先声明观察者:
// observer for selecting chip in view
View actionView;
Observer selectChipFunction = (action) -> selectChip(actionView, action);
2:然后使用观察者:
// select an action if set before
if (sharedViewModel.getAction() != null)
sharedViewModel.getAction().observe(getViewLifecycleOwner(), selectChipFunction);
3:然后在selectChip观察者中去掉观察者:
/**
* select action chip
* @param actionView - view to use for selecting action chip
* @param actionObject - action chip to select
*/
private void selectChip(View actionView, Object actionObject)
{
// no need for observing when action is changed so remove.
sharedViewModel.getAction().removeObserver(selectChipFunction);
这种方式只触发一次,之后就被移除了。在我的例子中,我需要这个,因为我在 selectChipFunction 中设置了触发观察者的“动作”,如果我不这样做,你将以循环观察者触发结束。
这个怎么样:
fun <T> LiveData<T>.observeOnCondition(lifecycleOwner: LifecycleOwner,
observer: Observer<T>,
condition: () -> Boolean) {
observe(lifecycleOwner) { t ->
if (condition()) {
observer.onChanged(t)
}
}
}
如果您想在稍后阶段再次获取数据,您可以通过这种方式定义更通用的条件。
如何在收到第一个结果后删除观察者?下面是我尝试过的两种代码方式,但即使我已经删除了观察者,它们都继续接收更新。
Observer observer = new Observer<DownloadItem>() {
@Override
public void onChanged(@Nullable DownloadItem downloadItem) {
if(downloadItem!= null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObservers((AppCompatActivity)context);
}
};
model.getDownloadByContentId(contentId).observeForever(observer);
model.getDownloadByContentId(contentId).observe((AppCompatActivity)context, downloadItem-> {
if(downloadItem!= null) {
this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObserver(downloadItem-> {});
} );
你的第一个不起作用,因为 observeForever()
没有绑定到任何 LifecycleOwner
。
你的第二个将不起作用,因为你没有将现有的注册观察者传递给 removeObserver()
。
您首先需要确定是否将 LiveData
与 LifecycleOwner
(您的 activity)一起使用。我的假设是您应该使用 LifecycleOwner
。在这种情况下,使用:
Observer observer = new Observer<DownloadItem>() {
@Override
public void onChanged(@Nullable DownloadItem downloadItem) {
if(downloadItem!= null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObservers((AppCompatActivity)context);
}
};
model.getDownloadByContentId(contentId).observe((AppCompatActivity)context, observer);
根据 CommonsWare 的回答,您可以简单地调用 removeObserver(this)
来仅删除此观察者,而不是调用 removeObservers()
这将删除所有附加到 LiveData 的观察者:
Observer observer = new Observer<DownloadItem>() {
@Override
public void onChanged(@Nullable DownloadItem downloadItem) {
if(downloadItem!= null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObserver(this);
}
};
model.getDownloadByContentId(contentId).observe((AppCompatActivity)context, observer);
注意:removeObserver(this)
中的,this
指的是观察者实例,这只在匿名内部class的情况下有效.如果您使用 lambda,则 this
将引用 activity 实例。
- LiveData class 有 2 种类似的方法来删除观察者。首先是命名,
removeObserver(@NonNull final Observer<T> observer)
(仔细看方法名,是单数),它接收你想从同一个LifecycleOwner的观察者列表中移除的观察者。
- 第二种方法是
removeObservers(@NonNull final LifecycleOwner owner)
(参见复数方法名称)。此方法接收 LifecycleOwner 本身并删除指定 LifecycleOwner 的所有观察者。
现在在你的情况下,你可以通过两种方式移除你的观察者(可能有很多方式),@ToniJoe 在之前的回答中告诉了你一种方式。
另一种方法是在您的 ViewModel 中有一个布尔值的 MutableLiveData,它在第一次被观察时存储 true 并且也只观察该 Livedata。因此,只要它变为真,您就会收到通知,并且您可以通过传递该特定观察者来移除您的观察者。
Kotlin 有一个更方便的扩展解决方案:
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}
此扩展允许我们这样做:
liveData.observeOnce(this, Observer<Password> {
if (it != null) {
// do something
}
})
因此,为了回答您最初的问题,我们可以这样做:
val livedata = model.getDownloadByContentId(contentId)
livedata.observeOnce((AppCompatActivity) context, Observer<T> {
if (it != null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
}
startDownload();
})
原文出处在这里:https://code.luasoftware.com/tutorials/android/android-livedata-observe-once-only-kotlin/
更新:@Hakem-Zaied 是对的,我们需要使用 observe
而不是 observeForever
。
我同意上面的 Vince,但我认为我们要么跳过 lifecycleOwner
并使用 observerForever
,如下所示:
fun <T> LiveData<T>.observeOnce(observer: Observer<T>) {
observeForever(object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}
或者,使用 lifecycleOwner
和 observe
如下:
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}
我喜欢 Vince and Hakem Zaied 的通用解决方案,但对我来说,lambda 版本似乎更好:
fun <T> LiveData<T>.observeOnce(observer: (T) -> Unit) {
observeForever(object: Observer<T> {
override fun onChanged(value: T) {
removeObserver(this)
observer(value)
}
})
}
fun <T> LiveData<T>.observeOnce(owner: LifecycleOwner, observer: (T) -> Unit) {
observe(owner, object: Observer<T> {
override fun onChanged(value: T) {
removeObserver(this)
observer(value)
}
})
}
所以你最终得到:
val livedata = model.getDownloadByContentId(contentId)
livedata.observeOnce((AppCompatActivity) context) {
if (it != null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists")
}
startDownload();
}
我觉得哪个更干净。
此外,removeObserver()
在观察者被调度时被调用 first-thing,这使得它更安全(即处理从用户的观察者代码中抛出的潜在运行时错误)。
@CommonsWare 和@Toni Joe 提出的解决方案并没有解决我的问题,因为我需要在我的 ViewModel 中收到来自 DAO 查询的第一个结果后删除观察者。但是,在 Livedata keeps observer after calling removeObserer 上找到的以下解决方案凭借我自己的一些直觉为我解决了这个问题。
过程如下,根据请求在您的 ViewModel 中创建一个变量,其中存储 LiveData,在进行空检查后,在 activity 中的创建观察者函数调用中检索它,并调用 remove在导入的 class 中调用 flushToDB 例程之前观察者函数。也就是说,我的 ViewModel 中的代码如下所示:
public class GameDataModel extends AndroidViewModel {
private LiveData<Integer> lastMatchNum = null;
.
.
.
private void initLastMatchNum(Integer player1ID, Integer player2ID) {
List<Integer> playerIDs = new ArrayList<>();
playerIDs.add(player1ID);
playerIDs.add(player2ID);
lastMatchNum = mRepository.getLastMatchNum(playerIDs);
}
public LiveData<Integer> getLastMatchNum(Integer player1ID, Integer player2ID) {
if (lastMatchNum == null) { initLastMatchNum(player1ID, player2ID); }
return lastMatchNum;
}
在上面,如果 ViewModel 的 LiveData 变量中没有数据,我调用 initLastMatchNum()
从视图模型中的函数检索数据。从 activity 调用的函数是 getLastMatchNum()
。此例程检索 ViewModel 中变量中的数据(通过 DAO 通过存储库检索)。
我的 Activity
中有以下代码public class SomeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
.
.
.
setupLastMatchNumObserver();
.
.
.
}
private void setupLastMatchNumObserver() {
if (mGameDataViewModel.getLastMatchNum(Player1ID, Player2ID).hasObservers()) {
Log.v("Observers", "setupLastMatchNumObserver has observers...returning");
return;
}
Log.v("Setting up Observers", "running mGameDataViewModel.get...observer()");
mGameDataViewModel.getLastMatchNum(Player1ID, Player2ID).observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer MatchNumber) {
if (MatchNumber == null ) {
matchNumber = 1;
Log.v( "null MatchNumber", "matchNumber: " + matchNumber.toString());
}
else {
matchNumber = MatchNumber; matchNumber++;
Log.v( "matchNumber", "Incrementing match number: " + matchNumber.toString());
}
MatchNumberText.setText(matchNumber.toString());
}
});
}
private void removeObservers() {
final LiveData<Integer> observable = mGameDataViewModel.getLastMatchNum(Player1ID, Player2ID);
if (observable != null && observable.hasObservers()) {
Log.v("removeObserver", "Removing Observers");
observable.removeObservers(this);
}
}
上面发生的事情是 1.) 我在 activity 的 onCreate
方法中调用 setupLastMatchNumObserver()
例程来更新 class'变量 matchNum
。这会跟踪存储在数据库中的游戏中玩家之间的比赛号码。每组球员在数据库中都会有不同的比赛编号,这取决于他们彼此进行新比赛的频率。这个线程中的第一个解决方案对我来说似乎有点厌倦,因为在 onChanged
中调用 remove observers 对我来说似乎很奇怪,并且会在玩家每次移动的每次数据库刷新后不断更改 TextView
对象。所以 matchNumber
在每次移动后都会增加,因为在第一次移动后数据库中有一个新值(即一个 matchNumber++
值)并且 onChanged
一直被调用,因为 removeObservers
没有按预期工作。 setupLastMatchNumObserver()
检查是否有实时数据的观察者,如果有,则不会在每一轮实例化新调用。如您所见,我正在设置一个 TextView
对象来反映玩家的当前比赛号码。
下一部分是关于何时调用 removeObservers()
的小技巧。起初我想如果我在 onCreate
覆盖 activity 的 setupLastMatchNumObserver()
之后直接调用它,一切都会很好。但是它在观察者可以获取数据之前移除了观察者。我发现如果我在调用之前直接调用 removeObservers()
将 activity 中收集的新数据刷新到数据库(在整个 activity 的单独例程中),它就像一个魅力.即,
public void addListenerOnButton() {
.
.
.
@Override
public void onClick(View v) {
.
.
.
removeObservers();
updateMatchData(data);
}
}
我也在 activity 的其他地方以上述方式调用 removeObservers();
和 updateMatchData(data)
。美妙之处在于 removeObservers()
可以根据需要多次调用,因为如果没有观察者在场,就会检查 return 。
这是其他答案中建议的 observeOnce
方法的 Java 版本(util class 方法而不是 Kotlin 扩展函数):
public class LiveDataUtil {
public static <T> void observeOnce(final LiveData<T> liveData, final Observer<T> observer) {
liveData.observeForever(new Observer<T>() {
@Override
public void onChanged(T t) {
liveData.removeObserver(this);
observer.onChanged(t);
}
});
}
}
您不止一次创建实时数据实例 (model.getDownloadByContentId(contentId)),这就是问题所在。
试试这个:
LiveData myLiveData =model.getDownloadByContentId(contentId);
myLiveData.observe(getViewLifecycleOwner(), downloadItem-> {
if(downloadItem!= null) {
this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
myLiveData.removeObservers(getViewLifecycleOwner());
} );
Vince 和 Hakem Zaied 解决方案运行良好,但就我而言,我试图获取实时数据实例并更新本地数据库,但实时数据首先要从远程 API 更新,因此我得到一个 NullPointer,所以我切换到 observeForever 并且我能够在更新时获取数据,但现在我必须在获取数据后处理观察者,所以我修改了 Vince 解决方案以仅在以下情况下观察和发出数据实时数据包含数据。
fun <T> LiveData<T>.observeOnce(observer: (T) -> Unit) {
observeForever(object : Observer<T> {
override fun onChanged(value: T) {
//Resource is my data class response wrapper, with this i was able to
//only update the observer when the livedata had values
//the idea is to cast the value to the expected type and check for nulls
val resource = value as Resource<*>
if (resource.data != null) {
observer(value)
removeObserver(this)
}}
})
}
这是一个 androidx.lifecycle.Observer Java 示例:
Observer <? super List<MyEntity>> observer = new Observer<List<MyEntity>>() {
@Override
public void onChanged(List<MyEntity> myEntities) {
Log.d(TAG, "observer changed");
MySearchViewModel.getMyList().removeObserver(this);
}
};
MySearchViewModel.getMyList().observe(MainActivity.this, observer);
在我看来,Livedata 旨在持续接收即将到来的数据。如果你只是想让它只执行一次,比如从服务器请求数据来初始化 UI,我建议你这样设计你的代码:
1、将你的耗时方法定义为non-Livedata type inside Viewmodel。您不必在此过程中启动新线程。
2、在Activity中启动一个new Thread,在new Thread中,调用上面定义的方法,然后runOnUiThread()
编写使用请求数据的逻辑。通过这种方式,耗时的方法不会阻塞 UI 线程,而它会阻塞新线程,因此 runOnUiThread() 仅在成功接收到您请求的数据后运行。
因此,如果这是您想要的,请考虑更换 Livedata。
Java 版本的 observeOnce
方法已经被许多用户推荐。但是这里我们将在主要代码中实现。
首先,我们需要创建Util class方法
public class LiveDataUtil {
public static <T> void observeOnce(final LiveData<T> liveData, final Observer<T> observer) {
liveData.observeForever(new Observer<T>() {
@Override
public void onChanged(T t) {
liveData.removeObserver(this);
observer.onChanged(t);
}
});
}}
现在,我们需要在需要 ViewModel 的地方调用它 class。
LiveDataUtil.observeOnce(viewModel.getUserDetails(), response-> {
if(response.isSuccessful()){
//Do your task
}
}
就这些!
我阅读了一些文档并在观察者那里看到了 remove 方法,所以我得出了这个解决方案:
1:先声明观察者:
// observer for selecting chip in view
View actionView;
Observer selectChipFunction = (action) -> selectChip(actionView, action);
2:然后使用观察者:
// select an action if set before
if (sharedViewModel.getAction() != null)
sharedViewModel.getAction().observe(getViewLifecycleOwner(), selectChipFunction);
3:然后在selectChip观察者中去掉观察者:
/**
* select action chip
* @param actionView - view to use for selecting action chip
* @param actionObject - action chip to select
*/
private void selectChip(View actionView, Object actionObject)
{
// no need for observing when action is changed so remove.
sharedViewModel.getAction().removeObserver(selectChipFunction);
这种方式只触发一次,之后就被移除了。在我的例子中,我需要这个,因为我在 selectChipFunction 中设置了触发观察者的“动作”,如果我不这样做,你将以循环观察者触发结束。
这个怎么样:
fun <T> LiveData<T>.observeOnCondition(lifecycleOwner: LifecycleOwner,
observer: Observer<T>,
condition: () -> Boolean) {
observe(lifecycleOwner) { t ->
if (condition()) {
observer.onChanged(t)
}
}
}
如果您想在稍后阶段再次获取数据,您可以通过这种方式定义更通用的条件。