LiveData 防止在开始观察时收到最后一个值
LiveData prevent receive the last value when start observing
是否可以防止LiveData
在开始观察时收到最后一个值?
我正在考虑使用 LiveData
作为事件。
例如显示消息、导航事件或对话触发器等事件,类似于 EventBus
。
关于ViewModel
和fragment之间通信的问题,Google让我们LiveData
用数据更新视图,但是当我们需要更新时这种通信方式不适合单个事件只查看一次,我们也不能在 ViewModel
中保存视图的引用并调用一些方法,因为它会造成内存泄漏。
我发现了类似的东西 SingleLiveEvent - 但它只适用于 1 个观察者,而不适用于多个观察者。
---更新----
正如@EpicPandaForce 所说“没有理由将 LiveData 用作它不是 的东西”,问题的意图可能是 Communication between view and ViewModel in MVVM with LiveData
我认为如果您按原样使用它们,则无法阻止 LiveData 在开始观察时接收到最后一个值。您可以做的是 扩展 ViewModel
class 并使其仅在添加观察者时通知视图。
另一种选择是简单地忽略回调。
向 ViewModel 添加标志。
private boolean isFirstTime = true;
public boolean isFirstTime() { return isFirstTime; }
public boolean onObserverAdded() { isFirstTime = false; }`
在回调中添加检查
@Override
public void onChanged(@Nullable final String newName) {
boolean ignore = ((MyViewModel)ViewModelProviders.of(MyActivity.this).get(MyViewModel.class)).isFirstTime();
if(ignore) return;
// Update the UI
}
添加观察者后最后调用onObserverAdded()
我创建了一个新的 Class 来保存我的真实数据和一个 "special ID":
class LiveDataItem {
long mRealtimeNanos;
YOUR_PREVIOUS_LIVEDATA_TYPE mData;
LiveDataItem(YOUR_PREVIOUS_LIVEDATA_TYPE data, long realtimeNanos) {
this.mRealtimeNanos = realtimeNanos;
this.mData = data;
}
}
然后我创建了一个新的 "global" 变量:
final List<Long> mExcludedRealtimeNanos = new ArrayList<>;
此时我选择 "set/postValue()" 我的 "LiveDataItem" 类型而不是原来的 "YOUR_PREVIOUS_LIVEDATA_TYPE" 通过新的自定义 "postValue()" 方法:
public void myPostValue(YOUR_PREVIOUS_LIVEDATA_TYPE data, boolean notifyWhenObserved) {
long cRealtimeNanos = SystemClock.realtimeNanos();
if (!notifyWhenObserved) mExcludedRealtimeNanos.add(cRealtimeNanos);
....postValue(new LiveDataItem(data, cRealtimeNanos));
}
然后我创建了一个普通的 Observer,它将接收所有 "Changed()" 事件,并在其中检查 "RealtimeNanos":
public void onChanged(LiveDataItem myDataItem) {
boolean cFound = false;
for (Long cRealtimeNanos : mExcludedRealtimeNanos) {
if (cRealtimeNanos == myDataItem.mRealtimeNanos) {
cFound = true;
break;
}
}
//check if it was found --> NO: it means that I wish to get the notification
if (!cFound) mMyOnChangedCallback(myDataItem.mData)
}
显然,"mMyOnChangedCallback()" 方法是一个回调函数,只要原始 "onChanged()" 事件被引发,但只有当您设置为在数据创建期间通知它时才会被调用。
您可以选择再次收到通知,只需从 "mExcludedRealtimeNanos" 中删除那个 RealtimeNanos,然后将一个新的观察者附加到那个 LiveData。
几乎没有什么改变可以改进这段代码,但我把我记得的旧代码写给你了(我现在不在电脑旁)。例如,当使用我们的自定义 postValue() 方法发布新数据时,我们可以决定从 "mExcludedRealtimeNanos" 中删除一个值....
有了一些使用 RxJava 的经验,我已经习惯于认为这种行为要求通常是 Observeable
(在我们的例子中是 LiveData
)的关注点。有许多 operators such as replay() 可以控制与用户实际发布的内容相比实际发布的内容(以及发布时间)。本质上,SingleLiveEvent
也有同样的概念。
因此,我想出了这个 MutableLiveData
的修改实现,称为 VolatileLiveData
:
open class VolatileLiveData<T> : MutableLiveData<T>() {
private val lastValueSeq = AtomicInteger(0)
private val wrappers = HashMap<Observer<in T>, Observer<T>>()
@MainThread
public override fun setValue(value: T) {
lastValueSeq.incrementAndGet()
super.setValue(value)
}
@MainThread
public override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
val observerWrapper = ObserverWrapper(lastValueSeq, observer)
wrappers[observer] = observerWrapper
super.observe(owner, observerWrapper)
}
@MainThread
public override fun observeForever(observer: Observer<in T>) {
val observerWrapper = ObserverWrapper(lastValueSeq, observer)
wrappers[observer] = observerWrapper
super.observeForever(observerWrapper)
}
@MainThread
public override fun removeObserver(observer: Observer<in T>) {
val observerWrapper = wrappers[observer]
observerWrapper?.let {
wrappers.remove(observerWrapper)
super.removeObserver(observerWrapper)
}
}
}
private class ObserverWrapper<T>(private var currentSeq: AtomicInteger, private val observer: Observer<in T>) : Observer<T> {
private val initialSeq = currentSeq.get()
private var _observer: Observer<in T> = Observer {
if (currentSeq.get() != initialSeq) {
// Optimization: this wrapper implementation is only needed in the beginning.
// Once a valid call is made (i.e. with a different concurrent sequence), we
// get rid of it any apply the real implementation as a direct callthrough.
_observer = observer
_observer.onChanged(it)
}
}
override fun onChanged(value: T) {
_observer.onChanged(value)
}
}
首先,与@emandt 类似,我将唯一序列与每个实时值相关联——但严格在实时数据本身的范围内。只要将值设置为实时数据,就会设置此序列。
其次,受到 SingleLiveData
的启发,我在用户的观察者周围引入了包装器,只有在序列不同时才调用它(即 new 值自订阅以来已设置)。
这基本上就是总结了,但要获得完整的文档,请转到我的 gist。
用法
至于使用它 - 如果您可以完全控制 LiveData
,只需像使用 MutableLiveData
一样使用 VolatileLiveData
。如果数据最初来自其他地方(例如 Room),可以使用 Transformations.switchMap()
来对 volatile 实现进行 'switch'。
遇到同样的问题,我创建了一些简单的kotlin扩展函数,可以轻松解决问题。
用法如下:
val liveData = MutableLiveData<String>()
liveData.value = "Hello"
val freshResult = mutableListOf<String>()
val normalResult = mutableListOf<String>()
liveData.observeForeverFreshly(Observer {
freshResult.add(it)
})
liveData.observeForever(Observer {
normalResult.add(it)
})
liveData.value = "World"
assertEquals(listOf("World"), freshResult)
assertEquals(listOf("Hello", "World"), normalResult)
基本源代码解释如下。
更多详细信息(支持一些特殊情况,例如 Transformations.map 返回的 MediatorLiveData
),您可以在 github 中查看:livedata-ext
FreshLiveData.kt
fun <T> LiveData<T>.observeFreshly(owner: LifecycleOwner, observer: Observer<in T>) {
// extention fuction to get LiveData's version, will explain in below.
val sinceVersion = this.version()
this.observe(owner, FreshObserver<T>(observer, this, sinceVersion))
}
fun <T> LiveData<T>.observeForeverFreshly(observer: Observer<in T>, skipPendingValue: Boolean = true) {
val sinceVersion = this.version()
this.observeForever(FreshObserver<T>(observer, this, sinceVersion))
}
// Removes the observer which has been previously observed by [observeFreshly] or [observeForeverFreshly].
fun <T> LiveData<T>.removeObserverFreshly(observer: Observer<in T>) {
this.removeObserver(FreshObserver<T>(observer, this, 0))
}
class FreshObserver<T>(
private val delegate: Observer<in T>,
private val liveData: LiveData<*>,
private val sinceVersion: Int
) : Observer<T> {
override fun onChanged(t: T) {
if (liveData.version() > sinceVersion) {
delegate.onChanged(t)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (delegate != (other as FreshObserver<*>).delegate) return false
return true
}
override fun hashCode(): Int {
return delegate.hashCode()
}
}
因为我们需要访问LiveData的pcakage visibile方法getVersion()
进行比较,所以在包android.arch.lifecycle
或androidx.lifecycle
(AndroidX)中创建一个class:
LiveDataHiddenApi.kt
package androidx.lifecycle
fun LiveData<*>.version(): Int {
return this.getVersion()
}
我正在使用这个 EventWraper class 来自 MutableLiveDataGoogle 样本
/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
*/
public class Event<T> {
private T mContent;
private boolean hasBeenHandled = false;
public Event( T content) {
if (content == null) {
throw new IllegalArgumentException("null values in Event are not allowed.");
}
mContent = content;
}
@Nullable
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return mContent;
}
}
public boolean hasBeenHandled() {
return hasBeenHandled;
}
}
在 ViewModel 中:
/** expose Save LiveData Event */
public void newSaveEvent() {
saveEvent.setValue(new Event<>(true));
}
private final MutableLiveData<Event<Boolean>> saveEvent = new MutableLiveData<>();
public LiveData<Event<Boolean>> onSaveEvent() {
return saveEvent;
}
在Activity/Fragment
mViewModel
.onSaveEvent()
.observe(
getViewLifecycleOwner(),
booleanEvent -> {
if (booleanEvent != null)
final Boolean shouldSave = booleanEvent.getContentIfNotHandled();
if (shouldSave != null && shouldSave) saveData();
}
});
我也有同样的要求。我通过扩展 MutableLiveData
实现了这一点
package com.idroidz.android.ion.util;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.Observer;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
import java.util.concurrent.atomic.AtomicBoolean;
public class VolatileMutableLiveData<T> extends MutableLiveData<T> {
private final AtomicBoolean mPending = new AtomicBoolean(false);
@MainThread
public void observe(LifecycleOwner owner, final Observer<T> observer) {
// Observe the internal MutableLiveData
mPending.set(false);
super.observe(owner, new Observer<T>() {
@Override
public void onChanged(@Nullable T t) {
if (mPending.get()) {
observer.onChanged(t);
}
}
});
}
@MainThread
public void setValue(@Nullable T t) {
mPending.set(true);
super.setValue(t);
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
public void call() {
setValue(null);
}
public void callFromThread() {
super.postValue(null);
}
}
忽略调用 android.arch.lifecycle.LiveData#observe
函数之前的数据。
class IgnoreHistoryLiveData<T> : MutableLiveData<T>() {
private val unactivedObservers = LinkedBlockingQueue<WrapperObserver<T>>()
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
val wo = WrapperObserver<T>(observer)
unactivedObservers.add(wo)
super.observe(owner, wo)
}
override fun setValue(value: T) {
while (unactivedObservers.isNotEmpty()) {
unactivedObservers.poll()?.actived = true
}
super.setValue(value)
}
}
private class WrapperObserver<T>(private val origin: Observer<T>) : Observer<T> {
var actived = false
override fun onChanged(t: T?) {
if (actived) {
origin.onChanged(t)
}
}
}
我创建了一个 LiveData 对象 FreshLiveData
,它仅在调用 setValue
或 postValue
.[=19= 后才向观察者发出 onChange
]
FreshLiveData.kt
/**
* A lifecycle-aware observable that emits only new data after subscription. Any data that has
* already been set, before the observable has subscribed, will be ignored.
*
* This avoids a common problem with events: on configuration change (like rotation, font change) an
* update can be emitted if the observer is active. This LiveData only calls the observable if
* there's an explicit call to setValue() or postValue().
*
* All observers will be notified of change(s).
*/
class FreshLiveData<T> : MutableLiveData<T>() {
private val observers = mutableMapOf<LifecycleOwner, FreshLiveDataObserver>()
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
@Suppress("UNCHECKED_CAST")
observer as Observer<T>
observers[owner].apply {
if (this == null) {
observers[owner] = FreshLiveDataObserver(observer).apply {
super.observe(owner, this)
}
} else {
add(observer)
}
}
}
override fun observeForever(observer: Observer<in T>) {
@Suppress("UNCHECKED_CAST")
observer as Observer<T>
observers[ProcessLifecycleOwner.get()].apply {
if (this == null) {
observers[ProcessLifecycleOwner.get()] = FreshLiveDataObserver(observer).apply {
super.observeForever(this)
}
} else {
add(observer)
}
}
}
override fun removeObservers(owner: LifecycleOwner) {
observers.remove(owner)
super.removeObservers(owner)
}
override fun removeObserver(observer: Observer<in T>) {
@Suppress("UNCHECKED_CAST")
observers.forEach { it.value.remove(observer as Observer<T>) }
super.removeObserver(observer)
}
@MainThread
override fun setValue(t: T?) {
observers.forEach { it.value.setPending() }
super.setValue(t)
}
override fun postValue(value: T) {
observers.forEach { it.value.setPending() }
super.postValue(value)
}
inner class FreshLiveDataObserver(observer: Observer<T>) : Observer<T> {
private val observers = mutableSetOf<Observer<T>>()
private val pending = AtomicBoolean(false)
init {
observers.add(observer)
}
fun add(observer: Observer<T>) = observers.add(observer)
fun remove(observer: Observer<T>) = observers.remove(observer)
fun setPending() = pending.set(true)
override fun onChanged(t: T) {
if (pending.compareAndSet(true, false)) {
observers.forEach { observer ->
observer.onChanged(t)
}
}
}
}
}
这里是将现有 LiveData
转换为 FreshLiveData
的扩展。
LiveDataExtensions.kt
@MainThread
fun <T> LiveData<T>.toFreshLiveData(): LiveData<T> {
val freshLiveData = FreshLiveData<T>()
val output = MediatorLiveData<T>()
// push any onChange from the LiveData to the FreshLiveData
output.addSource(this) { liveDataValue -> freshLiveData.value = liveDataValue }
// then push any onChange from the FreshLiveData out
output.addSource(freshLiveData) { freshLiveDataValue -> output.value = freshLiveDataValue }
return output
}
用法:
val liveData = MutableLiveData<Boolean>()
liveData.value = false
liveData.toFreshLiveData().observeForever {
// won't get called with `it = false` because the observe was setup after setting that livedata value
// will get called with `it = true` because the observer was setup before setting that livedata value
}
liveData.value = false
val freshLiveData = FreshLiveData<Boolean>()
freshLiveData.value = false
freshLiveData.observeForever {
// won't get called with `it = false` because the observe was setup after setting that livedata value
// will get called with `it = true` because the observer was setup before setting that livedata value
}
freshLiveData.value = true
没有理由将 LiveData 用作它不是的东西。如果你需要一个单独的行为(不保留以前的值的东西),那么你应该使用一个不保留以前的值的组件——而不是绕过它(“记住”它已经发出然后忘记发射等)
虽然没有其他可用的正确解决方案,所以我最终不得不自己写一个,因为没有其他选择可以推荐,所以我不得不推荐我为这个特定目的写的那个。
无论如何,你可以添加live-event
库:
implementation 'com.github.Zhuinden:live-event:1.2.0'
来自 Jitpack:maven { url "https://jitpack.io" }
那你可以做
private val eventEmitter = EventEmitter<WordController.Events>()
val controllerEvents: EventSource<WordController.Events> = eventEmitter
和
controllerEvents.observe(viewLifecycleOwner) { event: WordController.Events ->
when (event) {
is WordController.Events.NewWordAdded -> showToast("Added ${event.word}")
}.safe()
}
您可以使用 this 文章中描述的 EventLiveData。它将解决您的问题,我在 2 个生产项目中使用了它。
它是 LiveData 扩展,就像 SingleLiveData 但支持多个观察者。当观察者应该接收事件时,还允许自定义生命周期限制。例如,如果您不想在片段处于后台时接收事件。
EventLiveData 持有它永远观察的内部观察者,覆盖观察方法,绕过本机 LiveData 事件调度机制将观察者保存到内部映射中。
您可以copy/paste整个class或导入库,后者更方便
public class EventLiveData<T> extends LiveData<T> {
private final HashMap<Observer<? super T>, EventObserverWrapper> observers= new HashMap<>();
private final Observer<T> internalObserver;
int mActiveCount = 0;
public EventLiveData() {
this.internalObserver = (new Observer<T>() {
@Override
public void onChanged(T t) {
Iterator<Map.Entry<Observer<? super T>, EventObserverWrapper>> iterator = EventLiveData.this.observers.entrySet().iterator();
while (iterator.hasNext()){
EventObserverWrapper wrapper= iterator.next().getValue();
if(wrapper.shouldBeActive())
wrapper.getObserver().onChanged(t);
}
}
});
}
private void internalObserve(){
super.observeForever(this.internalObserver);
}
@MainThread
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
observe(owner, observer,STARTED,null);
}
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer, @NonNull Lifecycle.State minimumStateForSendingEvent) {
observe(owner, observer,minimumStateForSendingEvent,null);
}
@MainThread
public void observeInOnStart(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
observe(owner, observer,STARTED, Lifecycle.Event.ON_STOP);
}
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer, @NonNull Lifecycle.State minimumStateForSendingEvent, Lifecycle.Event removeObserverEvent) {
assertMainThread("observe");
assertNotNull(owner, "owner");
assertNotNull(observer, "observer");
assertNotNull(owner, "minimumStateForSendingEvent");
assertDestroyedState(minimumStateForSendingEvent);
assertMaximumEvent(removeObserverEvent);
if(minimumStateForSendingEvent==DESTROYED){
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement caller = stackTraceElements[3];
String className = caller.getClassName();
String methodName = caller.getMethodName();
IllegalArgumentException exception =
new IllegalArgumentException("State can not be equal to DESTROYED! : " +
"method " + className + "." + methodName +
", parameter " + minimumStateForSendingEvent);
throw sanitizeStackTrace(exception);
}
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
return;
}
EventLifecycleBoundEventObserver wrapper = new EventLifecycleBoundEventObserver(owner, observer);
wrapper.setMinimumStateForSendingEvent(minimumStateForSendingEvent);
wrapper.setMaximumEventForRemovingEvent(removeObserverEvent);
EventObserverWrapper existing = wrapper;
if(!observers.containsKey(observer))existing = observers.put(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
if (!super.hasObservers()) {
internalObserve();
}
}
@MainThread
@Override
public void observeForever(@NonNull Observer observer) {
assertMainThread("observeForever");
assertNotNull(observer, "observer");
EventAlwaysActiveEventObserver wrapper = new EventAlwaysActiveEventObserver(observer);
EventObserverWrapper existing = wrapper;
if(!observers.containsKey(observer))existing = observers.put(observer, wrapper);
if (existing != null && existing instanceof EventLiveData.EventLifecycleBoundEventObserver) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
if (!super.hasObservers()) {
internalObserve();
}
wrapper.activeStateChanged(true);
}
/**
{@inheritDoc}
*/
@Override
public void removeObservers(@NonNull LifecycleOwner owner) {
assertMainThread("removeObservers");
assertNotNull(owner, "owner");
Iterator<Map.Entry<Observer<? super T>, EventObserverWrapper>> iterator = EventLiveData.this.observers.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<Observer<? super T>, EventObserverWrapper> entry=iterator.next();
if(entry.getValue() instanceof EventLiveData.EventLifecycleBoundEventObserver){
EventLifecycleBoundEventObserver eventLifecycleBoundObserver =(EventLifecycleBoundEventObserver) entry.getValue();
if(eventLifecycleBoundObserver.isAttachedTo(owner))this.observers.remove(entry.getKey());
}
}
}
@Override
public void removeObserver(@NonNull Observer observer) {
assertMainThread("removeObserver");
assertNotNull(observer, "observer");
this.observers.remove(observer);
}
final protected void onActive() {}
protected void onActiveEvent() {}
protected void onInactive() {
}
@SuppressWarnings("WeakerAccess")
public boolean hasObservers() {
return observers.size() > 0;
}
@SuppressWarnings("WeakerAccess")
public boolean hasActiveObservers() {
return mActiveCount > 0;
}
class EventLifecycleBoundEventObserver extends EventObserverWrapper implements LifecycleObserver {
@NonNull
private final LifecycleOwner mOwner;
private Lifecycle.State MINIMUM_STATE_FOR_SENDING_EVENT= STARTED;
private Lifecycle.Event MAXIMUM_EVENT_FOR_REMOVING_EVENT= null;
EventLifecycleBoundEventObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
public Lifecycle.State getMinimumStateForSendingEvent() {
return MINIMUM_STATE_FOR_SENDING_EVENT;
}
public Lifecycle.Event getMaximumStateForRemovingEvent() {
return MAXIMUM_EVENT_FOR_REMOVING_EVENT;
}
public void setMaximumEventForRemovingEvent(Lifecycle.Event MAXIMUM_EVENT_FOR_REMOVING_EVENT) {
this.MAXIMUM_EVENT_FOR_REMOVING_EVENT = MAXIMUM_EVENT_FOR_REMOVING_EVENT;
}
public void setMinimumStateForSendingEvent(Lifecycle.State MINIMUM_STATE_FOR_SENDING_EVENT) {
this.MINIMUM_STATE_FOR_SENDING_EVENT = MINIMUM_STATE_FOR_SENDING_EVENT;
}
@Override
boolean shouldBeActive() {
Lifecycle.State state=mOwner.getLifecycle().getCurrentState();
return state.isAtLeast(MINIMUM_STATE_FOR_SENDING_EVENT);
}
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED||(MAXIMUM_EVENT_FOR_REMOVING_EVENT!=null&&MAXIMUM_EVENT_FOR_REMOVING_EVENT==event)) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
private abstract class EventObserverWrapper {
protected final Observer<? super T> mObserver;
boolean mActive;
EventObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive();
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver() {
}
public Observer<? super T> getObserver() {
return mObserver;
}
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = EventLiveData.this.mActiveCount == 0;
EventLiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
onActiveEvent();
}
if (EventLiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
}
}
private class EventAlwaysActiveEventObserver extends EventObserverWrapper {
EventAlwaysActiveEventObserver(Observer<? super T> observer) {
super(observer);
}
@Override
boolean shouldBeActive() {
return true;
}
}
private void assertDestroyedState(@NonNull Lifecycle.State minimumStateForSendingEvent){
if(minimumStateForSendingEvent==DESTROYED){
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement caller = stackTraceElements[3];
String className = caller.getClassName();
String methodName = caller.getMethodName();
IllegalArgumentException exception =new IllegalArgumentException("State can not be equal to "+ minimumStateForSendingEvent +"method " + className + "." + methodName +", parameter minimumStateForSendingEvent");
throw sanitizeStackTrace(exception);}
}
private void assertMaximumEvent(@NonNull Lifecycle.Event maximumEventForRemovingEvent){
if(maximumEventForRemovingEvent== Lifecycle.Event.ON_START||maximumEventForRemovingEvent== Lifecycle.Event.ON_CREATE
||maximumEventForRemovingEvent== Lifecycle.Event.ON_RESUME){
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement caller = stackTraceElements[3];
String className = caller.getClassName();
String methodName = caller.getMethodName();
IllegalArgumentException exception = new IllegalArgumentException("State can not be equal to "+maximumEventForRemovingEvent + "method " + className + "." + methodName +", parameter maximumEventForRemovingEvent" );
throw sanitizeStackTrace(exception);
}
}
private void assertMainThread(String methodName) {
boolean isUiThread = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? Looper.getMainLooper().isCurrentThread() : Thread.currentThread() == Looper.getMainLooper().getThread();
if (!isUiThread) {throw new IllegalStateException("Cannot invoke " + methodName + " on a background"+ " thread"); }
}
private void assertNotNull(Object value, String paramName) {
if (value == null) {throwParameterIsNullException(paramName); } }
private void throwParameterIsNullException(String paramName) {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement caller = stackTraceElements[3];
String className = caller.getClassName();
String methodName = caller.getMethodName();
IllegalArgumentException exception =
new IllegalArgumentException("Parameter specified as non-null is null: " +
"method " + className + "." + methodName +
", parameter " + paramName);
throw sanitizeStackTrace(exception);
}
private <T extends Throwable> T sanitizeStackTrace(T throwable) { return sanitizeStackTrace(throwable, this.getClass().getName());}
<T extends Throwable> T sanitizeStackTrace(T throwable, String classNameToDrop) {
StackTraceElement[] stackTrace = throwable.getStackTrace();
int size = stackTrace.length;
int lastIntrinsic = -1;
for (int i = 0; i < size; i++) {
if (classNameToDrop.equals(stackTrace[i].getClassName())) {lastIntrinsic = i; } }
StackTraceElement[] newStackTrace = Arrays.copyOfRange(stackTrace, lastIntrinsic + 1, size);
throwable.setStackTrace(newStackTrace);
return throwable;
}
}
更简单的解决方案是使用 EventLiveData 库:
implementation 'com.rugovit.eventlivedata:eventlivedata:1.0'
MutableEventLiveData<String> eventLiveData =new MutableEventLiveData<>();
viewModel.event.observe(this, Observer {
// ...
})
您可以像使用常规实时数据一样使用它。它是 livedata 的扩展,支持 livedata 的每一个特性。
与其他解决方案不同,它支持多个观察者。
Github link: https://github.com/rugovit/EventLiveData
根据 jurij-pitulja 的回答。
如果我们使用 kotlin coroutines
解决方案看起来像这样。
class Event<T>(private val content: T) {
var isHandled = false
private set
fun getContentIfNotHandled(): T? {
return takeIf { !isHandled }?.let {
isHandled = true
content
}
}
}
view model
内部 class 将 Flow.asLiveData()
替换为 emit new Event
val authResult: LiveData<Event<Result<AuthResponse>>> = _emailLiveData.switchMap { email ->
liveData{
repository.authRequest(email).collect{
emit(Event(it))
}
}
}
在 fragment
中实现 observer
方法
viewModel.authResult.observe(viewLifecycleOwner){
it.getContentIfNotHandled()?.run {
onAuthRequestComplete(this)
}
}
我编写了一个小实用程序 class,它使我能够快速阻止任何代码块的第一个 运行,如下所示:
class SkipFirstRun {
var canRun = false
fun run(codeToRunButNotFirstTime: () -> Unit) {
if (canRun) codeToRunButNotFirstTime()
canRun = true
}
}
然后在我的 Fragment 中声明它的一个实例变量:
class TrainingDetailCardInfoFragment
override val viewModel: TrainingViewModel by viewModels
private var skipInitialEndOfTraining = SkipFirstRun()
...
然后像这样使用它
viewModel.runningTask.observe(viewLifecycleOwner) { task ->
skipInitialEndOfTraining.run {
Log.debug("Playing stopped!")
sound.play(requireContext(), R.raw.endoftraining)
playBarViewModel.stop()
}
}
我反复遇到这个问题,这似乎是新 recommended guidance on consuming single UI events 的 Flow 和 的问题。我难以理解的原因是您需要使用事件的方式存在细微差别。
著名的 EventWrapper 正在处理一个用例:
开始观察 -> 事件发生 -> re-establish观察并确保我们不不会再收到那个事件了
但是如果首先观察的行为也触发了最新数据怎么办?似乎 none 的官方指南解决了如果你 post 有意向 LiveData 发送某些内容,它将在第一次观察时传播 如果尚未观察到 ,即使您使用的是 Flow 或 EventWrapper。
有:
- 您只想消费一次的事件 (SingleLiveEvent)
- 您希望多个观察者只使用一次的事件(EventWrapper,或使用 StateFlow + 列表 + ID 的当前架构指南,带有大量疯狂的样板)
- 并且只有在你开始观察一个或多个观察者之后你才想消费的事件(这个问题)
当视图模型在两个片段之间共享时,这尤其是一个问题。即片段 A post 是实时数据的一个值,然后点击几下或稍后网络响应片段 B 出现在屏幕上并开始观察相同的共享实时数据,您希望它丢弃最新结果并仅使用未来的结果. 在这种情况下,您将 LiveData 用于其设计目的之外的事情,并且需要查看条件以了解为什么片段 B 不应该关心最新结果并使用逻辑 在观察中过滤掉它。
是否可以防止LiveData
在开始观察时收到最后一个值?
我正在考虑使用 LiveData
作为事件。
例如显示消息、导航事件或对话触发器等事件,类似于 EventBus
。
关于ViewModel
和fragment之间通信的问题,Google让我们LiveData
用数据更新视图,但是当我们需要更新时这种通信方式不适合单个事件只查看一次,我们也不能在 ViewModel
中保存视图的引用并调用一些方法,因为它会造成内存泄漏。
我发现了类似的东西 SingleLiveEvent - 但它只适用于 1 个观察者,而不适用于多个观察者。
---更新----
正如@EpicPandaForce 所说“没有理由将 LiveData 用作它不是 的东西”,问题的意图可能是 Communication between view and ViewModel in MVVM with LiveData
我认为如果您按原样使用它们,则无法阻止 LiveData 在开始观察时接收到最后一个值。您可以做的是 扩展 ViewModel
class 并使其仅在添加观察者时通知视图。
另一种选择是简单地忽略回调。
向 ViewModel 添加标志。
private boolean isFirstTime = true; public boolean isFirstTime() { return isFirstTime; } public boolean onObserverAdded() { isFirstTime = false; }`
在回调中添加检查
@Override public void onChanged(@Nullable final String newName) { boolean ignore = ((MyViewModel)ViewModelProviders.of(MyActivity.this).get(MyViewModel.class)).isFirstTime(); if(ignore) return; // Update the UI }
添加观察者后最后调用
onObserverAdded()
我创建了一个新的 Class 来保存我的真实数据和一个 "special ID":
class LiveDataItem {
long mRealtimeNanos;
YOUR_PREVIOUS_LIVEDATA_TYPE mData;
LiveDataItem(YOUR_PREVIOUS_LIVEDATA_TYPE data, long realtimeNanos) {
this.mRealtimeNanos = realtimeNanos;
this.mData = data;
}
}
然后我创建了一个新的 "global" 变量:
final List<Long> mExcludedRealtimeNanos = new ArrayList<>;
此时我选择 "set/postValue()" 我的 "LiveDataItem" 类型而不是原来的 "YOUR_PREVIOUS_LIVEDATA_TYPE" 通过新的自定义 "postValue()" 方法:
public void myPostValue(YOUR_PREVIOUS_LIVEDATA_TYPE data, boolean notifyWhenObserved) {
long cRealtimeNanos = SystemClock.realtimeNanos();
if (!notifyWhenObserved) mExcludedRealtimeNanos.add(cRealtimeNanos);
....postValue(new LiveDataItem(data, cRealtimeNanos));
}
然后我创建了一个普通的 Observer,它将接收所有 "Changed()" 事件,并在其中检查 "RealtimeNanos":
public void onChanged(LiveDataItem myDataItem) {
boolean cFound = false;
for (Long cRealtimeNanos : mExcludedRealtimeNanos) {
if (cRealtimeNanos == myDataItem.mRealtimeNanos) {
cFound = true;
break;
}
}
//check if it was found --> NO: it means that I wish to get the notification
if (!cFound) mMyOnChangedCallback(myDataItem.mData)
}
显然,"mMyOnChangedCallback()" 方法是一个回调函数,只要原始 "onChanged()" 事件被引发,但只有当您设置为在数据创建期间通知它时才会被调用。
您可以选择再次收到通知,只需从 "mExcludedRealtimeNanos" 中删除那个 RealtimeNanos,然后将一个新的观察者附加到那个 LiveData。
几乎没有什么改变可以改进这段代码,但我把我记得的旧代码写给你了(我现在不在电脑旁)。例如,当使用我们的自定义 postValue() 方法发布新数据时,我们可以决定从 "mExcludedRealtimeNanos" 中删除一个值....
有了一些使用 RxJava 的经验,我已经习惯于认为这种行为要求通常是 Observeable
(在我们的例子中是 LiveData
)的关注点。有许多 operators such as replay() 可以控制与用户实际发布的内容相比实际发布的内容(以及发布时间)。本质上,SingleLiveEvent
也有同样的概念。
因此,我想出了这个 MutableLiveData
的修改实现,称为 VolatileLiveData
:
open class VolatileLiveData<T> : MutableLiveData<T>() {
private val lastValueSeq = AtomicInteger(0)
private val wrappers = HashMap<Observer<in T>, Observer<T>>()
@MainThread
public override fun setValue(value: T) {
lastValueSeq.incrementAndGet()
super.setValue(value)
}
@MainThread
public override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
val observerWrapper = ObserverWrapper(lastValueSeq, observer)
wrappers[observer] = observerWrapper
super.observe(owner, observerWrapper)
}
@MainThread
public override fun observeForever(observer: Observer<in T>) {
val observerWrapper = ObserverWrapper(lastValueSeq, observer)
wrappers[observer] = observerWrapper
super.observeForever(observerWrapper)
}
@MainThread
public override fun removeObserver(observer: Observer<in T>) {
val observerWrapper = wrappers[observer]
observerWrapper?.let {
wrappers.remove(observerWrapper)
super.removeObserver(observerWrapper)
}
}
}
private class ObserverWrapper<T>(private var currentSeq: AtomicInteger, private val observer: Observer<in T>) : Observer<T> {
private val initialSeq = currentSeq.get()
private var _observer: Observer<in T> = Observer {
if (currentSeq.get() != initialSeq) {
// Optimization: this wrapper implementation is only needed in the beginning.
// Once a valid call is made (i.e. with a different concurrent sequence), we
// get rid of it any apply the real implementation as a direct callthrough.
_observer = observer
_observer.onChanged(it)
}
}
override fun onChanged(value: T) {
_observer.onChanged(value)
}
}
首先,与@emandt 类似,我将唯一序列与每个实时值相关联——但严格在实时数据本身的范围内。只要将值设置为实时数据,就会设置此序列。
其次,受到 SingleLiveData
的启发,我在用户的观察者周围引入了包装器,只有在序列不同时才调用它(即 new 值自订阅以来已设置)。
这基本上就是总结了,但要获得完整的文档,请转到我的 gist。
用法
至于使用它 - 如果您可以完全控制 LiveData
,只需像使用 MutableLiveData
一样使用 VolatileLiveData
。如果数据最初来自其他地方(例如 Room),可以使用 Transformations.switchMap()
来对 volatile 实现进行 'switch'。
遇到同样的问题,我创建了一些简单的kotlin扩展函数,可以轻松解决问题。
用法如下:
val liveData = MutableLiveData<String>()
liveData.value = "Hello"
val freshResult = mutableListOf<String>()
val normalResult = mutableListOf<String>()
liveData.observeForeverFreshly(Observer {
freshResult.add(it)
})
liveData.observeForever(Observer {
normalResult.add(it)
})
liveData.value = "World"
assertEquals(listOf("World"), freshResult)
assertEquals(listOf("Hello", "World"), normalResult)
基本源代码解释如下。
更多详细信息(支持一些特殊情况,例如 Transformations.map 返回的 MediatorLiveData
),您可以在 github 中查看:livedata-ext
FreshLiveData.kt
fun <T> LiveData<T>.observeFreshly(owner: LifecycleOwner, observer: Observer<in T>) {
// extention fuction to get LiveData's version, will explain in below.
val sinceVersion = this.version()
this.observe(owner, FreshObserver<T>(observer, this, sinceVersion))
}
fun <T> LiveData<T>.observeForeverFreshly(observer: Observer<in T>, skipPendingValue: Boolean = true) {
val sinceVersion = this.version()
this.observeForever(FreshObserver<T>(observer, this, sinceVersion))
}
// Removes the observer which has been previously observed by [observeFreshly] or [observeForeverFreshly].
fun <T> LiveData<T>.removeObserverFreshly(observer: Observer<in T>) {
this.removeObserver(FreshObserver<T>(observer, this, 0))
}
class FreshObserver<T>(
private val delegate: Observer<in T>,
private val liveData: LiveData<*>,
private val sinceVersion: Int
) : Observer<T> {
override fun onChanged(t: T) {
if (liveData.version() > sinceVersion) {
delegate.onChanged(t)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (delegate != (other as FreshObserver<*>).delegate) return false
return true
}
override fun hashCode(): Int {
return delegate.hashCode()
}
}
因为我们需要访问LiveData的pcakage visibile方法getVersion()
进行比较,所以在包android.arch.lifecycle
或androidx.lifecycle
(AndroidX)中创建一个class:
LiveDataHiddenApi.kt
package androidx.lifecycle
fun LiveData<*>.version(): Int {
return this.getVersion()
}
我正在使用这个 EventWraper class 来自 MutableLiveDataGoogle 样本
/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
*/
public class Event<T> {
private T mContent;
private boolean hasBeenHandled = false;
public Event( T content) {
if (content == null) {
throw new IllegalArgumentException("null values in Event are not allowed.");
}
mContent = content;
}
@Nullable
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return mContent;
}
}
public boolean hasBeenHandled() {
return hasBeenHandled;
}
}
在 ViewModel 中:
/** expose Save LiveData Event */
public void newSaveEvent() {
saveEvent.setValue(new Event<>(true));
}
private final MutableLiveData<Event<Boolean>> saveEvent = new MutableLiveData<>();
public LiveData<Event<Boolean>> onSaveEvent() {
return saveEvent;
}
在Activity/Fragment
mViewModel
.onSaveEvent()
.observe(
getViewLifecycleOwner(),
booleanEvent -> {
if (booleanEvent != null)
final Boolean shouldSave = booleanEvent.getContentIfNotHandled();
if (shouldSave != null && shouldSave) saveData();
}
});
我也有同样的要求。我通过扩展 MutableLiveData
实现了这一点package com.idroidz.android.ion.util;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.Observer;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
import java.util.concurrent.atomic.AtomicBoolean;
public class VolatileMutableLiveData<T> extends MutableLiveData<T> {
private final AtomicBoolean mPending = new AtomicBoolean(false);
@MainThread
public void observe(LifecycleOwner owner, final Observer<T> observer) {
// Observe the internal MutableLiveData
mPending.set(false);
super.observe(owner, new Observer<T>() {
@Override
public void onChanged(@Nullable T t) {
if (mPending.get()) {
observer.onChanged(t);
}
}
});
}
@MainThread
public void setValue(@Nullable T t) {
mPending.set(true);
super.setValue(t);
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
public void call() {
setValue(null);
}
public void callFromThread() {
super.postValue(null);
}
}
忽略调用 android.arch.lifecycle.LiveData#observe
函数之前的数据。
class IgnoreHistoryLiveData<T> : MutableLiveData<T>() {
private val unactivedObservers = LinkedBlockingQueue<WrapperObserver<T>>()
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
val wo = WrapperObserver<T>(observer)
unactivedObservers.add(wo)
super.observe(owner, wo)
}
override fun setValue(value: T) {
while (unactivedObservers.isNotEmpty()) {
unactivedObservers.poll()?.actived = true
}
super.setValue(value)
}
}
private class WrapperObserver<T>(private val origin: Observer<T>) : Observer<T> {
var actived = false
override fun onChanged(t: T?) {
if (actived) {
origin.onChanged(t)
}
}
}
我创建了一个 LiveData 对象 FreshLiveData
,它仅在调用 setValue
或 postValue
.[=19= 后才向观察者发出 onChange
]
FreshLiveData.kt
/**
* A lifecycle-aware observable that emits only new data after subscription. Any data that has
* already been set, before the observable has subscribed, will be ignored.
*
* This avoids a common problem with events: on configuration change (like rotation, font change) an
* update can be emitted if the observer is active. This LiveData only calls the observable if
* there's an explicit call to setValue() or postValue().
*
* All observers will be notified of change(s).
*/
class FreshLiveData<T> : MutableLiveData<T>() {
private val observers = mutableMapOf<LifecycleOwner, FreshLiveDataObserver>()
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
@Suppress("UNCHECKED_CAST")
observer as Observer<T>
observers[owner].apply {
if (this == null) {
observers[owner] = FreshLiveDataObserver(observer).apply {
super.observe(owner, this)
}
} else {
add(observer)
}
}
}
override fun observeForever(observer: Observer<in T>) {
@Suppress("UNCHECKED_CAST")
observer as Observer<T>
observers[ProcessLifecycleOwner.get()].apply {
if (this == null) {
observers[ProcessLifecycleOwner.get()] = FreshLiveDataObserver(observer).apply {
super.observeForever(this)
}
} else {
add(observer)
}
}
}
override fun removeObservers(owner: LifecycleOwner) {
observers.remove(owner)
super.removeObservers(owner)
}
override fun removeObserver(observer: Observer<in T>) {
@Suppress("UNCHECKED_CAST")
observers.forEach { it.value.remove(observer as Observer<T>) }
super.removeObserver(observer)
}
@MainThread
override fun setValue(t: T?) {
observers.forEach { it.value.setPending() }
super.setValue(t)
}
override fun postValue(value: T) {
observers.forEach { it.value.setPending() }
super.postValue(value)
}
inner class FreshLiveDataObserver(observer: Observer<T>) : Observer<T> {
private val observers = mutableSetOf<Observer<T>>()
private val pending = AtomicBoolean(false)
init {
observers.add(observer)
}
fun add(observer: Observer<T>) = observers.add(observer)
fun remove(observer: Observer<T>) = observers.remove(observer)
fun setPending() = pending.set(true)
override fun onChanged(t: T) {
if (pending.compareAndSet(true, false)) {
observers.forEach { observer ->
observer.onChanged(t)
}
}
}
}
}
这里是将现有 LiveData
转换为 FreshLiveData
的扩展。
LiveDataExtensions.kt
@MainThread
fun <T> LiveData<T>.toFreshLiveData(): LiveData<T> {
val freshLiveData = FreshLiveData<T>()
val output = MediatorLiveData<T>()
// push any onChange from the LiveData to the FreshLiveData
output.addSource(this) { liveDataValue -> freshLiveData.value = liveDataValue }
// then push any onChange from the FreshLiveData out
output.addSource(freshLiveData) { freshLiveDataValue -> output.value = freshLiveDataValue }
return output
}
用法:
val liveData = MutableLiveData<Boolean>()
liveData.value = false
liveData.toFreshLiveData().observeForever {
// won't get called with `it = false` because the observe was setup after setting that livedata value
// will get called with `it = true` because the observer was setup before setting that livedata value
}
liveData.value = false
val freshLiveData = FreshLiveData<Boolean>()
freshLiveData.value = false
freshLiveData.observeForever {
// won't get called with `it = false` because the observe was setup after setting that livedata value
// will get called with `it = true` because the observer was setup before setting that livedata value
}
freshLiveData.value = true
没有理由将 LiveData 用作它不是的东西。如果你需要一个单独的行为(不保留以前的值的东西),那么你应该使用一个不保留以前的值的组件——而不是绕过它(“记住”它已经发出然后忘记发射等)
虽然没有其他可用的正确解决方案,所以我最终不得不自己写一个,因为没有其他选择可以推荐,所以我不得不推荐我为这个特定目的写的那个。
无论如何,你可以添加live-event
库:
implementation 'com.github.Zhuinden:live-event:1.2.0'
来自 Jitpack:maven { url "https://jitpack.io" }
那你可以做
private val eventEmitter = EventEmitter<WordController.Events>()
val controllerEvents: EventSource<WordController.Events> = eventEmitter
和
controllerEvents.observe(viewLifecycleOwner) { event: WordController.Events ->
when (event) {
is WordController.Events.NewWordAdded -> showToast("Added ${event.word}")
}.safe()
}
您可以使用 this 文章中描述的 EventLiveData。它将解决您的问题,我在 2 个生产项目中使用了它。 它是 LiveData 扩展,就像 SingleLiveData 但支持多个观察者。当观察者应该接收事件时,还允许自定义生命周期限制。例如,如果您不想在片段处于后台时接收事件。
EventLiveData 持有它永远观察的内部观察者,覆盖观察方法,绕过本机 LiveData 事件调度机制将观察者保存到内部映射中。
您可以copy/paste整个class或导入库,后者更方便
public class EventLiveData<T> extends LiveData<T> {
private final HashMap<Observer<? super T>, EventObserverWrapper> observers= new HashMap<>();
private final Observer<T> internalObserver;
int mActiveCount = 0;
public EventLiveData() {
this.internalObserver = (new Observer<T>() {
@Override
public void onChanged(T t) {
Iterator<Map.Entry<Observer<? super T>, EventObserverWrapper>> iterator = EventLiveData.this.observers.entrySet().iterator();
while (iterator.hasNext()){
EventObserverWrapper wrapper= iterator.next().getValue();
if(wrapper.shouldBeActive())
wrapper.getObserver().onChanged(t);
}
}
});
}
private void internalObserve(){
super.observeForever(this.internalObserver);
}
@MainThread
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
observe(owner, observer,STARTED,null);
}
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer, @NonNull Lifecycle.State minimumStateForSendingEvent) {
observe(owner, observer,minimumStateForSendingEvent,null);
}
@MainThread
public void observeInOnStart(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
observe(owner, observer,STARTED, Lifecycle.Event.ON_STOP);
}
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer, @NonNull Lifecycle.State minimumStateForSendingEvent, Lifecycle.Event removeObserverEvent) {
assertMainThread("observe");
assertNotNull(owner, "owner");
assertNotNull(observer, "observer");
assertNotNull(owner, "minimumStateForSendingEvent");
assertDestroyedState(minimumStateForSendingEvent);
assertMaximumEvent(removeObserverEvent);
if(minimumStateForSendingEvent==DESTROYED){
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement caller = stackTraceElements[3];
String className = caller.getClassName();
String methodName = caller.getMethodName();
IllegalArgumentException exception =
new IllegalArgumentException("State can not be equal to DESTROYED! : " +
"method " + className + "." + methodName +
", parameter " + minimumStateForSendingEvent);
throw sanitizeStackTrace(exception);
}
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
return;
}
EventLifecycleBoundEventObserver wrapper = new EventLifecycleBoundEventObserver(owner, observer);
wrapper.setMinimumStateForSendingEvent(minimumStateForSendingEvent);
wrapper.setMaximumEventForRemovingEvent(removeObserverEvent);
EventObserverWrapper existing = wrapper;
if(!observers.containsKey(observer))existing = observers.put(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
if (!super.hasObservers()) {
internalObserve();
}
}
@MainThread
@Override
public void observeForever(@NonNull Observer observer) {
assertMainThread("observeForever");
assertNotNull(observer, "observer");
EventAlwaysActiveEventObserver wrapper = new EventAlwaysActiveEventObserver(observer);
EventObserverWrapper existing = wrapper;
if(!observers.containsKey(observer))existing = observers.put(observer, wrapper);
if (existing != null && existing instanceof EventLiveData.EventLifecycleBoundEventObserver) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
if (!super.hasObservers()) {
internalObserve();
}
wrapper.activeStateChanged(true);
}
/**
{@inheritDoc}
*/
@Override
public void removeObservers(@NonNull LifecycleOwner owner) {
assertMainThread("removeObservers");
assertNotNull(owner, "owner");
Iterator<Map.Entry<Observer<? super T>, EventObserverWrapper>> iterator = EventLiveData.this.observers.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<Observer<? super T>, EventObserverWrapper> entry=iterator.next();
if(entry.getValue() instanceof EventLiveData.EventLifecycleBoundEventObserver){
EventLifecycleBoundEventObserver eventLifecycleBoundObserver =(EventLifecycleBoundEventObserver) entry.getValue();
if(eventLifecycleBoundObserver.isAttachedTo(owner))this.observers.remove(entry.getKey());
}
}
}
@Override
public void removeObserver(@NonNull Observer observer) {
assertMainThread("removeObserver");
assertNotNull(observer, "observer");
this.observers.remove(observer);
}
final protected void onActive() {}
protected void onActiveEvent() {}
protected void onInactive() {
}
@SuppressWarnings("WeakerAccess")
public boolean hasObservers() {
return observers.size() > 0;
}
@SuppressWarnings("WeakerAccess")
public boolean hasActiveObservers() {
return mActiveCount > 0;
}
class EventLifecycleBoundEventObserver extends EventObserverWrapper implements LifecycleObserver {
@NonNull
private final LifecycleOwner mOwner;
private Lifecycle.State MINIMUM_STATE_FOR_SENDING_EVENT= STARTED;
private Lifecycle.Event MAXIMUM_EVENT_FOR_REMOVING_EVENT= null;
EventLifecycleBoundEventObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
public Lifecycle.State getMinimumStateForSendingEvent() {
return MINIMUM_STATE_FOR_SENDING_EVENT;
}
public Lifecycle.Event getMaximumStateForRemovingEvent() {
return MAXIMUM_EVENT_FOR_REMOVING_EVENT;
}
public void setMaximumEventForRemovingEvent(Lifecycle.Event MAXIMUM_EVENT_FOR_REMOVING_EVENT) {
this.MAXIMUM_EVENT_FOR_REMOVING_EVENT = MAXIMUM_EVENT_FOR_REMOVING_EVENT;
}
public void setMinimumStateForSendingEvent(Lifecycle.State MINIMUM_STATE_FOR_SENDING_EVENT) {
this.MINIMUM_STATE_FOR_SENDING_EVENT = MINIMUM_STATE_FOR_SENDING_EVENT;
}
@Override
boolean shouldBeActive() {
Lifecycle.State state=mOwner.getLifecycle().getCurrentState();
return state.isAtLeast(MINIMUM_STATE_FOR_SENDING_EVENT);
}
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED||(MAXIMUM_EVENT_FOR_REMOVING_EVENT!=null&&MAXIMUM_EVENT_FOR_REMOVING_EVENT==event)) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
private abstract class EventObserverWrapper {
protected final Observer<? super T> mObserver;
boolean mActive;
EventObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive();
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver() {
}
public Observer<? super T> getObserver() {
return mObserver;
}
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = EventLiveData.this.mActiveCount == 0;
EventLiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
onActiveEvent();
}
if (EventLiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
}
}
private class EventAlwaysActiveEventObserver extends EventObserverWrapper {
EventAlwaysActiveEventObserver(Observer<? super T> observer) {
super(observer);
}
@Override
boolean shouldBeActive() {
return true;
}
}
private void assertDestroyedState(@NonNull Lifecycle.State minimumStateForSendingEvent){
if(minimumStateForSendingEvent==DESTROYED){
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement caller = stackTraceElements[3];
String className = caller.getClassName();
String methodName = caller.getMethodName();
IllegalArgumentException exception =new IllegalArgumentException("State can not be equal to "+ minimumStateForSendingEvent +"method " + className + "." + methodName +", parameter minimumStateForSendingEvent");
throw sanitizeStackTrace(exception);}
}
private void assertMaximumEvent(@NonNull Lifecycle.Event maximumEventForRemovingEvent){
if(maximumEventForRemovingEvent== Lifecycle.Event.ON_START||maximumEventForRemovingEvent== Lifecycle.Event.ON_CREATE
||maximumEventForRemovingEvent== Lifecycle.Event.ON_RESUME){
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement caller = stackTraceElements[3];
String className = caller.getClassName();
String methodName = caller.getMethodName();
IllegalArgumentException exception = new IllegalArgumentException("State can not be equal to "+maximumEventForRemovingEvent + "method " + className + "." + methodName +", parameter maximumEventForRemovingEvent" );
throw sanitizeStackTrace(exception);
}
}
private void assertMainThread(String methodName) {
boolean isUiThread = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? Looper.getMainLooper().isCurrentThread() : Thread.currentThread() == Looper.getMainLooper().getThread();
if (!isUiThread) {throw new IllegalStateException("Cannot invoke " + methodName + " on a background"+ " thread"); }
}
private void assertNotNull(Object value, String paramName) {
if (value == null) {throwParameterIsNullException(paramName); } }
private void throwParameterIsNullException(String paramName) {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement caller = stackTraceElements[3];
String className = caller.getClassName();
String methodName = caller.getMethodName();
IllegalArgumentException exception =
new IllegalArgumentException("Parameter specified as non-null is null: " +
"method " + className + "." + methodName +
", parameter " + paramName);
throw sanitizeStackTrace(exception);
}
private <T extends Throwable> T sanitizeStackTrace(T throwable) { return sanitizeStackTrace(throwable, this.getClass().getName());}
<T extends Throwable> T sanitizeStackTrace(T throwable, String classNameToDrop) {
StackTraceElement[] stackTrace = throwable.getStackTrace();
int size = stackTrace.length;
int lastIntrinsic = -1;
for (int i = 0; i < size; i++) {
if (classNameToDrop.equals(stackTrace[i].getClassName())) {lastIntrinsic = i; } }
StackTraceElement[] newStackTrace = Arrays.copyOfRange(stackTrace, lastIntrinsic + 1, size);
throwable.setStackTrace(newStackTrace);
return throwable;
}
}
更简单的解决方案是使用 EventLiveData 库:
implementation 'com.rugovit.eventlivedata:eventlivedata:1.0'
MutableEventLiveData<String> eventLiveData =new MutableEventLiveData<>();
viewModel.event.observe(this, Observer {
// ...
})
您可以像使用常规实时数据一样使用它。它是 livedata 的扩展,支持 livedata 的每一个特性。 与其他解决方案不同,它支持多个观察者。
Github link: https://github.com/rugovit/EventLiveData
根据 jurij-pitulja 的回答。
如果我们使用 kotlin coroutines
解决方案看起来像这样。
class Event<T>(private val content: T) {
var isHandled = false
private set
fun getContentIfNotHandled(): T? {
return takeIf { !isHandled }?.let {
isHandled = true
content
}
}
}
view model
内部 class 将 Flow.asLiveData()
替换为 emit new Event
val authResult: LiveData<Event<Result<AuthResponse>>> = _emailLiveData.switchMap { email ->
liveData{
repository.authRequest(email).collect{
emit(Event(it))
}
}
}
在 fragment
observer
方法
viewModel.authResult.observe(viewLifecycleOwner){
it.getContentIfNotHandled()?.run {
onAuthRequestComplete(this)
}
}
我编写了一个小实用程序 class,它使我能够快速阻止任何代码块的第一个 运行,如下所示:
class SkipFirstRun {
var canRun = false
fun run(codeToRunButNotFirstTime: () -> Unit) {
if (canRun) codeToRunButNotFirstTime()
canRun = true
}
}
然后在我的 Fragment 中声明它的一个实例变量:
class TrainingDetailCardInfoFragment
override val viewModel: TrainingViewModel by viewModels
private var skipInitialEndOfTraining = SkipFirstRun()
...
然后像这样使用它
viewModel.runningTask.observe(viewLifecycleOwner) { task ->
skipInitialEndOfTraining.run {
Log.debug("Playing stopped!")
sound.play(requireContext(), R.raw.endoftraining)
playBarViewModel.stop()
}
}
我反复遇到这个问题,这似乎是新 recommended guidance on consuming single UI events 的 Flow 和 的问题。我难以理解的原因是您需要使用事件的方式存在细微差别。
著名的 EventWrapper 正在处理一个用例:
开始观察 -> 事件发生 -> re-establish观察并确保我们不不会再收到那个事件了
但是如果首先观察的行为也触发了最新数据怎么办?似乎 none 的官方指南解决了如果你 post 有意向 LiveData 发送某些内容,它将在第一次观察时传播 如果尚未观察到 ,即使您使用的是 Flow 或 EventWrapper。
有:
- 您只想消费一次的事件 (SingleLiveEvent)
- 您希望多个观察者只使用一次的事件(EventWrapper,或使用 StateFlow + 列表 + ID 的当前架构指南,带有大量疯狂的样板)
- 并且只有在你开始观察一个或多个观察者之后你才想消费的事件(这个问题)
当视图模型在两个片段之间共享时,这尤其是一个问题。即片段 A post 是实时数据的一个值,然后点击几下或稍后网络响应片段 B 出现在屏幕上并开始观察相同的共享实时数据,您希望它丢弃最新结果并仅使用未来的结果. 在这种情况下,您将 LiveData 用于其设计目的之外的事情,并且需要查看条件以了解为什么片段 B 不应该关心最新结果并使用逻辑 在观察中过滤掉它。