diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Player.java b/library/common/src/main/java/com/google/android/exoplayer2/Player.java index 1cad966edd..9058ac9f60 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Player.java @@ -53,6 +53,10 @@ import java.util.List; * A media player interface defining traditional high-level functionality, such as the ability to * play, pause, seek and query properties of the currently playing media. * + *
All methods must be called from a single {@linkplain #getApplicationLooper() application + * thread} unless indicated otherwise. Callbacks in registered listeners are called on the same + * thread. + * *
This interface includes some convenience methods that can be implemented by calling other * methods in the interface. {@link BasePlayer} implements these convenience methods so inheriting * {@link BasePlayer} is recommended when implementing the interface so that only the minimal set of @@ -1546,6 +1550,8 @@ public interface Player { /** * Returns the {@link Looper} associated with the application thread that's used to access the * player and on which player events are received. + * + *
This method can be called from any thread. */ Looper getApplicationLooper(); @@ -1555,6 +1561,8 @@ public interface Player { *
The listener's methods will be called on the thread associated with {@link * #getApplicationLooper()}. * + *
This method can be called from any thread. + * * @param listener The listener to register. */ void addListener(Listener listener); diff --git a/library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java b/library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java index 66a3d8ced9..ae6c89dbc1 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java @@ -1981,8 +1981,7 @@ public abstract class SimpleBasePlayer extends BasePlayer { @Override public final void removeListener(Listener listener) { - // Don't verify application thread. We allow calls to this method from any thread. - checkNotNull(listener); + verifyApplicationThreadAndInitState(); listeners.remove(listener); } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/util/ListenerSet.java b/library/common/src/main/java/com/google/android/exoplayer2/util/ListenerSet.java index 52c7c0bf9a..e1a6ec6504 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/util/ListenerSet.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/util/ListenerSet.java @@ -15,9 +15,12 @@ */ package com.google.android.exoplayer2.util; +import static com.google.android.exoplayer2.util.Assertions.checkState; + import android.os.Looper; import android.os.Message; import androidx.annotation.CheckResult; +import androidx.annotation.GuardedBy; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import java.util.ArrayDeque; @@ -33,6 +36,9 @@ import org.checkerframework.checker.nullness.qual.NonNull; *
Events are also guaranteed to be only sent to the listeners registered at the time the event * was enqueued and haven't been removed since. * + *
All methods must be called on the {@link Looper} passed to the constructor unless indicated
+ * otherwise.
+ *
* @param This method can be called from any thread.
+ *
* @param looper The new {@link Looper} for the copied listener set.
* @param iterationFinishedEvent The new {@link IterationFinishedEvent} sent when all other events
* sent during one {@link Looper} message queue iteration were handled by the listeners.
@@ -122,6 +136,8 @@ public final class ListenerSet This method can be called from any thread.
+ *
* @param looper The new {@link Looper} for the copied listener set.
* @param clock The new {@link Clock} for the copied listener set.
* @param iterationFinishedEvent The new {@link IterationFinishedEvent} sent when all other events
@@ -139,14 +155,18 @@ public final class ListenerSet If a listener is already present, it will not be added again.
*
+ * This method can be called from any thread.
+ *
* @param listener The listener to be added.
*/
public void add(T listener) {
- if (released) {
- return;
- }
Assertions.checkNotNull(listener);
- listeners.add(new ListenerHolder<>(listener));
+ synchronized (releasedLock) {
+ if (released) {
+ return;
+ }
+ listeners.add(new ListenerHolder<>(listener));
+ }
}
/**
@@ -157,6 +177,7 @@ public final class ListenerSet This will ensure no events are sent to any listener after this method has been called.
*/
public void release() {
+ verifyCurrentThread();
+ synchronized (releasedLock) {
+ released = true;
+ }
for (ListenerHolder Do not use this method unless to support legacy use cases.
+ *
+ * @param throwsWhenUsingWrongThread Whether to throw when using the wrong thread.
+ * @deprecated Do not use this method and ensure all calls are made from the correct thread.
+ */
+ @Deprecated
+ public void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) {
+ this.throwsWhenUsingWrongThread = throwsWhenUsingWrongThread;
}
private boolean handleMessage(Message message) {
@@ -252,6 +293,13 @@ public final class ListenerSet This method can be called from any thread.
+ *
* @param listener The listener to register.
*/
void addAudioOffloadListener(AudioOffloadListener listener);
@@ -1204,6 +1206,8 @@ public interface ExoPlayer extends Player {
/**
* Adds an {@link AnalyticsListener} to receive analytics events.
*
+ * This method can be called from any thread.
+ *
* @param listener The listener to be added.
*/
void addAnalyticsListener(AnalyticsListener listener);
@@ -1263,10 +1267,18 @@ public interface ExoPlayer extends Player {
@Deprecated
TrackSelectionArray getCurrentTrackSelections();
- /** Returns the {@link Looper} associated with the playback thread. */
+ /**
+ * Returns the {@link Looper} associated with the playback thread.
+ *
+ * This method may be called from any thread.
+ */
Looper getPlaybackLooper();
- /** Returns the {@link Clock} used for playback. */
+ /**
+ * Returns the {@link Clock} used for playback.
+ *
+ * This method can be called from any thread.
+ */
Clock getClock();
/**
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
index 2270d7c2ef..1d76a85f9b 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
@@ -59,6 +59,7 @@ import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.Renderer.MessageType;
import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.analytics.AnalyticsListener;
+import com.google.android.exoplayer2.analytics.DefaultAnalyticsCollector;
import com.google.android.exoplayer2.analytics.MediaMetricsListener;
import com.google.android.exoplayer2.analytics.PlayerId;
import com.google.android.exoplayer2.audio.AudioAttributes;
@@ -469,7 +470,7 @@ import java.util.concurrent.TimeoutException;
@Override
public void removeAudioOffloadListener(AudioOffloadListener listener) {
- // Don't verify application thread. We allow calls to this method from any thread.
+ verifyApplicationThread();
audioOffloadListeners.remove(listener);
}
@@ -1477,7 +1478,7 @@ import java.util.concurrent.TimeoutException;
@Override
public void removeAnalyticsListener(AnalyticsListener listener) {
- // Don't verify application thread. We allow calls to this method from any thread.
+ verifyApplicationThread();
analyticsCollector.removeListener(checkNotNull(listener));
}
@@ -1594,9 +1595,8 @@ import java.util.concurrent.TimeoutException;
@Override
public void removeListener(Listener listener) {
- // Don't verify application thread. We allow calls to this method from any thread.
- checkNotNull(listener);
- listeners.remove(listener);
+ verifyApplicationThread();
+ listeners.remove(checkNotNull(listener));
}
@Override
@@ -1679,8 +1679,14 @@ import java.util.concurrent.TimeoutException;
return false;
}
+ @SuppressWarnings("deprecation") // Calling deprecated methods.
/* package */ void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) {
this.throwsWhenUsingWrongThread = throwsWhenUsingWrongThread;
+ listeners.setThrowsWhenUsingWrongThread(throwsWhenUsingWrongThread);
+ if (analyticsCollector instanceof DefaultAnalyticsCollector) {
+ ((DefaultAnalyticsCollector) analyticsCollector)
+ .setThrowsWhenUsingWrongThread(throwsWhenUsingWrongThread);
+ }
}
/**
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/DefaultAnalyticsCollector.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/DefaultAnalyticsCollector.java
index 14d1f546cd..4d3eda9373 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/DefaultAnalyticsCollector.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/DefaultAnalyticsCollector.java
@@ -94,6 +94,20 @@ public class DefaultAnalyticsCollector implements AnalyticsCollector {
eventTimes = new SparseArray<>();
}
+ /**
+ * Sets whether methods throw when using the wrong thread.
+ *
+ * Do not use this method unless to support legacy use cases.
+ *
+ * @param throwsWhenUsingWrongThread Whether to throw when using the wrong thread.
+ * @deprecated Do not use this method and ensure all calls are made from the correct thread.
+ */
+ @SuppressWarnings("deprecation") // Calling deprecated method.
+ @Deprecated
+ public void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) {
+ listeners.setThrowsWhenUsingWrongThread(throwsWhenUsingWrongThread);
+ }
+
@Override
@CallSuper
public void addListener(AnalyticsListener listener) {
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
index 43c0480b57..aa2e6bfc68 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
@@ -11996,10 +11996,20 @@ public final class ExoPlayerTest {
@Test
@Config(sdk = Config.ALL_SDKS)
- public void builder_inBackgroundThread_doesNotThrow() throws Exception {
+ public void builder_inBackgroundThreadWithAllowedAnyThreadMethods_doesNotThrow()
+ throws Exception {
Thread builderThread =
new Thread(
- () -> new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build());
+ () -> {
+ ExoPlayer player =
+ new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build();
+ player.addListener(new Listener() {});
+ player.addAnalyticsListener(new AnalyticsListener() {});
+ player.addAudioOffloadListener(new ExoPlayer.AudioOffloadListener() {});
+ player.getClock();
+ player.getApplicationLooper();
+ player.getPlaybackLooper();
+ });
AtomicReference
- *