Why OkDownload

Identify the requirements of the download framework you are looking for: support for concurrent download, multipoint file fragmentation, breakpoint continuation download, dynamic callback, use okHttp, Retrofit, high stability (star number). Online to find a circle, there are a lot of analysis of multi-threaded breakpoint continuation download principle of the article, the core content is very clear, the core code also has, but a practical download framework, to meet and compatible with as many business scenarios as possible, is the most time-consuming. PRDownloader is a network request initiated by HttpClient. PRDownloader is a network request initiated by HttpClient. PRDownloader is a network request initiated by HttpClient. It also needs to be launched in Application, which makes it not an option for T0.

At first, I was a little hesitant to use OkDownload library, because I saw that it had not been updated for more than a year, and no one responded to the issue, so it was basically in a state of no maintenance. But there was no other better choice, so I just stuck to it and started to step on the hole.

1. OkDownloadbasis

Let’s take a look at the basics:

/ / core code library implementation 'com. Liulishuo. Okdownload: okdownload: {latest_version}' / / sqlite download information database cache (breakpoint downloading use), Without introducing memory cache is used implementation 'com. Liulishuo. Okdownload: sqlite: {latest_version}' / / using okhttp request network must introduce the implementation 'com. Liulishuo. Okdownload: okhttp: {latest_version}' / / this is filedownloader download file management, Can not introduce implementation 'com. Liulishuo. Okdownload: filedownloader: {latest_version}' / / provide kotlin extension, Can not introduce implementation 'com. Liulishuo. Okdownload: KTX {latest_version}' / / must introduce the implementation 'com. Squareup. Okhttp3: okhttp: 3.10.0'Copy the code

You can use OkDownload after you build the dependency library. The basic usage can be learned by downloading a demo from Github, which is not repeated here.

Let me illustrate the problem with a few business scenarios.

2. Silent download

A silent download means that the task is not attached to the main thread and the callback is attached to the child thread after the download.

private DownloadTask createTask(a) {
    String filePath = '... ';
    return new DownloadTask.Builder(url, filePath, fileName)
            .setPriority(0)
            // The minimum interval for callback progress (milliseconds)
            .setMinIntervalMillisCallbackProcess(300)
            // Do callback to main thread after task download
            .setAutoCallbackToUIThread(false)
            .build();
}
Copy the code

This is how tasks are created, and it can also set attributes such as task execution priority.

  • Single Task execution
// Execute tasks asynchronously
task.enqueue(listener);
/ / cancel the tasks
task.cancel();
Copy the code
  • Multiple tasks can be executed in parallel in several ways:
  1. useDownloadContextClass to determine whether all tasks have been downloaded
DownloadContext.Builder builder = new DownloadContext.QueueSet()
        .setParentPathFile(parentFile)
        .setMinIntervalMillisCallbackProcess(150)
        .commit();
builder.bindSetTask(task1);
//task.addTag(key, value) is used to pass the parameters required for downloading tasks
builder.bindSetTask(task2.addTag(key, value));
builder.bindSetTask(task3)
//contextListener: Callback at the end of a single task and all tasksbuilder.setListener(contextListener); DownloadContext context = builder.build(); context.startOnParallel(listener); .// stop
context.stop();
Copy the code
  1. Using DownloadTask. The enqueue (tasks, the listener)

  2. Write a singleton class and use the UnifiedListenerManager class to manage tasks. It can manage tasks and listeners in pairs and dynamically add or delete listeners on a task

public class GlobalTaskManager {
    private UnifiedListenerManager2 manager;
    private DownloadListener mListener;

    private GlobalTaskManager(a) {
        manager = new UnifiedListenerManager2();
    }

    private static class ClassHolder {
        private static final GlobalTaskManager INSTANCE = new GlobalTaskManager();
    }

    public static GlobalTaskManager getInstance(a) {
        return ClassHolder.INSTANCE;
    }

    public void addAutoRemoveListenersWhenTaskEnd(int id) {
        manager.addAutoRemoveListenersWhenTaskEnd(id);
    }

    public void addAutoRemoveListenersWhenTaskEnd(int[] ids) {
        for (intid : ids) { manager.addAutoRemoveListenersWhenTaskEnd(id); }}public void attachListener(@NonNull DownloadTask task, @NonNull DownloadListener listener) {
        manager.attachListener(task, listener);
    }

    public void attachAndEnqueueIfNotRun(@NonNull DownloadTask task, @NonNull DownloadListener listener) {
        manager.detachListener(task.getId());
        manager.attachAndEnqueueIfNotRun(task, listener);
    }

    public void enqueueTask(@NonNull DownloadTask task, @NonNull DownloadListener listener) {
        manager.enqueueTaskWithUnifiedListener(task, listener);
    }

    public void enqueueTasks(@NonNull ArrayList<DownloadTask> tasks, @NonNull DownloadListener listener) {
        mListener = listener;
        manager.enqueueTasks(tasks, listener);
    }

    public DownloadListener getDownloadListener(a) {
        return mListener;
    }

    static class UnifiedListenerManager2 extends UnifiedListenerManager {

