第一次回调后删除观察者

收到第一个结果后,如何删除观察者?下面是我尝试过的两种代码方法,但是即使我删除了观察者,它们仍然能够接收到更新。

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-> {});
} );
112414 次浏览

Your first one will not work, because observeForever() is not tied to any LifecycleOwner.

Your second one will not work, because you are not passing the existing registered observer to removeObserver().

您首先需要确定是否使用 LiveDataLifecycleOwner(您的活动)。我的假设是您应该使用 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);

共享软件应答之后,不需要调用 removeObservers()来删除所有附加到 LiveData 的观察器,只需要调用 removeObserver(this)来删除这个观察器:

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引用观察者实例,这仅适用于匿名内部类。如果使用 lambda,那么 this将引用活动实例。

  1. LiveData 类有两个类似的方法来删除观察者,

removeObserver(@NonNull final Observer<T> observer)(仔细查看方法的名称,它是单数) ,它接受您想要从同一 LifcycleOwner 的观察者列表中删除的观察者。

  1. 第二种方法是

removeObservers(@NonNull final LifecycleOwner owner)(参见复数方法名称)。此方法接受 LificycleOwner 本身并删除指定 LificycleOwner 的所有观察者。

Now in your case, you can remove your Observer by 2 ways (there might be many ways), one is told by @ToniJoe in the previous answer.

另一种方法是在 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)
}
})
}

This extension permit us to do that:

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();
})

The original source is here: https://code.luasoftware.com/tutorials/android/android-livedata-observe-once-only-kotlin/

更新:@Hakem-Zaied 是对的,我们需要使用 observe而不是 observeForever

我同意上面的 文斯,但是我相信我们要么跳过 lifecycleOwner,使用如下的 observerForever:

fun <T> LiveData<T>.observeOnce(observer: Observer<T>) {
observeForever(object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}

Or, using the lifecycleOwner with observe as below:

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)
}
})
}

我喜欢 文斯哈基姆 · 扎伊德的通用解决方案,但对我来说,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(context as AppCompatActivity) {
if (it != null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists")
}
startDownload();
}

我觉得这样更干净。

此外,在分派观察器时,removeObserver()被称为第一件事,这使得它更加安全(例如,在用户的观察器代码中处理潜在的运行时错误抛出)。

当我在 ViewModel 中接收到来自 DAO 查询的第一个结果后需要删除观察者时,@CommonsWare 和@Toni Joe 提出的解决方案并没有为我解决这个问题。然而,在 Livedata 在调用 RemoveObserer 之后保持 Observer中发现的下面的解决方案用我自己的一点直觉为我解决了这个问题。

过程如下,在 ViewModel 中创建一个变量,LiveData 在该变量中存储在请求中,在执行 null 检查之后在活动中的 create Observer 函数调用中检索它,在导入的类中调用 flushToDB 例程之前调用 remove Observer 函数。也就是说,我的 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()从视图模型中的函数中检索数据。要从活动中调用的函数是 getLastMatchNum()。这个例程检索 ViewModel 中变量中的数据(通过存储库通过 DAO 检索)。

The following code I have in my 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。)我在活动的 onCreate方法中调用 setupLastMatchNumObserver()例程,以更新类的变量 matchNum。这保持跟踪的比赛号码之间的球员在我的游戏,这是存储在一个数据库。每组球员将有一个不同的比赛号码在数据库中基于他们多久发挥新的比赛与对方。这个线程中的第一个解决方案对我来说似乎有点厌倦,因为在 onChanged中调用移除观察者对我来说似乎很奇怪,并且会在每次玩家移动的数据库刷新之后不断更改 TextView对象。因此 matchNumber在每次移动之后都在增加,因为在第一次移动之后数据库中有一个新的值(即一个 matchNumber++值) ,而 onChanged一直被调用,因为 removeObservers没有按预期的方式工作。setupLastMatchNumObserver()检查是否有实时数据的观察者,如果有,则每轮不实例化一个新的调用。正如你所看到的,我正在设置一个 TextView对象,以反映当前的比赛号码的球员。

下一部分是关于什么时候调用 removeObservers()的一个小技巧。起初,我认为如果我在 onCreate覆盖活动的 setupLastMatchNumObserver()之后直接调用它,那么一切都会很好。但是它在观察者拿到数据之前移除了观察者。我发现,如果我在调用之前直接调用 removeObservers(),将在活动中收集的新数据刷新到数据库(在整个活动的不同例程中) ,那么它的工作方式就像魔法一样。也就是说,

 public void addListenerOnButton() {
.
.
.
@Override
public void onClick(View v) {
.
.
.
removeObservers();
updateMatchData(data);
}
}


我也调用 removeObservers();updateMatchData(data)在其他地方在我的活动在上述时尚。美丽的是 removeObservers()可以调用多次,因为有一个检查返回,如果没有观察员在场。

下面是在其他答案中提出的 observeOnce方法的 Java 版本(一个 util 类方法而不是 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 解决方案运行良好,但在我的例子中,我试图获得 livedata 实例并更新本地数据库,但是 livedata 首先要从远程 API 更新,因此我得到了一个 NullPointer,所以我切换到 Observer Forever,当它被更新时,我能够获得数据,但是现在我必须在获得数据后处理观察者,所以我修改 Vince 解决方案,只在 livedata 包含数据时观察和发出数据。

   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.life 循环观察者 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、在 Viewmodel 中定义 非实时数据类型的耗时方法。在此过程中不必启动新线程。

2、在一个活动中启动一个 新线程,在新的 Thread 中,调用上面定义的方法,然后是 runOnUiThread(),在这里你可以编写使用请求数据的逻辑。这样,耗时的方法将不会阻塞 UI 线程,而阻塞新线程,因此 runOnUiThread ()仅在成功接收到请求的数据后运行。

所以,如果这是你想要的,那就考虑一个 Livedata 的替代品。

许多用户已经建议使用 Java 版本的 observeOnce方法。但是在这里我们将看到主代码中的实现。

首先,我们需要创建 Util 类方法

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 的地方调用这个类。

LiveDataUtil.observeOnce(viewModel.getUserDetails(), response-> {
if(response.isSuccessful()){
//Do your task
}
}

仅此而已!

我阅读了一些文档,在观察者那里看到了删除方法,于是我得出了这个解决方案:

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);




    

然后在 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)
}
}
}

This way you can define a more generic condition if you might want to pick up the data again at a later stage.

您可以使用这样的函数来观察一次,然后删除观察者

fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
var ob: Observer<T>? = null


ob = Observer { value ->
ob?.let {
removeObserver(it)
}
observer.onChanged(value)
}


observe(lifecycleOwner, ob)
}

不幸的是,所有的解决方案都不适合我,唯一适合我的例子就是那个链接。

Https://gist.github.com/kobeumut/edb3edd9a2ae9abf6984a42bb2de0441