diff --git a/library/src/main/java/com/liulishuo/filedownloader/BaseDownloadTask.java b/library/src/main/java/com/liulishuo/filedownloader/BaseDownloadTask.java index f66cb72b..7f7d0ea0 100644 --- a/library/src/main/java/com/liulishuo/filedownloader/BaseDownloadTask.java +++ b/library/src/main/java/com/liulishuo/filedownloader/BaseDownloadTask.java @@ -432,8 +432,8 @@ public boolean isRunning() { * @return Whether has already attached to a listener / a serial-queue. If {@code true}, this task * object must be running with the listener or has already assembled to a serial-queue and would * be started automatically when it is come to its turn. - * @see FileDownloader#startParallelTasks(FileDownloadListener) - * @see FileDownloader#startSerialTasks(FileDownloadListener) + * @see IQueuesHandler#startQueueSerial(FileDownloadListener) + * @see IQueuesHandler#startQueueParallel(FileDownloadListener) */ public boolean isAttached() { return attachKey != 0; diff --git a/library/src/main/java/com/liulishuo/filedownloader/FileDownloadTask.java b/library/src/main/java/com/liulishuo/filedownloader/FileDownloadTask.java index 13315c24..201f0c6c 100644 --- a/library/src/main/java/com/liulishuo/filedownloader/FileDownloadTask.java +++ b/library/src/main/java/com/liulishuo/filedownloader/FileDownloadTask.java @@ -16,8 +16,6 @@ package com.liulishuo.filedownloader; -import android.os.Handler; - import com.liulishuo.filedownloader.event.DownloadEventSampleListener; import com.liulishuo.filedownloader.event.DownloadServiceConnectChangedEvent; import com.liulishuo.filedownloader.event.IDownloadEvent; @@ -129,7 +127,8 @@ protected boolean _checkCanStart() { if (!FileDownloadServiceProxy.getImpl().isConnected()) { // 没有连上 服务 if (FileDownloadLog.NEED_LOG) { - FileDownloadLog.d(this, "no connect service !! %s", getId()); + FileDownloadLog.d(this, "Waiting for connecting with the downloader " + + "service... %d", getId()); } FileDownloadServiceProxy.getImpl().bindStartByContext(FileDownloadHelper.getAppContext()); if (!NEED_RESTART_LIST.contains(this)) { @@ -266,6 +265,9 @@ public boolean callback(IDownloadEvent event) { FileDownloadLog.d(FileDownloadTask.class, "callback connect service %s", ((DownloadServiceConnectChangedEvent) event).getStatus()); } + + final IQueuesHandler queueHandler = FileDownloader.getImpl().getQueueHandler(); + if (((DownloadServiceConnectChangedEvent) event).getStatus() == DownloadServiceConnectChangedEvent.ConnectStatus.connected) { List needRestartList; @@ -275,8 +277,9 @@ public boolean callback(IDownloadEvent event) { needRestartList = (List) NEED_RESTART_LIST.clone(); NEED_RESTART_LIST.clear(); + for (BaseDownloadTask o : needRestartList) { - if (FileDownloader.RUNNING_SERIAL_MAP.get(o.attachKey) != null) { + if (queueHandler.contain(o.attachKey)) { o.ready(); continue; } @@ -290,11 +293,7 @@ public boolean callback(IDownloadEvent event) { } } - for (int i = 0; i < FileDownloader.RUNNING_SERIAL_MAP.size(); i++) { - final int key = FileDownloader.RUNNING_SERIAL_MAP.keyAt(i); - Handler handler = FileDownloader.RUNNING_SERIAL_MAP.get(key); - FileDownloader.unFreezeSerialHandler(handler); - } + queueHandler.unFreezeAllSerialQueues(); } } else if (((DownloadServiceConnectChangedEvent) event).getStatus() == @@ -306,7 +305,6 @@ public boolean callback(IDownloadEvent event) { FileDownloadList.getImpl().size()); } - // TODO Multi-engine support, need to deal with similar situation if (FileDownloadList.getImpl().size() > 0) { synchronized (NEED_RESTART_LIST) { FileDownloadList.getImpl().divert(NEED_RESTART_LIST); @@ -315,11 +313,7 @@ public boolean callback(IDownloadEvent event) { baseDownloadTask.clearMarkAdded2List(); } - for (int i = 0; i < FileDownloader.RUNNING_SERIAL_MAP.size(); i++) { - final int key = FileDownloader.RUNNING_SERIAL_MAP.keyAt(i); - Handler handler = FileDownloader.RUNNING_SERIAL_MAP.get(key); - FileDownloader.freezeSerialHandler(handler); - } + queueHandler.freezeAllSerialQueues(); } } diff --git a/library/src/main/java/com/liulishuo/filedownloader/FileDownloader.java b/library/src/main/java/com/liulishuo/filedownloader/FileDownloader.java index f30f41df..634e4015 100644 --- a/library/src/main/java/com/liulishuo/filedownloader/FileDownloader.java +++ b/library/src/main/java/com/liulishuo/filedownloader/FileDownloader.java @@ -21,9 +21,6 @@ import android.app.NotificationManager; import android.content.Context; import android.os.Handler; -import android.os.HandlerThread; -import android.os.Message; -import android.util.SparseArray; import com.liulishuo.filedownloader.event.DownloadServiceConnectChangedEvent; import com.liulishuo.filedownloader.model.FileDownloadStatus; @@ -34,7 +31,6 @@ import com.liulishuo.filedownloader.util.FileDownloadUtils; import java.io.File; -import java.lang.ref.WeakReference; import java.util.List; import okhttp3.OkHttpClient; @@ -236,7 +232,9 @@ public boolean start(final FileDownloadListener listener, final boolean isSerial } - return isSerial ? startSerialTasks(listener) : startParallelTasks(listener); + return isSerial ? + getQueueHandler().startQueueSerial(listener) : + getQueueHandler().startQueueParallel(listener); } @@ -641,203 +639,9 @@ public boolean setMaxNetworkThreadCount(final int count) { return FileDownloadServiceProxy.getImpl().setMaxNetworkThreadCount(count); } - private boolean startParallelTasks(FileDownloadListener listener) { - final int attachKey = listener.hashCode(); + private final IQueuesHandler mQueueHandler = new QueuesHandler(pauseLock); - final List list = FileDownloadList.getImpl(). - assembleTasksToStart(attachKey, listener); - - if (onAssembledTasksToStart(attachKey, list, listener, false)) { - return false; - } - - for (BaseDownloadTask task : list) { - task.start(); - } - - return true; - } - - private boolean startSerialTasks(FileDownloadListener listener) { - final SerialHandlerCallback callback = new SerialHandlerCallback(); - final int attachKey = callback.hashCode(); - - final List list = FileDownloadList.getImpl(). - assembleTasksToStart(attachKey, listener); - - if (onAssembledTasksToStart(attachKey, list, listener, true)) { - return false; - } - - final HandlerThread serialThread = new HandlerThread( - FileDownloadUtils.formatString("filedownloader serial thread %s-%d", - listener, attachKey)); - serialThread.start(); - - final Handler serialHandler = new Handler(serialThread.getLooper(), callback); - callback.setHandler(serialHandler); - callback.setList(list); - - callback.goNext(0); - - synchronized (RUNNING_SERIAL_MAP) { - RUNNING_SERIAL_MAP.put(attachKey, serialHandler); - } - return true; - } - - private boolean onAssembledTasksToStart(int attachKey, final List list, - final FileDownloadListener listener, boolean isSerial) { - if (FileDownloadMonitor.isValid()) { - FileDownloadMonitor.getMonitor().onRequestStart(list.size(), true, listener); - } - - if (FileDownloadLog.NEED_LOG) { - FileDownloadLog.v(FileDownloader.class, "start list attachKey[%d] size[%d] " + - "listener[%s] isSerial[%B]", attachKey, list.size(), listener, isSerial); - } - - if (list == null || list.isEmpty()) { - FileDownloadLog.w(FileDownloader.class, "Tasks with the listener can't start, " + - "because can't find any task with the provided listener: [%s, %B]", - listener, isSerial); - - return true; - } - - return false; - - } - - final static SparseArray RUNNING_SERIAL_MAP = new SparseArray<>(); - - final static int WHAT_SERIAL_NEXT = 1; - final static int WHAT_FREEZE = 2; - final static int WHAT_UNFREEZE = 3; - - private static class SerialHandlerCallback implements Handler.Callback { - private Handler handler; - private List list; - private int runningIndex = 0; - private SerialFinishListener serialFinishListener; - - SerialHandlerCallback() { - serialFinishListener = - new SerialFinishListener(new WeakReference<>(this)); - } - - public void setHandler(final Handler handler) { - this.handler = handler; - } - - public void setList(List list) { - this.list = list; - } - - @Override - public boolean handleMessage(final Message msg) { - if (msg.what == WHAT_SERIAL_NEXT) { - if (msg.arg1 >= list.size()) { - synchronized (RUNNING_SERIAL_MAP) { - RUNNING_SERIAL_MAP.remove(list.get(0).attachKey); - } - // final serial tasks - if (this.handler != null && this.handler.getLooper() != null) { - this.handler.getLooper().quit(); - this.handler = null; - this.list = null; - this.serialFinishListener = null; - } - - if (FileDownloadLog.NEED_LOG) { - FileDownloadLog.d(SerialHandlerCallback.class, "final serial %s %d", - this.list == null ? null : this.list.get(0) == null ? null : this.list.get(0).getListener(), - msg.arg1); - } - return true; - } - - runningIndex = msg.arg1; - final BaseDownloadTask stackTopTask = this.list.get(runningIndex); - synchronized (pauseLock) { - if (!FileDownloadList.getImpl().contains(stackTopTask)) { - // pause? - if (FileDownloadLog.NEED_LOG) { - FileDownloadLog.d(SerialHandlerCallback.class, "direct go next by not contains %s %d", stackTopTask, msg.arg1); - } - goNext(msg.arg1 + 1); - return true; - } - } - - - stackTopTask - .addFinishListener(serialFinishListener.setNextIndex(runningIndex + 1)) - .start(); - - } else if (msg.what == WHAT_FREEZE) { - freeze(); - } else if (msg.what == WHAT_UNFREEZE) { - unfreeze(); - } - return true; - } - - public void freeze() { - list.get(runningIndex).removeFinishListener(serialFinishListener); - handler.removeCallbacksAndMessages(null); - } - - public void unfreeze() { - goNext(runningIndex); - } - - private void goNext(final int nextIndex) { - if (this.handler == null || this.list == null) { - FileDownloadLog.w(this, "need go next %d, but params is not ready %s %s", - nextIndex, this.handler, this.list); - return; - } - - Message nextMsg = this.handler.obtainMessage(); - nextMsg.what = WHAT_SERIAL_NEXT; - nextMsg.arg1 = nextIndex; - if (FileDownloadLog.NEED_LOG) { - FileDownloadLog.d(SerialHandlerCallback.class, "start next %s %s", - this.list == null ? null : this.list.get(0) == null ? null : - this.list.get(0).getListener(), nextMsg.arg1); - } - this.handler.sendMessage(nextMsg); - } - } - - private static class SerialFinishListener implements BaseDownloadTask.FinishListener { - private final WeakReference wSerialHandlerCallback; - - private SerialFinishListener(WeakReference wSerialHandlerCallback) { - this.wSerialHandlerCallback = wSerialHandlerCallback; - } - - private int nextIndex; - - public BaseDownloadTask.FinishListener setNextIndex(int index) { - this.nextIndex = index; - return this; - } - - @Override - public void over(final BaseDownloadTask task) { - if (wSerialHandlerCallback != null && wSerialHandlerCallback.get() != null) { - wSerialHandlerCallback.get().goNext(this.nextIndex); - } - } - } - - static void freezeSerialHandler(Handler handler) { - handler.sendEmptyMessage(WHAT_FREEZE); - } - - static void unFreezeSerialHandler(Handler handler) { - handler.sendEmptyMessage(WHAT_UNFREEZE); + IQueuesHandler getQueueHandler() { + return mQueueHandler; } } diff --git a/library/src/main/java/com/liulishuo/filedownloader/IQueuesHandler.java b/library/src/main/java/com/liulishuo/filedownloader/IQueuesHandler.java new file mode 100644 index 00000000..f57b6bb9 --- /dev/null +++ b/library/src/main/java/com/liulishuo/filedownloader/IQueuesHandler.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015 LingoChamp Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.liulishuo.filedownloader; + +/** + * Created by Jacksgong on 20/08/2016. + *

+ * The interface for handle affairs of queues. + */ + +public interface IQueuesHandler { + + /** + * Start tasks which the same {@code listener} as a queue, and execute theme in parallel. + * + * @param listener Used to assemble tasks which is bound by the same {@code listener} + * @return Whether start tasks successfully. + */ + boolean startQueueParallel(FileDownloadListener listener); + + /** + * Start tasks which the same {@code listener} as a queue, and execute theme one by one. + * + * @param listener Used to assemble tasks which is bound by the same {@code listener} + * @return Whether start tasks successfully. + */ + boolean startQueueSerial(FileDownloadListener listener); + + void freezeAllSerialQueues(); + + void unFreezeAllSerialQueues(); + + boolean contain(int attachKey); +} diff --git a/library/src/main/java/com/liulishuo/filedownloader/QueuesHandler.java b/library/src/main/java/com/liulishuo/filedownloader/QueuesHandler.java new file mode 100644 index 00000000..83632a9a --- /dev/null +++ b/library/src/main/java/com/liulishuo/filedownloader/QueuesHandler.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2015 LingoChamp Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.liulishuo.filedownloader; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import android.util.SparseArray; + +import com.liulishuo.filedownloader.util.FileDownloadLog; +import com.liulishuo.filedownloader.util.FileDownloadUtils; + +import java.lang.ref.WeakReference; +import java.util.List; + +/** + * Created by Jacksgong on 20/08/2016. + *

+ * The default queue handler. + */ + +class QueuesHandler implements IQueuesHandler { + + private final Object mPauseLock; + private final SparseArray mRunningSerialMap; + + public QueuesHandler(Object pauseLock) { + this.mPauseLock = pauseLock; + this.mRunningSerialMap = new SparseArray<>(); + } + + @Override + public boolean startQueueParallel(FileDownloadListener listener) { + final int attachKey = listener.hashCode(); + + final List list = FileDownloadList.getImpl(). + assembleTasksToStart(attachKey, listener); + + if (onAssembledTasksToStart(attachKey, list, listener, false)) { + return false; + } + + for (BaseDownloadTask task : list) { + task.start(); + } + + return true; + } + + @Override + public boolean startQueueSerial(FileDownloadListener listener) { + final SerialHandlerCallback callback = new SerialHandlerCallback(); + final int attachKey = callback.hashCode(); + + final List list = FileDownloadList.getImpl(). + assembleTasksToStart(attachKey, listener); + + if (onAssembledTasksToStart(attachKey, list, listener, true)) { + return false; + } + + final HandlerThread serialThread = new HandlerThread( + FileDownloadUtils.formatString("filedownloader serial thread %s-%d", + listener, attachKey)); + serialThread.start(); + + final Handler serialHandler = new Handler(serialThread.getLooper(), callback); + callback.setHandler(serialHandler); + callback.setList(list); + + callback.goNext(0); + + synchronized (mRunningSerialMap) { + mRunningSerialMap.put(attachKey, serialHandler); + } + + return true; + } + + @Override + public void freezeAllSerialQueues() { + for (int i = 0; i < mRunningSerialMap.size(); i++) { + final int key = mRunningSerialMap.keyAt(i); + Handler handler = mRunningSerialMap.get(key); + freezeSerialHandler(handler); + } + } + + @Override + public void unFreezeAllSerialQueues() { + for (int i = 0; i < mRunningSerialMap.size(); i++) { + final int key = mRunningSerialMap.keyAt(i); + Handler handler = mRunningSerialMap.get(key); + unFreezeSerialHandler(handler); + } + + } + + @Override + public boolean contain(int attachKey) { + return mRunningSerialMap.get(attachKey) != null; + } + + private boolean onAssembledTasksToStart(int attachKey, final List list, + final FileDownloadListener listener, boolean isSerial) { + if (FileDownloadMonitor.isValid()) { + FileDownloadMonitor.getMonitor().onRequestStart(list.size(), true, listener); + } + + if (FileDownloadLog.NEED_LOG) { + FileDownloadLog.v(FileDownloader.class, "start list attachKey[%d] size[%d] " + + "listener[%s] isSerial[%B]", attachKey, list.size(), listener, isSerial); + } + + if (list == null || list.isEmpty()) { + FileDownloadLog.w(FileDownloader.class, "Tasks with the listener can't start, " + + "because can't find any task with the provided listener: [%s, %B]", + listener, isSerial); + + return true; + } + + return false; + + } + + + final static int WHAT_SERIAL_NEXT = 1; + final static int WHAT_FREEZE = 2; + final static int WHAT_UNFREEZE = 3; + + + private class SerialHandlerCallback implements Handler.Callback { + private Handler handler; + private List list; + private int runningIndex = 0; + private SerialFinishListener serialFinishListener; + + SerialHandlerCallback() { + serialFinishListener = + new SerialFinishListener(new WeakReference<>(this)); + } + + public void setHandler(final Handler handler) { + this.handler = handler; + } + + public void setList(List list) { + this.list = list; + } + + @Override + public boolean handleMessage(final Message msg) { + if (msg.what == WHAT_SERIAL_NEXT) { + if (msg.arg1 >= list.size()) { + synchronized (mRunningSerialMap) { + mRunningSerialMap.remove(list.get(0).attachKey); + } + // final serial tasks + if (this.handler != null && this.handler.getLooper() != null) { + this.handler.getLooper().quit(); + this.handler = null; + this.list = null; + this.serialFinishListener = null; + } + + if (FileDownloadLog.NEED_LOG) { + FileDownloadLog.d(SerialHandlerCallback.class, "final serial %s %d", + this.list == null ? null : this.list.get(0) == null ? null : this.list.get(0).getListener(), + msg.arg1); + } + return true; + } + + runningIndex = msg.arg1; + final BaseDownloadTask stackTopTask = this.list.get(runningIndex); + synchronized (mPauseLock) { + if (!FileDownloadList.getImpl().contains(stackTopTask)) { + // pause? + if (FileDownloadLog.NEED_LOG) { + FileDownloadLog.d(SerialHandlerCallback.class, + "direct go next by not contains %s %d", stackTopTask, msg.arg1); + } + goNext(msg.arg1 + 1); + return true; + } + } + + + stackTopTask + .addFinishListener(serialFinishListener.setNextIndex(runningIndex + 1)) + .start(); + + } else if (msg.what == WHAT_FREEZE) { + freeze(); + } else if (msg.what == WHAT_UNFREEZE) { + unfreeze(); + } + return true; + } + + public void freeze() { + list.get(runningIndex).removeFinishListener(serialFinishListener); + handler.removeCallbacksAndMessages(null); + } + + public void unfreeze() { + goNext(runningIndex); + } + + private void goNext(final int nextIndex) { + if (this.handler == null || this.list == null) { + FileDownloadLog.w(this, "need go next %d, but params is not ready %s %s", + nextIndex, this.handler, this.list); + return; + } + + Message nextMsg = this.handler.obtainMessage(); + nextMsg.what = WHAT_SERIAL_NEXT; + nextMsg.arg1 = nextIndex; + if (FileDownloadLog.NEED_LOG) { + FileDownloadLog.d(SerialHandlerCallback.class, "start next %s %s", + this.list == null ? null : this.list.get(0) == null ? null : + this.list.get(0).getListener(), nextMsg.arg1); + } + this.handler.sendMessage(nextMsg); + } + } + + private static class SerialFinishListener implements BaseDownloadTask.FinishListener { + private final WeakReference wSerialHandlerCallback; + + private SerialFinishListener(WeakReference wSerialHandlerCallback) { + this.wSerialHandlerCallback = wSerialHandlerCallback; + } + + private int nextIndex; + + public BaseDownloadTask.FinishListener setNextIndex(int index) { + this.nextIndex = index; + return this; + } + + @Override + public void over(final BaseDownloadTask task) { + if (wSerialHandlerCallback != null && wSerialHandlerCallback.get() != null) { + wSerialHandlerCallback.get().goNext(this.nextIndex); + } + } + } + + private void freezeSerialHandler(Handler handler) { + handler.sendEmptyMessage(WHAT_FREEZE); + } + + private void unFreezeSerialHandler(Handler handler) { + handler.sendEmptyMessage(WHAT_UNFREEZE); + } +}