From 98e0e39e134097d62147f5c672169e5afc845d01 Mon Sep 17 00:00:00 2001 From: hyb1996 <946994919@qq.com> Date: Wed, 6 Dec 2017 22:35:53 +0800 Subject: [PATCH] refactor: replace RxDownload with my own implementation --- app/build.gradle | 4 - .../scriptdroid/network/api/DownloadApi.java | 19 +++ .../network/download/DownloadManager.java | 154 ++++++++++++++---- .../ui/common/ScriptOperations.java | 4 +- .../scriptdroid/ui/main/MainActivity.java | 1 + 5 files changed, 143 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/com/stardust/scriptdroid/network/api/DownloadApi.java diff --git a/app/build.gradle b/app/build.gradle index a9404786..753f23eb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -106,10 +106,6 @@ dependencies { // RxJava compile "io.reactivex.rxjava2:rxjava:2.1.2" compile 'io.reactivex.rxjava2:rxandroid:2.0.1' - // RxDownload - compile('zlc.season:rxdownload3:1.0.8', { - exclude group: 'com.android.support' - }) // Retrofit compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0' diff --git a/app/src/main/java/com/stardust/scriptdroid/network/api/DownloadApi.java b/app/src/main/java/com/stardust/scriptdroid/network/api/DownloadApi.java new file mode 100644 index 00000000..997fe109 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/network/api/DownloadApi.java @@ -0,0 +1,19 @@ +package com.stardust.scriptdroid.network.api; + +import io.reactivex.Observable; +import okhttp3.ResponseBody; +import retrofit2.http.GET; +import retrofit2.http.Streaming; +import retrofit2.http.Url; + +/** + * Created by Stardust on 2017/12/6. + */ + +public interface DownloadApi { + + @GET + @Streaming + Observable download(@Url String url); + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/network/download/DownloadManager.java b/app/src/main/java/com/stardust/scriptdroid/network/download/DownloadManager.java index 118a9f5b..d978d025 100644 --- a/app/src/main/java/com/stardust/scriptdroid/network/download/DownloadManager.java +++ b/app/src/main/java/com/stardust/scriptdroid/network/download/DownloadManager.java @@ -1,17 +1,24 @@ package com.stardust.scriptdroid.network.download; -import android.content.Context; +import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; +import com.stardust.concurrent.VolatileBox; +import com.stardust.scriptdroid.network.NodeBB; +import com.stardust.scriptdroid.network.api.DownloadApi; -import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.net.URLDecoder; +import java.util.concurrent.ConcurrentHashMap; import io.reactivex.Observable; +import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.PublishSubject; -import retrofit2.http.Url; -import zlc.season.rxdownload3.RxDownload; -import zlc.season.rxdownload3.core.Failed; -import zlc.season.rxdownload3.core.Mission; -import zlc.season.rxdownload3.core.Succeed; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import retrofit2.Retrofit; /** * Created by Stardust on 2017/10/20. @@ -21,16 +28,36 @@ public class DownloadManager { private static DownloadManager sInstance; - private Context mContext; + private static final int RETRY_COUNT = 3; + private Retrofit mRetrofit; + private DownloadApi mDownloadApi; + private ConcurrentHashMap> mDownloadStatuses = new ConcurrentHashMap<>(); - - public DownloadManager(Context context) { - mContext = context; + public DownloadManager() { + mRetrofit = new Retrofit.Builder() + .baseUrl(NodeBB.BASE_URL) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .client(new OkHttpClient.Builder() + .addInterceptor(chain -> { + Request request = chain.request(); + Response response = chain.proceed(request); + int tryCount = 0; + while (!response.isSuccessful() && tryCount < RETRY_COUNT) { + tryCount++; + response = chain.proceed(request); + } + return response; + }) + .build() + ) + .build(); + mDownloadApi = mRetrofit.create(DownloadApi.class); } - public static DownloadManager getInstance(Context context) { + + public static DownloadManager getInstance() { if (sInstance == null) { - sInstance = new DownloadManager(context); + sInstance = new DownloadManager(); } return sInstance; } @@ -45,28 +72,89 @@ public class DownloadManager { } public Observable download(String url, String path) { - File file = new File(path); - Mission mission = new Mission(url, file.getName(), file.getParent()); - PublishSubject progress = PublishSubject.create(); - RxDownload.INSTANCE.create(mission) - .subscribe(status -> { - if (status.getTotalSize() > 0) { - int p = (int) Math.floor((float) status.getDownloadSize() / status.getTotalSize() * 100); - progress.onNext(p); - } - if (status instanceof Succeed) { - progress.onComplete(); - RxDownload.INSTANCE.delete(mission); - } else if (status instanceof Failed) { - progress.onError(((Failed) status).getThrowable()); - } - }); - RxDownload.INSTANCE.start(mission).subscribe(); - return progress; + DownloadTask task = new DownloadTask(url, path); + mDownloadApi.download(url) + .subscribeOn(Schedulers.io()) + .subscribe(task::start); + return task.progress(); } public void cancelDownload(String url) { - RxDownload.INSTANCE.stop(url); - RxDownload.INSTANCE.delete(url); + VolatileBox status = mDownloadStatuses.get(url); + if (status != null) { + status.set(false); + } + } + + private class DownloadTask { + + private String mUrl; + private String mPath; + private VolatileBox mStatus; + private InputStream mInputStream; + private FileOutputStream mFileOutputStream; + private PublishSubject mProgress; + + public DownloadTask(String url, String path) { + mUrl = url; + mPath = path; + mStatus = new VolatileBox<>(true); + VolatileBox previous = mDownloadStatuses.put(mUrl, mStatus); + if (previous != null) + previous.set(false); + mProgress = PublishSubject.create(); + } + + public void start(ResponseBody body) throws IOException { + byte[] buffer = new byte[4096]; + mFileOutputStream = new FileOutputStream(mPath); + mInputStream = body.byteStream(); + long total = body.contentLength(); + long read = 0; + while (read < total) { + if (!mStatus.get()) { + onCancel(); + return; + } + int len = mInputStream.read(buffer); + if (len == -1) { + break; + } + read += len; + mFileOutputStream.write(buffer, 0, len); + mProgress.onNext((int) (100 * read / total)); + } + mProgress.onComplete(); + recycle(); + } + + private void onCancel() throws IOException { + recycle(); + // TODO: 2017/12/6 notify? + } + + public void recycle() { + mDownloadStatuses.remove(mUrl); + if (mInputStream != null) { + try { + mInputStream.close(); + } catch (IOException ignored) { + + } + } + if (mFileOutputStream != null) { + try { + mFileOutputStream.close(); + } catch (IOException ignored) { + } + } + + } + + public Observable progress() { + return mProgress; + } + + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/common/ScriptOperations.java b/app/src/main/java/com/stardust/scriptdroid/ui/common/ScriptOperations.java index 4f5a6b0b..16bbfcf0 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/common/ScriptOperations.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/common/ScriptOperations.java @@ -265,13 +265,13 @@ public class ScriptOperations { .title(fileName) .cancelable(false) .positiveText(R.string.text_cancel_download) - .onPositive((dialog, which) -> DownloadManager.getInstance(mContext).cancelDownload(url)) + .onPositive((dialog, which) -> DownloadManager.getInstance().cancelDownload(url)) .show(); } public Observable download(String url, String path, MaterialDialog progressDialog) { PublishSubject subject = PublishSubject.create(); - DownloadManager.getInstance(mContext).download(url, path) + DownloadManager.getInstance().download(url, path) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(progressDialog::setProgress) .doOnComplete(() -> { diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java index 91e8ccee..8bf94647 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java @@ -27,6 +27,7 @@ import com.stardust.scriptdroid.Pref; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.autojs.AutoJs; import com.stardust.scriptdroid.ui.common.NotAskAgainDialog; +import com.stardust.scriptdroid.ui.common.ScriptOperations; import com.stardust.scriptdroid.ui.doc.DocsFragment_; import com.stardust.scriptdroid.ui.floating.FloatyWindowManger; import com.stardust.scriptdroid.storage.file.StorageFileProvider;