Restrict some Handler to current Looper only.

They currently fall back to the main Looper if the current thread
doesn't have a Looper. All the changed Handlers are guaranteed to
be created on a thread with a Looper (mostly the ExoPlayer playback
Looper) and thus can make this stricter assumption. This makes it
easier to reason about the code as there are no ambiguities as to which
thread the Handler is running on.

PiperOrigin-RevId: 317334503
This commit is contained in:
tonihei 2020-06-19 18:36:20 +01:00 committed by Andrew Lewis
parent 7d66865d20
commit c5c4c87728
14 changed files with 62 additions and 38 deletions

View file

@ -393,6 +393,32 @@ public final class Util {
return concatenation;
}
/**
* Creates a {@link Handler} on the current {@link Looper} thread.
*
* @throws IllegalStateException If the current thread doesn't have a {@link Looper}.
*/
public static Handler createHandlerForCurrentLooper() {
return createHandlerForCurrentLooper(/* callback= */ null);
}
/**
* Creates a {@link Handler} with the specified {@link Handler.Callback} on the current {@link
* Looper} thread.
*
* <p>The method accepts partially initialized objects as callback under the assumption that the
* Handler won't be used to send messages until the callback is fully initialized.
*
* @param callback A {@link Handler.Callback}. May be a partially initialized class, or null if no
* callback is required.
* @return A {@link Handler} with the specified callback on the current {@link Looper} thread.
* @throws IllegalStateException If the current thread doesn't have a {@link Looper}.
*/
public static Handler createHandlerForCurrentLooper(
@Nullable Handler.@UnknownInitialization Callback callback) {
return createHandler(Assertions.checkStateNotNull(Looper.myLooper()), callback);
}
/**
* Creates a {@link Handler} on the current {@link Looper} thread.
*
@ -405,9 +431,10 @@ public final class Util {
/**
* Creates a {@link Handler} with the specified {@link Handler.Callback} on the current {@link
* Looper} thread. The method accepts partially initialized objects as callback under the
* assumption that the Handler won't be used to send messages until the callback is fully
* initialized.
* Looper} thread.
*
* <p>The method accepts partially initialized objects as callback under the assumption that the
* Handler won't be used to send messages until the callback is fully initialized.
*
* <p>If the current thread doesn't have a {@link Looper}, the application's main thread {@link
* Looper} is used.
@ -423,9 +450,10 @@ public final class Util {
/**
* Creates a {@link Handler} with the specified {@link Handler.Callback} on the specified {@link
* Looper} thread. The method accepts partially initialized objects as callback under the
* assumption that the Handler won't be used to send messages until the callback is fully
* initialized.
* Looper} thread.
*
* <p>The method accepts partially initialized objects as callback under the assumption that the
* Handler won't be used to send messages until the callback is fully initialized.
*
* @param looper A {@link Looper} to run the callback on.
* @param callback A {@link Handler.Callback}. May be a partially initialized class, or null if no

View file

@ -48,7 +48,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
@CallSuper
protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
this.mediaTransferListener = mediaTransferListener;
eventHandler = Util.createHandlerForCurrentOrMainLooper();
eventHandler = Util.createHandlerForCurrentLooper();
}
@Override

View file

@ -192,7 +192,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
.onContinueLoadingRequested(ProgressiveMediaPeriod.this);
}
};
handler = Util.createHandlerForCurrentOrMainLooper();
handler = Util.createHandlerForCurrentLooper();
sampleQueueTrackIds = new TrackId[0];
sampleQueues = new SampleQueue[0];
pendingResetPositionUs = C.TIME_UNSET;

View file

@ -336,7 +336,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
* events on the external event listener thread.
*/
public ComponentListener() {
playerHandler = Util.createHandlerForCurrentOrMainLooper();
playerHandler = Util.createHandlerForCurrentLooper();
}
/** Releases the component listener. */

View file

@ -1763,7 +1763,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
private final Handler handler;
public OnFrameRenderedListenerV23(MediaCodec codec) {
handler = Util.createHandlerForCurrentOrMainLooper(/* callback= */ this);
handler = Util.createHandlerForCurrentLooper(/* callback= */ this);
codec.setOnFrameRenderedListener(/* listener= */ this, handler);
}

View file

