Commit a940d8bc by eguven Committed by Oliver Woodman

Use removing and restarting state internally in DownloadManager

PiperOrigin-RevId: 227682159
parent c1307239
...@@ -175,7 +175,6 @@ public final class DownloadManager { ...@@ -175,7 +175,6 @@ public final class DownloadManager {
for (int i = 0; i < downloads.size(); i++) { for (int i = 0; i < downloads.size(); i++) {
downloads.get(i).clearStopFlags(STOP_FLAG_STOPPED); downloads.get(i).clearStopFlags(STOP_FLAG_STOPPED);
} }
maybeStartDownloads();
logd("Downloads are started"); logd("Downloads are started");
} }
} }
...@@ -254,7 +253,7 @@ public final class DownloadManager { ...@@ -254,7 +253,7 @@ public final class DownloadManager {
return false; return false;
} }
for (int i = 0; i < downloads.size(); i++) { for (int i = 0; i < downloads.size(); i++) {
if (downloads.get(i).isStarted()) { if (!downloads.get(i).isIdle()) {
return false; return false;
} }
} }
...@@ -284,8 +283,7 @@ public final class DownloadManager { ...@@ -284,8 +283,7 @@ public final class DownloadManager {
private void addDownloadForAction(DownloadAction action) { private void addDownloadForAction(DownloadAction action) {
for (int i = 0; i < downloads.size(); i++) { for (int i = 0; i < downloads.size(); i++) {
Download download = downloads.get(i); Download download = downloads.get(i);
if (download.action.isSameMedia(action)) { if (download.addAction(action)) {
download.addAction(action);
logd("Action is added to existing download", download); logd("Action is added to existing download", download);
return; return;
} }
...@@ -296,33 +294,27 @@ public final class DownloadManager { ...@@ -296,33 +294,27 @@ public final class DownloadManager {
logd("Download is added", download); logd("Download is added", download);
} }
/**
* Iterates through the download queue and starts any download if all of the following are true:
*
* <ul>
* <li>It hasn't started yet.
* <li>The maximum number of active downloads hasn't been reached.
* </ul>
*/
private void maybeStartDownloads() { private void maybeStartDownloads() {
if (!initialized || released) {
return;
}
for (int i = 0; i < downloads.size(); i++) { for (int i = 0; i < downloads.size(); i++) {
maybeStartDownload(downloads.get(i)); maybeStartDownload(downloads.get(i));
} }
} }
private void maybeStartDownload(Download download) { private void maybeStartDownload(Download download) {
if (download.action.isRemoveAction) { if (initialized && !released && activeDownloads.size() < maxActiveDownloads) {
download.start();
} else if (activeDownloads.size() < maxActiveDownloads) {
if (download.start()) { if (download.start()) {
activeDownloads.add(download); activeDownloads.add(download);
} }
} }
} }
private void maybeRestartDownload(Download download) {
Assertions.checkState(activeDownloads.contains(download));
if (initialized && !released) {
download.start();
}
}
private void maybeNotifyListenersIdle() { private void maybeNotifyListenersIdle() {
if (!isIdle()) { if (!isIdle()) {
return; return;
...@@ -337,8 +329,8 @@ public final class DownloadManager { ...@@ -337,8 +329,8 @@ public final class DownloadManager {
if (released) { if (released) {
return; return;
} }
boolean stopped = !download.isStarted(); boolean idle = download.isIdle();
if (stopped) { if (idle) {
activeDownloads.remove(download); activeDownloads.remove(download);
} }
notifyListenersDownloadStateChange(download); notifyListenersDownloadStateChange(download);
...@@ -346,7 +338,7 @@ public final class DownloadManager { ...@@ -346,7 +338,7 @@ public final class DownloadManager {
downloads.remove(download); downloads.remove(download);
saveActions(); saveActions();
} }
if (stopped) { if (idle) {
maybeStartDownloads(); maybeStartDownloads();
maybeNotifyListenersIdle(); maybeNotifyListenersIdle();
} }
...@@ -380,17 +372,18 @@ public final class DownloadManager { ...@@ -380,17 +372,18 @@ public final class DownloadManager {
for (DownloadAction action : actions) { for (DownloadAction action : actions) {
addDownloadForAction(action); addDownloadForAction(action);
} }
logd("Downloads are created.");
initialized = true;
for (Listener listener : listeners) {
listener.onInitialized(DownloadManager.this);
}
if (!actionQueue.isEmpty()) { if (!actionQueue.isEmpty()) {
while (!actionQueue.isEmpty()) { while (!actionQueue.isEmpty()) {
addDownloadForAction(actionQueue.remove()); addDownloadForAction(actionQueue.remove());
} }
saveActions(); saveActions();
} }
logd("Downloads are created.");
initialized = true;
for (Listener listener : listeners) {
listener.onInitialized(DownloadManager.this);
}
maybeStartDownloads();
}); });
}); });
} }
...@@ -433,7 +426,6 @@ public final class DownloadManager { ...@@ -433,7 +426,6 @@ public final class DownloadManager {
private final int minRetryCount; private final int minRetryCount;
private final long startTimeMs; private final long startTimeMs;
private final ArrayDeque<DownloadAction> actionQueue; private final ArrayDeque<DownloadAction> actionQueue;
private DownloadAction action;
/** The current state of the download. */ /** The current state of the download. */
@DownloadState.State private int state; @DownloadState.State private int state;
...@@ -456,29 +448,36 @@ public final class DownloadManager { ...@@ -456,29 +448,36 @@ public final class DownloadManager {
this.startTimeMs = System.currentTimeMillis(); this.startTimeMs = System.currentTimeMillis();
actionQueue = new ArrayDeque<>(); actionQueue = new ArrayDeque<>();
actionQueue.add(action); actionQueue.add(action);
initialize(/* restart= */ false);
// Don't notify listeners until we make sure the state doesn't change immediately.
state = STATE_QUEUED;
setActionAndUpdateState(action);
downloadManager.maybeStartDownload(this);
if (state == STATE_QUEUED) {
downloadManager.onDownloadStateChange(this);
}
} }
public void addAction(DownloadAction newAction) { public boolean addAction(DownloadAction newAction) {
DownloadAction action = actionQueue.peek();
if (!action.isSameMedia(newAction)) {
return false;
}
Assertions.checkState(action.type.equals(newAction.type)); Assertions.checkState(action.type.equals(newAction.type));
actionQueue.add(newAction); actionQueue.add(newAction);
DownloadAction updatedAction = DownloadActionUtil.mergeActions(actionQueue); DownloadAction updatedAction = DownloadActionUtil.mergeActions(actionQueue);
if (action.equals(updatedAction)) { if (state == STATE_REMOVING) {
return; Assertions.checkState(updatedAction.isRemoveAction);
} if (actionQueue.size() > 1) {
if (state == STATE_DOWNLOADING) { setState(STATE_RESTARTING);
stopDownloadThread(); }
} else { } else if (state == STATE_RESTARTING) {
Assertions.checkState(state == STATE_QUEUED || state == STATE_STOPPED); Assertions.checkState(updatedAction.isRemoveAction);
setActionAndUpdateState(updatedAction); if (actionQueue.size() == 1) {
setState(STATE_REMOVING);
}
} else if (!action.equals(updatedAction)) {
if (state == STATE_DOWNLOADING) {
stopDownloadThread();
} else {
Assertions.checkState(state == STATE_QUEUED || state == STATE_STOPPED);
initialize(/* restart= */ false);
}
} }
return true;
} }
public DownloadState getDownloadState() { public DownloadState getDownloadState() {
...@@ -490,20 +489,13 @@ public final class DownloadManager { ...@@ -490,20 +489,13 @@ public final class DownloadManager {
downloadedBytes = downloader.getDownloadedBytes(); downloadedBytes = downloader.getDownloadedBytes();
totalBytes = downloader.getTotalBytes(); totalBytes = downloader.getTotalBytes();
} }
int newState = state; DownloadAction action = actionQueue.peek();
if (action.isRemoveAction) {
if (state == STATE_DOWNLOADING) {
newState = actionQueue.size() > 1 ? STATE_RESTARTING : STATE_REMOVING;
} else if (state == STATE_COMPLETED || state == STATE_FAILED) {
newState = STATE_REMOVED;
}
}
return new DownloadState( return new DownloadState(
action.id, action.id,
action.type, action.type,
action.uri, action.uri,
action.customCacheKey, action.customCacheKey,
newState, state,
downloadPercentage, downloadPercentage,
downloadedBytes, downloadedBytes,
totalBytes, totalBytes,
...@@ -515,30 +507,24 @@ public final class DownloadManager { ...@@ -515,30 +507,24 @@ public final class DownloadManager {
action.data); action.data);
} }
/** Returns whether the download is finished. */
public boolean isFinished() { public boolean isFinished() {
return state == STATE_FAILED || state == STATE_COMPLETED; return state == STATE_FAILED || state == STATE_COMPLETED || state == STATE_REMOVED;
} }
/** Returns whether the download is started. */ public boolean isIdle() {
public boolean isStarted() { return state != STATE_DOWNLOADING && state != STATE_REMOVING && state != STATE_RESTARTING;
return state == STATE_DOWNLOADING;
} }
@Override @Override
public String toString() { public String toString() {
String actionString = action.isRemoveAction ? "remove" : "download"; return id + ' ' + DownloadState.getStateString(state);
return id + ' ' + actionString + ' ' + DownloadState.getStateString(state);
} }
public boolean start() { public boolean start() {
if (state != STATE_QUEUED) { if (state != STATE_QUEUED) {
return false; return false;
} }
downloader = downloaderFactory.createDownloader(action); startDownloadThread(actionQueue.peek());
downloadThread =
new DownloadThread(
this, downloader, action.isRemoveAction, minRetryCount, downloadManager.handler);
setState(STATE_DOWNLOADING); setState(STATE_DOWNLOADING);
return true; return true;
} }
...@@ -560,30 +546,55 @@ public final class DownloadManager { ...@@ -560,30 +546,55 @@ public final class DownloadManager {
private void updateStopFlags(int mask, int flags) { private void updateStopFlags(int mask, int flags) {
stopFlags = (flags & mask) | (stopFlags & ~mask); stopFlags = (flags & mask) | (stopFlags & ~mask);
if (stopFlags != 0) { if (stopFlags != 0) {
if (!action.isRemoveAction) { if (state == STATE_DOWNLOADING) {
if (state == STATE_DOWNLOADING) { stopDownloadThread();
stopDownloadThread(); } else if (state == STATE_QUEUED) {
} else if (state == STATE_QUEUED) { setState(STATE_STOPPED);
setState(STATE_STOPPED);
}
} }
} else if (state == STATE_STOPPED) { } else if (state == STATE_STOPPED) {
setState(STATE_QUEUED); startOrQueue(/* restart= */ false);
} }
} }
private void setActionAndUpdateState(DownloadAction action) { private void initialize(boolean restart) {
this.action = action; DownloadAction action = actionQueue.peek();
setState(!this.action.isRemoveAction && stopFlags != 0 ? STATE_STOPPED : STATE_QUEUED); if (action.isRemoveAction) {
if (!downloadManager.released) {
startDownloadThread(action);
}
setState(actionQueue.size() == 1 ? STATE_REMOVING : STATE_RESTARTING);
} else if (stopFlags != 0) {
setState(STATE_STOPPED);
} else {
startOrQueue(restart);
}
} }
private void setState(@DownloadState.State int newState) { private void startOrQueue(boolean restart) {
if (state != newState) { // Set to queued state but don't notify listeners until we make sure we can't start now.
state = newState; state = STATE_QUEUED;
if (restart) {
downloadManager.maybeRestartDownload(this);
} else {
downloadManager.maybeStartDownload(this);
}
if (state == STATE_QUEUED) {
downloadManager.onDownloadStateChange(this); downloadManager.onDownloadStateChange(this);
} }
} }
private void setState(@DownloadState.State int newState) {
state = newState;
downloadManager.onDownloadStateChange(this);
}
private void startDownloadThread(DownloadAction action) {
downloader = downloaderFactory.createDownloader(action);
downloadThread =
new DownloadThread(
this, downloader, action.isRemoveAction, minRetryCount, downloadManager.handler);
}
private void stopDownloadThread() { private void stopDownloadThread() {
Assertions.checkNotNull(downloadThread).cancel(); Assertions.checkNotNull(downloadThread).cancel();
} }
...@@ -591,18 +602,23 @@ public final class DownloadManager { ...@@ -591,18 +602,23 @@ public final class DownloadManager {
private void onDownloadThreadStopped(@Nullable Throwable finalError) { private void onDownloadThreadStopped(@Nullable Throwable finalError) {
failureReason = FAILURE_REASON_NONE; failureReason = FAILURE_REASON_NONE;
if (!downloadThread.isCanceled) { if (!downloadThread.isCanceled) {
if (finalError != null) { if (finalError != null && state != STATE_REMOVING && state != STATE_RESTARTING) {
failureReason = FAILURE_REASON_UNKNOWN; failureReason = FAILURE_REASON_UNKNOWN;
setState(STATE_FAILED); setState(STATE_FAILED);
return; return;
} }
if (actionQueue.size() == 1) {
if (state == STATE_REMOVING) {
setState(STATE_REMOVED);
} else {
Assertions.checkState(state == STATE_DOWNLOADING);
setState(STATE_COMPLETED);
}
return;
}
actionQueue.remove(); actionQueue.remove();
} }
if (!actionQueue.isEmpty()) { initialize(/* restart= */ state == STATE_DOWNLOADING);
setActionAndUpdateState(actionQueue.peek());
} else {
setState(STATE_COMPLETED);
}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment