@Ningan, my idea is to provide a base class of one-time events to be inherited into client code, and wrap each observer received from the client code into an internal one that operates on the base class.
OnceEventLiveData.java (also available as Github Gist):
public class OnceEventLiveData<T extends OnceEventLiveData.OnceEvent> extends MutableLiveData<T> {
@Override
public void observe(@NonNull final LifecycleOwner owner,
@NonNull final Observer<? super T> observer) {
super.observe(owner, new OnceEventObserver<>(observer));
}
@Override
public void observeForever(@NonNull final Observer<? super T> observer) {
super.observeForever(new OnceEventObserver<>(observer));
}
@Override
public void removeObserver(@NonNull final Observer<? super T> observer) {
super.removeObserver(new OnceEventObserver<>(observer));
}
private static class OnceEventObserver<T extends OnceEvent> implements Observer<T> {
@NonNull
private final Observer<OnceEvent> mObserver;
OnceEventObserver(@NonNull final Observer<? super T> observer) {
//noinspection unchecked
mObserver = (Observer<OnceEvent>) observer;
}
@Override
public void onChanged(@NonNull OnceEvent event) {
if (!event.mIsConsumed.get()) {
mObserver.onChanged(event);
event.mIsConsumed.getAndSet(true);
}
}
@Override
public boolean equals(@Nullable final Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
OnceEventObserver<?> that = (OnceEventObserver<?>) other;
return mObserver.equals(that.mObserver);
}
@Override
public int hashCode() {
return mObserver.hashCode();
}
}
/**
* Declares a base class for any consumer data that be intended for use into this LiveData
* implementation.
*/
public abstract static class OnceEvent {
@NonNull
private final AtomicBoolean mIsConsumed = new AtomicBoolean(false);
}
}
Abstract OnceEvent class encapsulates it own "is consumed" status. It makes necessary to inherit the base class and create separate classes for each type of event.
On the other hand, the View and ViewModel do not change compared to use regular LiveData, you can add and remove any needed observers; all one-time processing logic is well-encapsulated into nested classes. It allows you to use OnceEventLiveData as any others LiveData, and to be focused only on the meaning of your custom event.
Usage in ViewModel :
class EventViewModel extends ViewModel {
public static class CustomEvent extends OnceEventLiveData.OnceEvent {
// any needed event's payload here
}
@NonNull
private final OnceEventLiveData<CustomEvent> mEvent = new OnceEventLiveData<>();
@NonNull
public LiveData<CustomEvent> getEvent() {
return mEvent;
}
public void doSomething() {
// business logic that beget the event
mEvent.setValue(new CustomEvent());
}
}
Usage in Activity/Fragment :
class EventActivity extends AppCompatActivity implements Observer<CustomEvent> {
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventViewModel eventViewModel = new ViewModelProvider(this).get(EventViewModel.class);
eventViewModel.getEvent().observe(this, this);
}
@Override
public void onChanged(@NonNull CustomEvent event) {
// eevent handling logic here
}
}