Skip to content

Commit

Permalink
refactor(FileDownloader): simplify FileDownloader, wrap the handler t…
Browse files Browse the repository at this point in the history
…o handle affairs of queues

Refs #283
  • Loading branch information
Jacksgong committed Aug 20, 2016
1 parent 74ef14e commit 09fc623
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 219 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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<BaseDownloadTask> needRestartList;
Expand All @@ -275,8 +277,9 @@ public boolean callback(IDownloadEvent event) {
needRestartList = (List<BaseDownloadTask>) 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;
}
Expand All @@ -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() ==
Expand All @@ -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);
Expand All @@ -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();
}
}

Expand Down
208 changes: 6 additions & 202 deletions library/src/main/java/com/liulishuo/filedownloader/FileDownloader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}


Expand Down Expand Up @@ -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<BaseDownloadTask> 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<BaseDownloadTask> 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<BaseDownloadTask> 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<Handler> 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<BaseDownloadTask> 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<BaseDownloadTask> 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<SerialHandlerCallback> wSerialHandlerCallback;

private SerialFinishListener(WeakReference<SerialHandlerCallback> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* 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);
}
Loading

0 comments on commit 09fc623

Please sign in to comment.