diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java index 997f4e09a2..6e45604531 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java @@ -38,7 +38,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -93,6 +92,7 @@ public final class DownloadManager { private final HandlerThread fileIOThread; private final Handler fileIOHandler; private final CopyOnWriteArraySet listeners; + private final ArrayDeque actionQueue; private boolean initialized; private boolean released; @@ -142,6 +142,7 @@ public final class DownloadManager { fileIOHandler = new Handler(fileIOThread.getLooper()); listeners = new CopyOnWriteArraySet<>(); + actionQueue = new ArrayDeque<>(); loadActions(); logd("Created"); @@ -194,8 +195,8 @@ public final class DownloadManager { */ public void handleAction(DownloadAction action) { Assertions.checkState(!released); - Download download = getDownloadForAction(action); if (initialized) { + Download download = getOrAddDownloadForAction(action); saveActions(); maybeStartDownloads(); if (download.state == STATE_QUEUED) { @@ -204,6 +205,8 @@ public final class DownloadManager { // reported to listeners. Do so now. notifyListenersDownloadStateChange(download); } + } else { + actionQueue.add(action); } } @@ -281,7 +284,7 @@ public final class DownloadManager { logd("Released"); } - private Download getDownloadForAction(DownloadAction action) { + private Download getOrAddDownloadForAction(DownloadAction action) { for (int i = 0; i < downloads.size(); i++) { Download download = downloads.get(i); if (download.action.isSameMedia(action)) { @@ -380,18 +383,18 @@ public final class DownloadManager { if (released) { return; } - List pendingDownloads = new ArrayList<>(downloads); - downloads.clear(); for (DownloadAction action : actions) { - getDownloadForAction(action); + getOrAddDownloadForAction(action); } logd("Downloads are created."); initialized = true; for (Listener listener : listeners) { listener.onInitialized(DownloadManager.this); } - if (!pendingDownloads.isEmpty()) { - downloads.addAll(pendingDownloads); + if (!actionQueue.isEmpty()) { + while (!actionQueue.isEmpty()) { + getOrAddDownloadForAction(actionQueue.remove()); + } saveActions(); } maybeStartDownloads(); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java index e7d54b4141..e9720c8d3b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java @@ -426,9 +426,9 @@ public class DownloadManagerTest { actionFile, downloaderFactory, maxActiveDownloadTasks, MIN_RETRY_COUNT); downloadManagerListener = new TestDownloadManagerListener(downloadManager, dummyMainThread); - downloadManager.addListener(downloadManagerListener); downloadManager.startDownloads(); }); + downloadManagerListener.waitUntilInitialized(); } catch (Throwable throwable) { throw new Exception(throwable); } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadManagerDashTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadManagerDashTest.java index 8c61432f66..abe868bcfb 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadManagerDashTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadManagerDashTest.java @@ -247,7 +247,6 @@ public class DownloadManagerDashTest { downloadManagerListener = new TestDownloadManagerListener(downloadManager, dummyMainThread); - downloadManager.addListener(downloadManagerListener); downloadManager.startDownloads(); }); } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java index 09f62fa776..adae0d7b04 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java @@ -124,7 +124,6 @@ public class DownloadServiceDashTest { /* minRetryCount= */ 3); downloadManagerListener = new TestDownloadManagerListener(dashDownloadManager, dummyMainThread); - dashDownloadManager.addListener(downloadManagerListener); dashDownloadManager.startDownloads(); dashDownloadService = diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/DummyMainThread.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/DummyMainThread.java index 858d287196..982fcdf558 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/DummyMainThread.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/DummyMainThread.java @@ -21,6 +21,8 @@ import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; +import com.google.android.exoplayer2.util.Util; +import java.util.concurrent.atomic.AtomicReference; /** Helper class to simulate main/UI thread in tests. */ public final class DummyMainThread { @@ -58,13 +60,21 @@ public final class DummyMainThread { if (Looper.myLooper() == handler.getLooper()) { runnable.run(); } else { - final ConditionVariable finishedCondition = new ConditionVariable(); + ConditionVariable finishedCondition = new ConditionVariable(); + AtomicReference thrown = new AtomicReference<>(); handler.post( () -> { - runnable.run(); + try { + runnable.run(); + } catch (Throwable t) { + thrown.set(t); + } finishedCondition.open(); }); assertThat(finishedCondition.block(timeoutMs)).isTrue(); + if (thrown.get() != null) { + Util.sneakyThrow(thrown.get()); + } } } diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java index e7b850d52b..c844e777c2 100644 --- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TestDownloadManagerListener.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.testutil; import static com.google.common.truth.Truth.assertThat; +import android.os.ConditionVariable; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import java.util.HashMap; @@ -28,10 +29,12 @@ import java.util.concurrent.TimeUnit; public final class TestDownloadManagerListener implements DownloadManager.Listener { private static final int TIMEOUT = 1000; + private static final int INITIALIZATION_TIMEOUT = 10000; private final DownloadManager downloadManager; private final DummyMainThread dummyMainThread; private final HashMap> actionStates; + private final ConditionVariable initializedCondition; private CountDownLatch downloadFinishedCondition; @DownloadState.FailureReason private int failureReason; @@ -41,6 +44,8 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen this.downloadManager = downloadManager; this.dummyMainThread = dummyMainThread; actionStates = new HashMap<>(); + initializedCondition = new ConditionVariable(); + downloadManager.addListener(this); } public Integer pollStateChange(String taskId, long timeoutMs) throws InterruptedException { @@ -53,7 +58,13 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen @Override public void onInitialized(DownloadManager downloadManager) { - // Do nothing. + initializedCondition.open(); + } + + public void waitUntilInitialized() { + if (!downloadManager.isInitialized()) { + assertThat(initializedCondition.block(INITIALIZATION_TIMEOUT)).isTrue(); + } } @Override