LiveData在第一次回调后删除Observer

问题描述 投票:26回答:6

收到第一个结果后,如何删除观察者?下面是我尝试过的两种代码方式,但即使我已经删除了观察者,它们也都会继续接收更新。

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-> {});
        } );
android observers android-room android-livedata
6个回答
19
投票

你的第一个将不起作用,因为observeForever()没有绑定任何LifecycleOwner

你的第二个将无法工作,因为你没有将现有的注册观察员传递给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);

14
投票

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

注意:在removeObserver(this)中,this引用观察者实例,这仅适用于匿名内部类的情况。如果你使用lambda,那么this将引用活动实例。


8
投票

对于带有扩展的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


3
投票

我同意上面的@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)
        }
    })
}

或使用lifecycleOwnerobserve如下:

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

1
投票
  1. LiveData类有两种类似的方法来删除Observers。首先命名,

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

  1. 第二种方法是

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

现在在你的情况下,你可以通过2种方式删除你的观察者(可能有很多种方式),@ ToniJoe在前一个答案中告诉你。

另一种方法是在ViewModel中有一个布尔值的MutableLiveData,当它第一次被观察时存储为true,并且只是观察Livedata。因此,无论何时转为true,您都会收到通知,并且您可以通过传递该特定观察者来移除您的观察者。


1
投票

我喜欢@Vince和@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()被称为第一件事,这使得它更安全(即应对用户观察者代码中潜在的运行时错误抛出)。

© www.soinside.com 2019 - 2024. All rights reserved.