Ensure onEvents is called when listener is removed.

When a listener is removed or released we may not have called
onEvents for events that happened before this point. To ensure
listeners don't miss events we need to trigger a final onEvents
with all events we have happened so far (if any).

PiperOrigin-RevId: 346553030
This commit is contained in:
tonihei 2020-12-09 16:07:44 +00:00 committed by Ian Baker
parent e7f5912677
commit f18d81f8a8
2 changed files with 14 additions and 6 deletions

View File

@ -156,7 +156,7 @@ public final class ListenerSet<T, E extends MutableFlags> {
public void remove(T listener) {
for (ListenerHolder<T, E> listenerHolder : listeners) {
if (listenerHolder.listener.equals(listener)) {
listenerHolder.release();
listenerHolder.release(iterationFinishedEvent);
listeners.remove(listenerHolder);
}
}
@ -221,7 +221,7 @@ public final class ListenerSet<T, E extends MutableFlags> {
*/
public void release() {
for (ListenerHolder<T, E> listenerHolder : listeners) {
listenerHolder.release();
listenerHolder.release(iterationFinishedEvent);
}
listeners.clear();
released = true;
@ -245,6 +245,7 @@ public final class ListenerSet<T, E extends MutableFlags> {
@Nonnull public final T listener;
private E eventsFlags;
private boolean needsIterationFinishedEvent;
private boolean released;
public ListenerHolder(@Nonnull T listener, Supplier<E> eventFlagSupplier) {
@ -252,26 +253,31 @@ public final class ListenerSet<T, E extends MutableFlags> {
this.eventsFlags = eventFlagSupplier.get();
}
public void release() {
public void release(IterationFinishedEvent<T, E> event) {
released = true;
if (needsIterationFinishedEvent) {
event.invoke(listener, eventsFlags);
}
}
public void invoke(int eventFlag, Event<T> event) {
if (!released) {
event.invoke(listener);
if (eventFlag != C.INDEX_UNSET) {
eventsFlags.add(eventFlag);
}
needsIterationFinishedEvent = true;
event.invoke(listener);
}
}
public void iterationFinished(
Supplier<E> eventFlagSupplier, IterationFinishedEvent<T, E> event) {
if (!released) {
if (!released && needsIterationFinishedEvent) {
// Reset flags before invoking the listener to ensure we keep all new flags that are set by
// recursive events triggered from this callback.
E flagToNotify = eventsFlags;
eventsFlags = eventFlagSupplier.get();
needsIterationFinishedEvent = false;
event.invoke(listener, flagToNotify);
}
}

View File

@ -297,10 +297,11 @@ public class ListenerSetTest {
// Listener2 shouldn't even get this event as it's removed before the event can be invoked.
listenerSet.sendEvent(EVENT_ID_1, TestListener::callback1);
listenerSet.remove(listener1);
listenerSet.sendEvent(EVENT_ID_1, TestListener::callback1);
listenerSet.sendEvent(EVENT_ID_2, TestListener::callback2);
ShadowLooper.runMainLooperToNextTask();
verify(listener1).callback1();
verify(listener1).iterationFinished(Flags.create(EVENT_ID_1));
verifyNoMoreInteractions(listener1, listener2);
}
@ -348,6 +349,7 @@ public class ListenerSetTest {
ShadowLooper.runMainLooperToNextTask();
verify(listener1).callback1();
verify(listener1).iterationFinished(Flags.create(EVENT_ID_1));
verifyNoMoreInteractions(listener1, listener2);
}