diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcher.java b/library/core/src/main/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcher.java index a3ee0ac9ef..c58221a12c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcher.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcher.java @@ -103,6 +103,11 @@ public class MediaSourceEventDispatcher { * event dispatched to {@code listenerClass} will only be passed to the {@code eventListener} * once. * + *
NOTE: This doesn't interact well with hierarchies of listener interfaces. If a + * listener is registered with a super-class type then it will only receive events dispatched + * directly to that super-class type. Similarly, if a listener is registered with a sub-class type + * then it will only receive events dispatched directly to that sub-class. + * * @param handler A handler on the which listener events will be posted. * @param eventListener The listener to be added. * @param listenerClass The type used to register the listener. Can be a superclass of {@code diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcherTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcherTest.java index 0063c4e738..5e5a6be7c6 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcherTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcherTest.java @@ -18,15 +18,18 @@ package com.google.android.exoplayer2.util; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.os.Handler; import android.os.Looper; +import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSourceEventListener; import org.junit.Before; import org.junit.Rule; @@ -123,6 +126,33 @@ public class MediaSourceEventDispatcherTest { verify(mediaAndDrmEventListener, never()).onDrmKeysLoaded(); } + @Test + public void listenerDoesntReceiveEventsDispatchedToSubclass() { + SubclassListener subclassListener = mock(SubclassListener.class); + eventDispatcher.addEventListener( + new Handler(Looper.getMainLooper()), subclassListener, MediaSourceEventListener.class); + + eventDispatcher.dispatch(SubclassListener::subclassMethod, SubclassListener.class); + + // subclassListener can handle the call to subclassMethod, but it isn't called because + // it was registered 'as-a' MediaSourceEventListener, not SubclassListener. + verify(subclassListener, never()).subclassMethod(anyInt(), any()); + } + + @Test + public void listenerDoesntReceiveEventsDispatchedToSuperclass() { + SubclassListener subclassListener = mock(SubclassListener.class); + eventDispatcher.addEventListener( + new Handler(Looper.getMainLooper()), subclassListener, SubclassListener.class); + + eventDispatcher.dispatch( + MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class); + + // subclassListener 'is-a' a MediaSourceEventListener, but it isn't called because the event + // is dispatched specifically to listeners registered as MediaSourceEventListener. + verify(subclassListener, never()).onMediaPeriodCreated(anyInt(), any()); + } + @Test public void listenersAreCopiedToNewDispatcher() { eventDispatcher.addEventListener( @@ -185,4 +215,8 @@ public class MediaSourceEventDispatcherTest { private interface MediaAndDrmEventListener extends MediaSourceEventListener, DrmSessionEventListener {} + + private interface SubclassListener extends MediaSourceEventListener { + void subclassMethod(int windowIndex, @Nullable MediaPeriodId mediaPeriodId); + } }