refactor: replace RxDownload with my own implementation

This commit is contained in:
hyb1996
2017-12-06 22:35:53 +08:00
parent 093c299fc1
commit 98e0e39e13
5 changed files with 143 additions and 39 deletions

View File

@@ -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<ResponseBody> download(@Url String url);
}

View File

@@ -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<String, VolatileBox<Boolean>> 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<Integer> download(String url, String path) {
File file = new File(path);
Mission mission = new Mission(url, file.getName(), file.getParent());
PublishSubject<Integer> 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<Boolean> status = mDownloadStatuses.get(url);
if (status != null) {
status.set(false);
}
}
private class DownloadTask {
private String mUrl;
private String mPath;
private VolatileBox<Boolean> mStatus;
private InputStream mInputStream;
private FileOutputStream mFileOutputStream;
private PublishSubject<Integer> mProgress;
public DownloadTask(String url, String path) {
mUrl = url;
mPath = path;
mStatus = new VolatileBox<>(true);
VolatileBox<Boolean> 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<Integer> progress() {
return mProgress;
}
}
}

View File

@@ -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<ScriptFile> download(String url, String path, MaterialDialog progressDialog) {
PublishSubject<ScriptFile> subject = PublishSubject.create();
DownloadManager.getInstance(mContext).download(url, path)
DownloadManager.getInstance().download(url, path)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(progressDialog::setProgress)
.doOnComplete(() -> {

View File

@@ -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;