diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java index cadd293bee..9ddbac1007 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java @@ -102,14 +102,11 @@ public final class AdsMediaSource extends CompositeMediaSource { @Nullable private final Handler eventHandler; @Nullable private final EventListener eventListener; private final Handler mainHandler; - private final ComponentListener componentListener; private final Map> deferredMediaPeriodByAdMediaSource; private final Timeline.Period period; - private Handler playerHandler; - private volatile boolean released; - // Accessed on the player thread. + private ComponentListener componentListener; private Timeline contentTimeline; private Object contentManifest; private AdPlaybackState adPlaybackState; @@ -192,7 +189,6 @@ public final class AdsMediaSource extends CompositeMediaSource { this.eventHandler = eventHandler; this.eventListener = eventListener; mainHandler = new Handler(Looper.getMainLooper()); - componentListener = new ComponentListener(); deferredMediaPeriodByAdMediaSource = new HashMap<>(); period = new Timeline.Period(); adGroupMediaSources = new MediaSource[0][]; @@ -204,9 +200,9 @@ public final class AdsMediaSource extends CompositeMediaSource { public void prepareSource(final ExoPlayer player, boolean isTopLevelSource, Listener listener) { super.prepareSource(player, isTopLevelSource, listener); Assertions.checkArgument(isTopLevelSource); - Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE); + final ComponentListener componentListener = new ComponentListener(); this.listener = listener; - playerHandler = new Handler(); + this.componentListener = componentListener; prepareChildSource(new MediaPeriodId(/* periodIndex= */ 0), contentMediaSource); mainHandler.post(new Runnable() { @Override @@ -258,13 +254,27 @@ public final class AdsMediaSource extends CompositeMediaSource { @Override public void releasePeriod(MediaPeriod mediaPeriod) { - ((DeferredMediaPeriod) mediaPeriod).releasePeriod(); + DeferredMediaPeriod deferredMediaPeriod = (DeferredMediaPeriod) mediaPeriod; + List mediaPeriods = + deferredMediaPeriodByAdMediaSource.get(deferredMediaPeriod.mediaSource); + if (mediaPeriods != null) { + mediaPeriods.remove(deferredMediaPeriod); + } + deferredMediaPeriod.releasePeriod(); } @Override public void releaseSource() { super.releaseSource(); - released = true; + componentListener.release(); + componentListener = null; + deferredMediaPeriodByAdMediaSource.clear(); + contentTimeline = null; + contentManifest = null; + adPlaybackState = null; + adGroupMediaSources = new MediaSource[0][]; + adDurationsUs = new long[0][]; + listener = null; mainHandler.post(new Runnable() { @Override public void run() { @@ -301,20 +311,6 @@ public final class AdsMediaSource extends CompositeMediaSource { maybeUpdateSourceInfo(); } - private void onLoadError(final IOException error) { - Log.w(TAG, "Ad load error", error); - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - if (!released) { - eventListener.onAdLoadError(error); - } - } - }); - } - } - private void onContentSourceInfoRefreshed(Timeline timeline, Object manifest) { contentTimeline = timeline; contentManifest = manifest; @@ -349,6 +345,23 @@ public final class AdsMediaSource extends CompositeMediaSource { /** Listener for component events. All methods are called on the main thread. */ private final class ComponentListener implements AdsLoader.EventListener { + private final Handler playerHandler; + private volatile boolean released; + + /** + * Creates new listener which forwards ad playback states on the creating thread and all other + * events on the external event listener thread. + */ + public ComponentListener() { + playerHandler = new Handler(); + } + + /** Releases the component listener. */ + public void release() { + released = true; + playerHandler.removeCallbacksAndMessages(null); + } + @Override public void onAdPlaybackState(final AdPlaybackState adPlaybackState) { if (released) { @@ -367,6 +380,9 @@ public final class AdsMediaSource extends CompositeMediaSource { @Override public void onAdClicked() { + if (released) { + return; + } if (eventHandler != null && eventListener != null) { eventHandler.post(new Runnable() { @Override @@ -381,6 +397,9 @@ public final class AdsMediaSource extends CompositeMediaSource { @Override public void onAdTapped() { + if (released) { + return; + } if (eventHandler != null && eventListener != null) { eventHandler.post(new Runnable() { @Override @@ -398,15 +417,18 @@ public final class AdsMediaSource extends CompositeMediaSource { if (released) { return; } - playerHandler.post(new Runnable() { - @Override - public void run() { - if (released) { - return; - } - AdsMediaSource.this.onLoadError(error); - } - }); + Log.w(TAG, "Ad load error", error); + if (eventHandler != null && eventListener != null) { + eventHandler.post( + new Runnable() { + @Override + public void run() { + if (!released) { + eventListener.onAdLoadError(error); + } + } + }); + } } }