diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManager.java index ca8bf45eec..c9054ac71e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManager.java @@ -22,6 +22,7 @@ import static java.lang.Math.abs; import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; +import android.os.Handler; import android.os.Looper; import android.os.Process; import androidx.annotation.IntDef; @@ -327,6 +328,7 @@ public final class DefaultPreloadManager extends BasePreloadManager { private final TrackSelector trackSelector; private final PlaybackLooperProvider preloadLooperProvider; private final PreloadMediaSource.Factory preloadMediaSourceFactory; + private final Handler preloadHandler; private final boolean deprecatedConstructorCalled; private DefaultPreloadManager(Builder builder) { @@ -341,6 +343,7 @@ public final class DefaultPreloadManager extends BasePreloadManager { trackSelector = builder.trackSelectorFactory.createTrackSelector(builder.context); BandwidthMeter bandwidthMeter = builder.bandwidthMeterSupplier.get(); trackSelector.init(() -> {}, bandwidthMeter); + Looper preloadLooper = preloadLooperProvider.obtainLooper(); preloadMediaSourceFactory = new PreloadMediaSource.Factory( builder.mediaSourceFactorySupplier.get(), @@ -349,7 +352,8 @@ public final class DefaultPreloadManager extends BasePreloadManager { bandwidthMeter, rendererCapabilitiesList.getRendererCapabilities(), builder.loadControlSupplier.get().getAllocator(), - preloadLooperProvider.obtainLooper()); + preloadLooper); + preloadHandler = Util.createHandler(preloadLooper, /* callback= */ null); deprecatedConstructorCalled = false; } @@ -370,6 +374,7 @@ public final class DefaultPreloadManager extends BasePreloadManager { rendererCapabilitiesListFactory.createRendererCapabilitiesList(); this.preloadLooperProvider = new PlaybackLooperProvider(preloadLooper); this.trackSelector = trackSelector; + Looper obtainedPreloadLooper = preloadLooperProvider.obtainLooper(); preloadMediaSourceFactory = new PreloadMediaSource.Factory( mediaSourceFactory, @@ -378,7 +383,8 @@ public final class DefaultPreloadManager extends BasePreloadManager { bandwidthMeter, rendererCapabilitiesList.getRendererCapabilities(), allocator, - preloadLooperProvider.obtainLooper()); + obtainedPreloadLooper); + preloadHandler = Util.createHandler(obtainedPreloadLooper, /* callback= */ null); deprecatedConstructorCalled = true; } @@ -418,13 +424,16 @@ public final class DefaultPreloadManager extends BasePreloadManager { @Override protected void releaseInternal() { - rendererCapabilitiesList.release(); - preloadLooperProvider.releaseLooper(); - if (!deprecatedConstructorCalled) { - // TODO: Remove the property deprecatedConstructorCalled and release the TrackSelector anyway - // after the deprecated constructor is removed. - trackSelector.release(); - } + preloadHandler.post( + () -> { + rendererCapabilitiesList.release(); + if (!deprecatedConstructorCalled) { + // TODO: Remove the property deprecatedConstructorCalled and release the TrackSelector + // anyway after the deprecated constructor is removed. + trackSelector.release(); + } + preloadLooperProvider.releaseLooper(); + }); } private static final class RankingDataComparator implements Comparator { diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java index 0470a0ab48..2f351868ea 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java @@ -830,7 +830,8 @@ public class DefaultPreloadManagerTest { } @Test - public void release_returnZeroCount_sourcesAndRendererCapabilitiesListReleased() { + public void release_returnZeroCount_sourcesAndRendererCapabilitiesListReleased() + throws Exception { TargetPreloadStatusControl targetPreloadStatusControl = rankingData -> new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED); MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class); @@ -853,11 +854,13 @@ public class DefaultPreloadManagerTest { underlyingRenderers.add(fakeAudioRenderer); return underlyingRenderers.toArray(new Renderer[2]); }; + HandlerThread preloadThread = new HandlerThread("preload"); + preloadThread.start(); DefaultPreloadManager preloadManager = new DefaultPreloadManager.Builder(context, targetPreloadStatusControl) .setMediaSourceFactory(mockMediaSourceFactory) .setRenderersFactory(renderersFactory) - .setPreloadLooper(Util.getCurrentOrMainLooper()) + .setPreloadLooper(preloadThread.getLooper()) .build(); MediaItem.Builder mediaItemBuilder = new MediaItem.Builder(); MediaItem mediaItem1 = @@ -885,16 +888,19 @@ public class DefaultPreloadManagerTest { preloadManager.add(mediaItem1, /* rankingData= */ 1); preloadManager.add(mediaItem2, /* rankingData= */ 2); preloadManager.invalidate(); + shadowOf(preloadThread.getLooper()).idle(); shadowOf(Looper.getMainLooper()).idle(); preloadManager.release(); - shadowOf(Looper.getMainLooper()).idle(); + shadowOf(preloadThread.getLooper()).idle(); assertThat(preloadManager.getSourceCount()).isEqualTo(0); assertThat(internalSourceToReleaseReferenceByMediaId).containsExactly("mediaId1", "mediaId2"); for (FakeRenderer renderer : underlyingRenderers) { assertThat(renderer.isReleased).isTrue(); } + + preloadThread.quit(); } private static class TestPreloadManagerListener implements BasePreloadManager.Listener {