        public void enqueueTasks(@NonNull ArrayList<DownloadTask> tasks, @NonNull DownloadListener listener) {
            for (DownloadTask task : tasks) {
                attachListener(task, listener);
                if (StatusUtil.isSameTaskPendingOrRunning(task)) {

                    tasks.remove(task);
                }
            }

            DownloadTask[] _tasks = newDownloadTask[tasks.size()]; DownloadTask.enqueue(tasks.toArray(_tasks), getHostListener()); }}}Copy the code

3. Plug-in download service scenario

Multi-task parallel silent download starts on the home page. Click item of the list on the home page to enter the download page of a single task. At this time, download status of the current task needs to be obtained and the download progress should be synchronized in real time.

There are several ways to implement this feature, please bear with me.

Use the ones mentioned aboveUnifiedListenerManagerTo implement the
//LoadingActivity.java. mDownLoadTask = createTask().addTag(TASK_BEAN, data); DownloadTask lastTask = OkDownload.with().downloadDispatcher().findSameTask(mDownLoadTask);// Check whether the task already exists in OkDownload
if(lastTask ! =null) {
    mDownLoadTask = lastTask;
}

BreakpointInfo info = StatusUtil.getCurrentInfo(mDownLoadTask);
if(info ! =null) {
    // If task information exists, the task progress is synchronized first
    calcProgressToView(info.getTotalOffset(), info.getTotalLength(), true);
}

StatusUtil.Status status = StatusUtil.getStatus(mDownLoadTask);
if (status == StatusUtil.Status.COMPLETED
        || ((status == StatusUtil.Status.RUNNING || status == StatusUtil.Status.PENDING)
                && PrefUtil.getKeyBoolean(PrefKeys.DOWNLOAD_TASK_HAS_END + mDownLoadTask.getFilename(), false))) {
    //1. The task downloaded silently has already gone to the taskEnd callback, so there is no need to replace the listener (extreme case)
    //2. Task downloaded silently has completed (extreme case)
    calcProgressToView(1.1.true);
    downloadFinish();
} else {
    // Task has not been downloaded or is downloadingGlobalTaskManager.getInstance().attachAndEnqueueIfNotRun(mDownLoadTask, getListener()); GlobalTaskManager.getInstance().addAutoRemoveListenersWhenTaskEnd(mDownLoadTask.getId()); }...private DownloadListener4 getListener(a) {
    return newDownloadListener4() { ... }}Copy the code
Using DownloadTask. ReplaceListener method
StatusUtil.Status status = StatusUtil.getStatus(mDownLoadTask);
if (status == StatusUtil.Status.COMPLETED
        || ((status == StatusUtil.Status.RUNNING || status == StatusUtil.Status.PENDING)
                && PrefUtil.getKeyBoolean(PrefKeys.DOWNLOAD_TASK_HAS_END + mDownLoadTask.getFilename(), false))) {... }else if (status == StatusUtil.Status.RUNNING || status == StatusUtil.Status.PENDING) {
    DownloadListener4 listener = getListener();
    listener.setAlwaysRecoverAssistModelIfNotSet(true);
    mDownLoadTask.replaceListener(listener);
} else {
    mDownLoadTask.enqueue(getListener());
}
Copy the code

You can do both, but there’s a big problem with thatcreateTask()If the background silent download setting does not call back the main thread while inLoadingActivityThe file downloaded by the task is incomplete, and no error is reported. It took me almost two days to discover and verify this.

The silent download listener records the download progress and saves it to the MapLoadingActivityTo obtain the progress periodically
if (status == StatusUtil.Status.PENDING || status == StatusUtil.Status.RUNNING) {
    // Start the countdown
    mSubscription = Observable.interval(0.1, TimeUnit.SECONDS)// Delay 0, interval 1s, in seconds
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Action1<Long>() {
                @Override
                public void call(Long time) {
                    DownloadListener listener = GlobalTaskManager.getInstance().getDownloadListener();
                    if (listener instanceof DownLoadListener) {
                        DownLoadListener listener = (DownLoadListener) listener;
                        long currentOffset = 0;
                        if(listener.getCurrentOffset(task.getFileName()) ! =null) {
                            currentOffset = listener.getCurrentOffset(task.getFileName());
                        }
                        long totalLength = 0;
                        if(listener.getTotalLength(task.getFileName()) ! =null) {
                            totalLength = listener.getTotalLength(task.getFileName());
                        }

                        calcProgressToView(currentOffset, totalLength, false);

                        String endStatus = listener.getTaskEndStatus(task.getFileName());

                        if (TextUtils.isEmpty(endStatus)) {
                            return;
                        }

                        if ("success".equals(endStatus)) { downloadFinish(); }}}},new Action1<Throwable>() {
                @Override
                public void call(Throwable throwable) {}}); }Copy the code

java.io.IOException: The current offset on block-info isn't update correct, 13439740 ! = 14072131 on 2

That’s the basic usage of OkDownload.

At the end

Since you see this, show that the article is still attractive to you, help point a thumbs-up and then go, thank you!

Pay attention to my public account “left behind programmers”, continue to output more content!

Write by yourself, decompose the requirements of each module in the project, and solve the problems encountered by checking the documents and searching the Cocos community. Finally, the following wechat mini game named Idiom Jinyiwei was launched on wechat. Welcome everyone to scan the code for experience and develop their own mini game as a reference project template