@ -599,7 +599,7 @@ public final class ClippingMediaSourceTest {
testRunner.runOnPlaybackThread(
() ->
clippingMediaSource.addEventListener(
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
new MediaSourceEventListener() {
@Override
public void onDownstreamFormatChanged(

View file

@ -413,7 +413,7 @@ public final class ConcatenatingMediaSourceTest {
() ->
mediaSource.addMediaSource(
createFakeMediaSource(),
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
runnableInvoked::countDown));
runnableInvoked.await(MediaSourceTestRunner.TIMEOUT_MS, TimeUnit.MILLISECONDS);
dummyMainThread.release();
@ -430,7 +430,7 @@ public final class ConcatenatingMediaSourceTest {
() ->
mediaSource.addMediaSources(
Arrays.asList(new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
runnableInvoked::countDown));
runnableInvoked.await(MediaSourceTestRunner.TIMEOUT_MS, TimeUnit.MILLISECONDS);
dummyMainThread.release();
@ -448,7 +448,7 @@ public final class ConcatenatingMediaSourceTest {
mediaSource.addMediaSource(
/* index */ 0,
createFakeMediaSource(),
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
runnableInvoked::countDown));
runnableInvoked.await(MediaSourceTestRunner.TIMEOUT_MS, TimeUnit.MILLISECONDS);
dummyMainThread.release();
@ -466,7 +466,7 @@ public final class ConcatenatingMediaSourceTest {
mediaSource.addMediaSources(
/* index */ 0,
Arrays.asList(new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
runnableInvoked::countDown));
runnableInvoked.await(MediaSourceTestRunner.TIMEOUT_MS, TimeUnit.MILLISECONDS);
dummyMainThread.release();
@ -483,9 +483,7 @@ public final class ConcatenatingMediaSourceTest {
() -> {
mediaSource.addMediaSource(createFakeMediaSource());
mediaSource.removeMediaSource(
/* index */ 0,
Util.createHandlerForCurrentOrMainLooper(),
runnableInvoked::countDown);
/* index */ 0, Util.createHandlerForCurrentLooper(), runnableInvoked::countDown);
});
runnableInvoked.await(MediaSourceTestRunner.TIMEOUT_MS, TimeUnit.MILLISECONDS);
dummyMainThread.release();
@ -505,7 +503,7 @@ public final class ConcatenatingMediaSourceTest {
mediaSource.moveMediaSource(
/* fromIndex */ 1, /* toIndex */
0,
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
runnableInvoked::countDown);
});
runnableInvoked.await(MediaSourceTestRunner.TIMEOUT_MS, TimeUnit.MILLISECONDS);
@ -523,9 +521,7 @@ public final class ConcatenatingMediaSourceTest {
dummyMainThread.runOnMainThread(
() ->
mediaSource.addMediaSource(
createFakeMediaSource(),
Util.createHandlerForCurrentOrMainLooper(),
timelineGrabber));
createFakeMediaSource(), Util.createHandlerForCurrentLooper(), timelineGrabber));
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
assertThat(timeline.getWindowCount()).isEqualTo(1);
} finally {
@ -544,7 +540,7 @@ public final class ConcatenatingMediaSourceTest {
mediaSource.addMediaSources(
Arrays.asList(
new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
timelineGrabber));
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
assertThat(timeline.getWindowCount()).isEqualTo(2);
@ -564,7 +560,7 @@ public final class ConcatenatingMediaSourceTest {
mediaSource.addMediaSource(
/* index */ 0,
createFakeMediaSource(),
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
timelineGrabber));
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
assertThat(timeline.getWindowCount()).isEqualTo(1);
@ -585,7 +581,7 @@ public final class ConcatenatingMediaSourceTest {
/* index */ 0,
Arrays.asList(
new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
timelineGrabber));
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
assertThat(timeline.getWindowCount()).isEqualTo(2);
@ -606,7 +602,7 @@ public final class ConcatenatingMediaSourceTest {
dummyMainThread.runOnMainThread(
() ->
mediaSource.removeMediaSource(
/* index */ 0, Util.createHandlerForCurrentOrMainLooper(), timelineGrabber));
/* index */ 0, Util.createHandlerForCurrentLooper(), timelineGrabber));
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
assertThat(timeline.getWindowCount()).isEqualTo(0);
} finally {
@ -632,7 +628,7 @@ public final class ConcatenatingMediaSourceTest {
mediaSource.moveMediaSource(
/* fromIndex */ 1, /* toIndex */
0,
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
timelineGrabber));
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
assertThat(timeline.getWindowCount()).isEqualTo(2);
@ -654,7 +650,7 @@ public final class ConcatenatingMediaSourceTest {
mediaSource.moveMediaSource(
/* currentIndex= */ 0,
/* newIndex= */ 1,
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
callbackCalledCondition::countDown);
mediaSource.releaseSource(caller);
});
@ -907,7 +903,7 @@ public final class ConcatenatingMediaSourceTest {
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
dummyMainThread.runOnMainThread(
() -> mediaSource.clear(Util.createHandlerForCurrentOrMainLooper(), timelineGrabber));
() -> mediaSource.clear(Util.createHandlerForCurrentLooper(), timelineGrabber));
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
assertThat(timeline.isEmpty()).isTrue();
@ -1059,7 +1055,7 @@ public final class ConcatenatingMediaSourceTest {
() ->
mediaSource.setShuffleOrder(
new ShuffleOrder.UnshuffledShuffleOrder(/* length= */ 0),
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
runnableInvoked::countDown));
runnableInvoked.await(MediaSourceTestRunner.TIMEOUT_MS, TimeUnit.MILLISECONDS);
dummyMainThread.release();
@ -1079,7 +1075,7 @@ public final class ConcatenatingMediaSourceTest {
() ->
mediaSource.setShuffleOrder(
new ShuffleOrder.UnshuffledShuffleOrder(/* length= */ 3),
Util.createHandlerForCurrentOrMainLooper(),
Util.createHandlerForCurrentLooper(),
timelineGrabber));
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
assertThat(timeline.getFirstWindowIndex(/* shuffleModeEnabled= */ true)).isEqualTo(0);

View file

@ -680,7 +680,7 @@ public final class DashMediaSource extends BaseMediaSource {
} else {
dataSource = manifestDataSourceFactory.createDataSource();
loader = new Loader("Loader:DashMediaSource");
handler = Util.createHandlerForCurrentOrMainLooper();
handler = Util.createHandlerForCurrentLooper();
startLoadingManifest();
}
}

View file

@ -105,7 +105,7 @@ public final class PlayerEmsgHandler implements Handler.Callback {
this.allocator = allocator;
manifestPublishTimeToExpiryTimeUs = new TreeMap<>();
handler = Util.createHandlerForCurrentOrMainLooper(/* callback= */ this);
handler = Util.createHandlerForCurrentLooper(/* callback= */ this);
decoder = new EventMessageDecoder();
lastLoadedChunkEndTimeUs = C.TIME_UNSET;
lastLoadedChunkEndTimeBeforeRefreshUs = C.TIME_UNSET;

View file

@ -227,7 +227,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@SuppressWarnings("nullness:methodref.receiver.bound.invalid")
Runnable onTracksEndedRunnable = this::onTracksEnded;
this.onTracksEndedRunnable = onTracksEndedRunnable;
handler = Util.createHandlerForCurrentOrMainLooper();
handler = Util.createHandlerForCurrentLooper();
lastSeekPositionUs = positionUs;
pendingResetPositionUs = positionUs;
}

View file

@ -121,7 +121,7 @@ public final class DefaultHlsPlaylistTracker
Uri initialPlaylistUri,
EventDispatcher eventDispatcher,
PrimaryPlaylistListener primaryPlaylistListener) {
this.playlistRefreshHandler = Util.createHandlerForCurrentOrMainLooper();
this.playlistRefreshHandler = Util.createHandlerForCurrentLooper();
this.eventDispatcher = eventDispatcher;
this.primaryPlaylistListener = primaryPlaylistListener;
ParsingLoadable<HlsPlaylist> masterPlaylistLoadable =

View file

@ -620,7 +620,7 @@ public final class SsMediaSource extends BaseMediaSource
manifestDataSource = manifestDataSourceFactory.createDataSource();
manifestLoader = new Loader("Loader:Manifest");
manifestLoaderErrorThrower = manifestLoader;
manifestRefreshHandler = Util.createHandlerForCurrentOrMainLooper();
manifestRefreshHandler = Util.createHandlerForCurrentLooper();
startLoadingManifest();
}
}

View file

@ -162,7 +162,7 @@ public class FakeMediaPeriod implements MediaPeriod {
/* mediaEndTimeUs = */ C.TIME_UNSET);
prepareCallback = callback;
if (deferOnPrepared) {
playerHandler = Util.createHandlerForCurrentOrMainLooper();
playerHandler = Util.createHandlerForCurrentLooper();
} else {
finishPreparation();
}

View file

@ -176,7 +176,7 @@ public class FakeMediaSource extends BaseMediaSource {
drmSessionManager.prepare();
preparedSource = true;
releasedSource = false;
sourceInfoRefreshHandler = Util.createHandlerForCurrentOrMainLooper();
sourceInfoRefreshHandler = Util.createHandlerForCurrentLooper();
if (timeline != null) {
finishSourcePreparation(/* sendManifestLoadEvents= */ true);
}