LiveData在第一次回调后删除Observer

gal*_*irl 38 android observers android-room android-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).removeObservers((AppCompatActivity)context);
        }
    };
    model.getDownloadByContentId(contentId).observeForever(observer);
Run Code Online (Sandbox Code Playgroud)
 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-> {});
        } );
Run Code Online (Sandbox Code Playgroud)

Com*_*are 26

你的第一个不会工作,因为observeForever()没有任何关系LifecycleOwner.

你的第二个将无法工作,因为你没有通过现有的注册观察员removeObserver().

首先需要解决您是否使用LiveDataLifecycleOwner(你的活动)或没有.我的假设是你应该使用a 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);
Run Code Online (Sandbox Code Playgroud)

  • 这将从活动/片段中删除所有观察者。我们如何只删除当前的观察者,而不是全部? (3认同)
  • @MrVasilev:这可能是Kotlin SAM对lambda或其他问题的支持。您可能需要使用object:Observer并创建一个“真实的” Observer实例,以便从该Observer的onChanged()函数内部获取正确的this。 (3认同)
  • @CommonsWare 感谢您的回复,您说得对,这是 Kotlin 的问题。只是提到对于 Kotlin,也许解决方案是创建一个像这样的“LiveData”的扩展: fun &lt;T&gt; LiveData&lt;T&gt;.observeOnlyOnce(lifecycleOwner: LifecycleOwner,observer: Observer&lt;T&gt;) { observe(lifecycleOwner, object : Observer &lt;T&gt; { override fun onChanged(t: T?) { observer.onChanged(t) removeObserver(this) } }) } (3认同)
  • @MrVasilev:我不知道在这种情况下“当前”是什么意思。要删除单个观察者,请在“LiveData”上调用“removeObserver()”。 (2认同)

Vin*_*nce 20

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)
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

此扩展允许我们这样做:

liveData.observeOnce(this, Observer<Password> {
    if (it != null) {
        // do something
    }
})
Run Code Online (Sandbox Code Playgroud)

因此,要回答您的原始问题,我们可以这样做:

val livedata = model.getDownloadByContentId(contentId)
livedata.observeOnce((AppCompatActivity) context, Observer<T> {
    if (it != null) {
        DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
    }
    startDownload();
})
Run Code Online (Sandbox Code Playgroud)

原始来源在这里:https : //code.luasoftware.com/tutorials/android/android-livedata-observe-once-only-kotlin/

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

  • 我可以建议先删除观察者 - 只有 _then_ 调用用户的 `onChanged`。否则,如果用户的实现抛出异常,中间观察者将“永远”保持注册状态。 (4认同)
  • 如果在哪里声明扩展函数并不明显,请参阅:https://kotlinlang.org/docs/reference/extensions.html#scope-of-extensions (3认同)
  • 很棒的答案!我稍微修改了它,现在它更加 Kotlinish 了!https://gist.github.com/bartekpacia/eb1c92886acf3972c3f030cde2579ebb (3认同)

Ton*_*Joe 17

CommonsWare回答之后removeObservers(),您可以简单地调用removeObserver(this)只删除此观察者,而不是调用哪个会删除附加到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);
Run Code Online (Sandbox Code Playgroud)

注意: in removeObserver(this),this指的是观察者实例,这仅适用于匿名内部类的情况.如果使用lambda,this则将引用活动实例.

  • @Psest328 这究竟是如何导致循环的? (2认同)

小智 10

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

首先,我们需要创建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);
        }
    });
}}
Run Code Online (Sandbox Code Playgroud)

现在,我们需要在需要 ViewModel 的地方调用此类。

LiveDataUtil.observeOnce(viewModel.getUserDetails(), response-> {
    if(response.isSuccessful()){
       //Do your task
    }
} 
Run Code Online (Sandbox Code Playgroud)

就这样!


Hak*_*ied 7

我同意上面的@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)
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

或如下使用lifecycleOwnerwith 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)
        }
    })
}
Run Code Online (Sandbox Code Playgroud)


alg*_*rid 6

这是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);
            }
        });
    }

}
Run Code Online (Sandbox Code Playgroud)


d4v*_*idi 5

I love the generic solutions by @Vince and @Hakem Zaied, but to me the lambda version seems even better:

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)
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

So you end up with:

    val livedata = model.getDownloadByContentId(contentId)
    livedata.observeOnce((AppCompatActivity) context) {
        if (it != null) {
            DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists")
        }
        startDownload();
    }
Run Code Online (Sandbox Code Playgroud)

Which I find cleaner.

Also, removeObserver() is called first-thing as the observer is dispatched, which makes it safer (i.e. copes with potential runtime error throws from within the user's observer code).

  • Kotlin 用户的完美答案!我刚刚写了相同的代码并想将其发布,直到我找到了这个。+1 (2认同)

小智 5

您正在多次创建实时数据实例 (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());
    } );
Run Code Online (Sandbox Code Playgroud)