242 lines
8.5 KiB
Java
242 lines
8.5 KiB
Java
package com.gh.download.cache;
|
|
|
|
import com.danikula.videocache.file.FileNameGenerator;
|
|
import com.danikula.videocache.file.Md5FileNameGenerator;
|
|
import com.gh.common.AppExecutor;
|
|
import com.halo.assistant.HaloApp;
|
|
import com.shuyu.gsyvideoplayer.utils.StorageUtils;
|
|
|
|
import java.io.File;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
import io.reactivex.Observable;
|
|
import io.reactivex.ObservableEmitter;
|
|
import io.reactivex.ObservableOnSubscribe;
|
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
import io.reactivex.schedulers.Schedulers;
|
|
import okhttp3.Call;
|
|
import okhttp3.Callback;
|
|
import okhttp3.OkHttpClient;
|
|
import okhttp3.Request;
|
|
import okhttp3.Response;
|
|
|
|
public class CacheManager {
|
|
private static final AtomicReference<CacheManager> INSTANCE = new AtomicReference<>();
|
|
private volatile ConcurrentHashMap<String, Call> downCalls;
|
|
private OkHttpClient mClient;
|
|
private File cacheDirectory = StorageUtils.getIndividualCacheDirectory(HaloApp.getInstance().getApplication());
|
|
private FileNameGenerator generator = new Md5FileNameGenerator();
|
|
private final String TEMP_POSTFIX = ".download";
|
|
// private final int preLength = 5 * 1024 * 1024;//预加载大小
|
|
|
|
|
|
public static CacheManager getInstance() {
|
|
for (; ; ) {
|
|
CacheManager current = INSTANCE.get();
|
|
if (current != null) {
|
|
return current;
|
|
}
|
|
current = new CacheManager();
|
|
if (INSTANCE.compareAndSet(null, current)) {
|
|
return current;
|
|
}
|
|
}
|
|
}
|
|
|
|
private CacheManager() {
|
|
downCalls = new ConcurrentHashMap<>();
|
|
mClient = new OkHttpClient.Builder().build();
|
|
}
|
|
|
|
private synchronized ConcurrentHashMap<String, Call> getDownCalls() {
|
|
return downCalls;
|
|
}
|
|
|
|
public void download(String url, CacheObserver cacheObserver) {
|
|
//当前url已下载完成则不再下载
|
|
for (File file : getAllFile()) {
|
|
if (file.getName().equals(generator.generate(url))) {
|
|
return;
|
|
}
|
|
}
|
|
Observable.just(url)
|
|
.filter(s -> !getDownCalls().containsKey(s))//call的map已经有了,就证明正在下载,则这次不下载
|
|
.flatMap(s -> Observable.just(createDownInfo(s)))
|
|
.map(this::getRealFileName)
|
|
.flatMap(cacheInfo -> Observable.create(new DownloadSubscribe(cacheInfo)))
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
.subscribeOn(Schedulers.io())
|
|
.subscribe(cacheObserver);
|
|
|
|
}
|
|
|
|
public void cancel(String url) {
|
|
Call call = getDownCalls().get(url);
|
|
if (call != null) {
|
|
call.cancel();
|
|
}
|
|
getDownCalls().remove(url);
|
|
}
|
|
|
|
/**
|
|
* 创建DownInfo
|
|
*
|
|
* @param url 请求网址
|
|
* @return DownInfo
|
|
*/
|
|
private CacheInfo createDownInfo(String url) {
|
|
CacheInfo cacheInfo = new CacheInfo(url);
|
|
long contentLength = getContentLength(url);//获得文件大小
|
|
cacheInfo.setTotal(contentLength);
|
|
String fileName = generator.generate(url) + TEMP_POSTFIX;
|
|
cacheInfo.setFileName(fileName);
|
|
return cacheInfo;
|
|
}
|
|
|
|
private CacheInfo getRealFileName(CacheInfo cacheInfo) {
|
|
String fileName = cacheInfo.getFileName();
|
|
long downloadLength = 0;
|
|
if (!cacheDirectory.exists()) {
|
|
cacheDirectory.mkdir();
|
|
}
|
|
File file = new File(cacheDirectory, fileName);
|
|
if (file.exists()) {
|
|
//找到了文件,代表已经下载过,则获取其长度
|
|
downloadLength = file.length();
|
|
}
|
|
//设置改变过的文件名/大小
|
|
cacheInfo.setProgress(downloadLength);
|
|
cacheInfo.setFileName(file.getName());
|
|
return cacheInfo;
|
|
}
|
|
|
|
|
|
private class DownloadSubscribe implements ObservableOnSubscribe<CacheInfo> {
|
|
private CacheInfo cacheInfo;
|
|
|
|
public DownloadSubscribe(CacheInfo cacheInfo) {
|
|
this.cacheInfo = cacheInfo;
|
|
}
|
|
|
|
@Override
|
|
public void subscribe(ObservableEmitter<CacheInfo> e) throws Exception {
|
|
String url = cacheInfo.getUrl();
|
|
final long[] downloadLength = {cacheInfo.getProgress()};//已经下载好的长度
|
|
long contentLength = cacheInfo.getTotal();//文件的总长度
|
|
// if (downloadLength[0] >= preLength) {
|
|
if (downloadLength[0] >= contentLength) {
|
|
e.onComplete();
|
|
return;
|
|
}
|
|
e.onNext(cacheInfo);
|
|
Request request = new Request.Builder()
|
|
// .addHeader("RANGE", "bytes=" + downloadLength[0] + "-" + (contentLength > preLength ? preLength : contentLength))
|
|
.addHeader("RANGE", "bytes=" + downloadLength[0] + "-" + contentLength)
|
|
.url(url)
|
|
.build();
|
|
Call call = mClient.newCall(request);
|
|
getDownCalls().put(url, call);
|
|
call.enqueue(new Callback() {
|
|
@Override
|
|
public void onFailure(Call call, IOException e) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onResponse(Call call, Response response) throws IOException {
|
|
File file = new File(cacheDirectory, cacheInfo.getFileName());
|
|
InputStream is = null;
|
|
FileOutputStream fileOutputStream = null;
|
|
try {
|
|
is = response.body().byteStream();
|
|
fileOutputStream = new FileOutputStream(file, true);
|
|
byte[] buffer = new byte[2048];
|
|
int len;
|
|
while (downCalls.get(url) != null && !call.isCanceled() && (len = is.read(buffer)) != -1) {
|
|
fileOutputStream.write(buffer, 0, len);
|
|
downloadLength[0] += len;
|
|
cacheInfo.setProgress(downloadLength[0]);
|
|
e.onNext(cacheInfo);
|
|
}
|
|
fileOutputStream.flush();
|
|
getDownCalls().remove(url);
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
} finally {
|
|
if (is != null) {
|
|
is.close();
|
|
}
|
|
if (fileOutputStream != null) {
|
|
fileOutputStream.close();
|
|
}
|
|
}
|
|
if (file.length() == contentLength) {
|
|
file.renameTo(new File(file.getPath().substring(0, file.getPath().lastIndexOf(TEMP_POSTFIX))));
|
|
}
|
|
e.onComplete();
|
|
}
|
|
});
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取下载长度
|
|
*
|
|
* @param downloadUrl
|
|
* @return
|
|
*/
|
|
public long getContentLength(String downloadUrl) {
|
|
long contentLength = CacheInfo.TOTAL_ERROR;
|
|
Request request = new Request.Builder()
|
|
.url(downloadUrl)
|
|
.build();
|
|
Response response = null;
|
|
try {
|
|
response = mClient.newCall(request).execute();
|
|
if (response.isSuccessful() && response.body() != null) {
|
|
long length = response.body().contentLength();
|
|
contentLength = length == 0 ? CacheInfo.TOTAL_ERROR : length;
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
} finally {
|
|
if (response != null) {
|
|
response.close();
|
|
}
|
|
}
|
|
return contentLength;
|
|
}
|
|
|
|
public void removeAllCall() {
|
|
AppExecutor.getIoExecutor().execute(() -> {
|
|
for (Map.Entry<String, Call> entry : getDownCalls().entrySet()) {
|
|
entry.getValue().cancel();
|
|
}
|
|
AppExecutor.getUiExecutor().execute(() -> getDownCalls().clear());
|
|
});
|
|
}
|
|
|
|
private List<File> getAllFile() {
|
|
try {
|
|
if (cacheDirectory.exists() && cacheDirectory.isDirectory()) {
|
|
File[] files = cacheDirectory.listFiles();
|
|
return Arrays.asList(files);
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
return new ArrayList<>();
|
|
}
|
|
return new ArrayList<>();
|
|
}
|
|
|
|
}
|