Files
assistant-android/app/src/main/java/com/gh/download/cache/CacheManager.java
2020-02-03 17:54:40 +08:00

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<>();
}
}