移除`getDownloadTask(String url)`、`getGroupTask(List<String>urls)`、`getFtpDirTask(String path)`等获取任务的api
修复使用`Content-Disposition`的文件名时,第一次下载无法重命名文件的问题
修复使用`Content-Disposition`的文件名时,多次重命名文件的问题
v3.6.6
laoyuyu 6 years ago
parent 0a447787ea
commit 8c0ef0f879
  1. 11
      Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java
  2. 2
      Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java
  3. 4
      Aria/src/main/java/com/arialyy/aria/core/download/DNormalDelegate.java
  4. 61
      Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java
  5. 101
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChunkedInputStream.java
  6. 42
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java
  7. 15
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java
  8. 58
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java
  9. 67
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java
  10. 2
      Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java
  11. 4
      Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java
  12. 3
      Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java
  13. 2
      Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java
  14. 61
      Aria/src/main/java/com/arialyy/aria/orm/AbsDelegate.java
  15. 1
      Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java
  16. 8
      Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java
  17. 9
      Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java
  18. 4
      Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java
  19. 2
      Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java
  20. 83
      Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java
  21. 17
      Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java
  22. 18
      Aria/src/main/java/com/arialyy/aria/util/DbDataHelper.java
  23. 9
      DEV_LOG.md
  24. 2
      README.md
  25. 2
      app/src/main/assets/aria_config.xml
  26. 37
      app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java
  27. 18
      app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java
  28. 15
      app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java
  29. 2
      app/src/main/java/com/arialyy/simple/core/test/AnyRunActivity.java
  30. 2
      build.gradle

@ -141,10 +141,11 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
} }
resetState(); resetState();
mConstance.resetState(); mConstance.resetState();
ALog.d(TAG, "path: " + getFilePath());
checkRecord(); checkRecord();
mConstance.isRunning = true; mConstance.isRunning = true;
mConstance.TASK_RECORD = mRecord; mConstance.TASK_RECORD = mRecord;
if (!mTaskWrapper.isSupportBP()) { if (!mTaskWrapper.isSupportBP() || mTaskWrapper.asHttp().isChunked()) {
mTotalThreadNum = 1; mTotalThreadNum = 1;
} else { } else {
mTotalThreadNum = mTotalThreadNum =
@ -348,6 +349,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
if (mRecord.isOpenDynamicFile) { if (mRecord.isOpenDynamicFile) {
ThreadRecord tr = mRecord.threadRecords.get(0); ThreadRecord tr = mRecord.threadRecords.get(0);
if (tr == null) { if (tr == null) {
ALog.d(TAG, "线程记录为空,任务为新任务");
mTaskWrapper.setNewTask(true); mTaskWrapper.setNewTask(true);
mTotalThreadNum = 1; mTotalThreadNum = 1;
return; return;
@ -438,6 +440,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
if (records == null || records.size() == 0) { if (records == null || records.size() == 0) {
Properties pro = CommonUtil.loadConfig(mConfigFile); Properties pro = CommonUtil.loadConfig(mConfigFile);
if (pro.isEmpty()) { if (pro.isEmpty()) {
ALog.d(TAG, "老版本的线程记录为空,任务为新任务");
mTaskWrapper.setNewTask(true); mTaskWrapper.setNewTask(true);
return; return;
} }
@ -453,6 +456,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
} }
int threadNum = set.size(); int threadNum = set.size();
if (threadNum == 0) { if (threadNum == 0) {
ALog.d(TAG, "线程数为空,任务为新任务");
mTaskWrapper.setNewTask(true); mTaskWrapper.setNewTask(true);
return; return;
} }
@ -495,6 +499,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
mRecord.dGroupHash = ((DownloadEntity) mTaskWrapper.getEntity()).getGroupHash(); mRecord.dGroupHash = ((DownloadEntity) mTaskWrapper.getEntity()).getGroupHash();
} }
} }
ALog.d(TAG, String.format("初始化记录,任务为%s任务", isNewTask ? "新" : "旧"));
mTaskWrapper.setNewTask(isNewTask); mTaskWrapper.setNewTask(isNewTask);
} }
@ -569,6 +574,10 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
long fileLength = mEntity.getFileSize(); long fileLength = mEntity.getFileSize();
long blockSize = fileLength / mTotalThreadNum; long blockSize = fileLength / mTotalThreadNum;
Set<Integer> threads = new HashSet<>(); Set<Integer> threads = new HashSet<>();
// 如果是新任务,检查下历史记录,如果有遗留的记录,删除并删除分块文件
if (mTaskWrapper.isNewTask()) {
CommonUtil.delTaskRecord(getFilePath(), 1, true);
}
mRecord.fileLength = fileLength; mRecord.fileLength = fileLength;
if (mTaskWrapper.isNewTask() && !handleNewTask()) { if (mTaskWrapper.isNewTask() && !handleNewTask()) {

@ -316,7 +316,7 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_WRAPPER
*/ */
protected void progress(long len) { protected void progress(long len) {
synchronized (AriaManager.LOCK) { synchronized (AriaManager.LOCK) {
if (STATE.CURRENT_LOCATION > mEntity.getFileSize()) { if (STATE.CURRENT_LOCATION > mEntity.getFileSize() && !mTaskWrapper.asHttp().isChunked()) {
String errorMsg = String errorMsg =
String.format("下载失败,下载长度超出文件真实长度;currentLocation=%s, fileSize=%s", String.format("下载失败,下载长度超出文件真实长度;currentLocation=%s, fileSize=%s",
STATE.CURRENT_LOCATION, STATE.CURRENT_LOCATION,

@ -145,6 +145,10 @@ class DNormalDelegate<TARGET extends AbsDTarget> implements INormalTarget {
File newFile = new File(filePath); File newFile = new File(filePath);
mEntity.setDownloadPath(filePath); mEntity.setDownloadPath(filePath);
mEntity.setFileName(newFile.getName()); mEntity.setFileName(newFile.getName());
// 如过使用Content-Disposition中的文件名,将不会执行重命名工作
if (mTarget.getTaskWrapper().asHttp().isUseServerFileName()) {
return true;
}
if (oldFile.exists()) { if (oldFile.exists()) {
// 处理普通任务的重命名 // 处理普通任务的重命名
CommonUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath()); CommonUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath());

@ -18,6 +18,7 @@ package com.arialyy.aria.core.download;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.annotations.DownloadGroup;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.command.ICmd;
import com.arialyy.aria.core.command.normal.CancelAllCmd; import com.arialyy.aria.core.command.normal.CancelAllCmd;
@ -33,6 +34,7 @@ import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.DbDataHelper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -231,62 +233,43 @@ public class DownloadReceiver extends AbsReceiver {
* @return 如果url错误或查找不到数据则返回null * @return 如果url错误或查找不到数据则返回null
*/ */
public DownloadEntity getDownloadEntity(String downloadUrl) { public DownloadEntity getDownloadEntity(String downloadUrl) {
if (!CheckUtil.checkUrl(downloadUrl)) { CheckUtil.checkUrl(downloadUrl);
return null;
}
return DbEntity.findFirst(DownloadEntity.class, "url=? and isGroupChild='false'", downloadUrl); return DbEntity.findFirst(DownloadEntity.class, "url=? and isGroupChild='false'", downloadUrl);
} }
/** /**
* 通过下载地址和文件保存路径获取下载任务实体 * 获取组合任务实在实体
* *
* @param downloadUrl 下载地址 * @param urls 组合任务的url
* @return 如果url错误或查找不到数据返回null * @return 如果实体不存在返回null
*/ */
public DTaskWrapper getDownloadTask(String downloadUrl) { public DownloadGroupEntity getDownloadGroupEntity(List<String> urls) {
if (!CheckUtil.checkUrl(downloadUrl)) { CheckUtil.checkDownloadUrls(urls);
return null; return DbDataHelper.getDGEntity(CommonUtil.getMd5Code(urls));
}
if (!taskExists(downloadUrl)) {
return null;
}
return TaskWrapperManager.getInstance().getHttpTaskWrapper(DTaskWrapper.class, downloadUrl);
} }
/** /**
* 通过下载链接获取保存在数据库的下载任务组实体 * 获取组合任务实在实体
* *
* @param urls 任务组子任务下载地址列表 * @param key 组合任务的key{@link DownloadGroupEntity#getKey()}
* @return 返回对应的任务组实体如果查找不到对应的数据或子任务列表为null返回null * @return 如果实体不存在返回null
*/ */
public DGTaskWrapper getGroupTask(List<String> urls) { public DownloadGroupEntity getDownloadGroupEntity(String key) {
if (urls == null || urls.isEmpty()) { if (TextUtils.isEmpty(key)) {
ALog.e(TAG, "获取任务组实体失败:任务组子任务下载地址列表为null"); throw new IllegalArgumentException("key为空");
return null;
}
if (!taskExists(urls)) {
return null;
} }
return TaskWrapperManager.getInstance().getDGTaskWrapper(DGTaskWrapper.class, urls); return DbDataHelper.getDGEntity(key);
} }
/** /**
* 获取FTP文件夹下载任务实体 * 获取Ftp文件夹任务的实体
* *
* @param dirUrl FTP文件夹本地下载路径 * @param url ftp文件夹下载路径
* @return 返回对应的任务组实体如果查找不到对应的数据或路径为null返回null * @return 如果实体不存在返回null
*/ */
public DGTaskWrapper getFtpDirTask(String dirUrl) { public DownloadGroupEntity getFtpDirEntity(String url) {
if (TextUtils.isEmpty(dirUrl)) { CheckUtil.checkUrl(url);
ALog.e(TAG, "获取FTP文件夹实体失败:下载路径为null"); return DbDataHelper.getDGEntity(url);
return null;
}
boolean b =
DownloadGroupEntity.findFirst(DownloadGroupEntity.class, "groupHash=?", dirUrl) != null;
if (!b) {
return null;
}
return TaskWrapperManager.getInstance().getFtpTaskWrapper(DGTaskWrapper.class, dirUrl);
} }
/** /**

@ -0,0 +1,101 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.core.download.downloader;
import com.arialyy.aria.util.ALog;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
// Data is expected to be a series of data chunks in the form <chunk size><chunk bytes><chunk size><chunk bytes>
// The final data chunk should be a 0-length chunk which will indicate end of input.
@Deprecated
public class ChunkedInputStream extends InputStream {
private static final String TAG = "ChunkedInputStream";
private DataInputStream din;
private int unreadBytes = 0; // Bytes remaining in the current chunk of data
private byte[] singleByte = new byte[1];
private boolean endOfData = false;
private String id;
public ChunkedInputStream(InputStream in, String id) {
din = new DataInputStream(in);
this.id = id;
ALog.d(TAG, String.format("Creating chunked input for %s", id));
}
@Override
public void close() throws IOException {
ALog.d(TAG, String.format("%s: Closing chunked input.", id));
din.close();
}
@Override
public int read() throws IOException {
int bytesRead = read(singleByte, 0, 1);
return (bytesRead == -1) ? -1 : (int) singleByte[0];
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int bytesRead = 0;
if (len < 0) {
throw new IllegalArgumentException(id + ": Negative read length");
} else if (len == 0) {
return 0;
}
// If there is a current unread chunk, read from that, or else get the next chunk.
if (unreadBytes == 0) {
try {
// Find the next chunk size
unreadBytes = din.readInt();
if (ALog.DEBUG) {
ALog.d(TAG, String.format("%s: Chunk size %s", id, unreadBytes));
}
if (unreadBytes == 0) {
ALog.d(TAG, String.format("%s: Hit end of data", id));
endOfData = true;
return -1;
}
} catch (IOException err) {
throw new IOException(id + ": Error while attempting to read chunk length", err);
}
}
int bytesToRead = Math.min(len, unreadBytes);
try {
din.readFully(b, off, bytesToRead);
} catch (IOException err) {
throw new IOException(
id + ": Error while attempting to read " + bytesToRead + " bytes from current chunk",
err);
}
unreadBytes -= bytesToRead;
bytesRead += bytesToRead;
return bytesRead;
}
public boolean isEndOfData() {
return endOfData;
}
}

@ -163,27 +163,27 @@ class ConnectionHelp {
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"); "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
} }
if (conn.getRequestProperty("Accept") == null) { if (conn.getRequestProperty("Accept") == null) {
//StringBuilder accept = new StringBuilder(); StringBuilder accept = new StringBuilder();
//accept accept.append("image/gif, ")
//.append("image/gif, ") .append("image/jpeg, ")
//.append("image/jpeg, ") .append("image/pjpeg, ")
//.append("image/pjpeg, ") .append("image/webp, ")
//.append("image/webp, ") .append("image/apng, ")
//.append("image/apng, ") .append("application/xml, ")
//.append("application/xml, ") .append("application/xaml+xml, ")
//.append("application/xaml+xml, ") .append("application/xhtml+xml, ")
//.append("application/xhtml+xml, ") .append("application/x-shockwave-flash, ")
//.append("application/x-shockwave-flash, ") .append("application/x-ms-xbap, ")
//.append("application/x-ms-xbap, ") .append("application/x-ms-application, ")
//.append("application/x-ms-application, ") .append("application/msword, ")
//.append("application/msword, ") .append("application/vnd.ms-excel, ")
//.append("application/vnd.ms-excel, ") .append("application/vnd.ms-xpsdocument, ")
//.append("application/vnd.ms-xpsdocument, ") .append("application/vnd.ms-powerpoint, ")
//.append("application/vnd.ms-powerpoint, ") .append("application/signed-exchange, ")
//.append("text/plain, ") .append("text/plain, ")
//.append("text/html, ") .append("text/html, ")
//.append("*/*"); .append("*/*");
conn.setRequestProperty("Accept", "*/*"); conn.setRequestProperty("Accept", accept.toString());
} }
//302获取重定向地址 //302获取重定向地址
conn.setInstanceFollowRedirects(false); conn.setInstanceFollowRedirects(false);

@ -24,6 +24,7 @@ import com.arialyy.aria.core.inf.AbsTaskWrapper;
import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IDownloadListener;
import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException; import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.BufferedRandomAccessFile;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -83,6 +84,20 @@ public class Downloader extends AbsFileer<DownloadEntity, DTaskWrapper> {
return false; return false;
} }
/**
* 如果使用"Content-Disposition"中的文件名需要更新{@link #mTempFile}的路径
*/
void updateTempFile() {
if (!mTempFile.getPath().equals(mEntity.getDownloadPath())) {
if (!mTempFile.exists()) {
mTempFile = new File(mEntity.getDownloadPath());
} else {
boolean b = mTempFile.renameTo(new File(mEntity.getDownloadPath()));
ALog.d(TAG, String.format("更新tempFile文件名%s", b ? "成功" : "失败"));
}
}
}
@Override protected int getType() { @Override protected int getType() {
return DOWNLOAD; return DOWNLOAD;
} }

@ -36,6 +36,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.CookieManager; import java.net.CookieManager;
import java.net.HttpCookie; import java.net.HttpCookie;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
@ -77,7 +78,6 @@ public class HttpFileInfoThread implements Runnable {
ConnectionHelp.setConnectParam(mTaskDelegate, conn); ConnectionHelp.setConnectParam(mTaskDelegate, conn);
conn.setRequestProperty("Range", "bytes=" + 0 + "-"); conn.setRequestProperty("Range", "bytes=" + 0 + "-");
conn.setConnectTimeout(mConnectTimeOut); conn.setConnectTimeout(mConnectTimeOut);
//conn.setChunkedStreamingMode(0);
conn.connect(); conn.connect();
handleConnect(conn); handleConnect(conn);
} catch (IOException e) { } catch (IOException e) {
@ -149,20 +149,7 @@ public class HttpFileInfoThread implements Runnable {
String disposition = conn.getHeaderField("Content-Disposition"); String disposition = conn.getHeaderField("Content-Disposition");
if (mTaskDelegate.isUseServerFileName() && !TextUtils.isEmpty(disposition)) { if (mTaskDelegate.isUseServerFileName() && !TextUtils.isEmpty(disposition)) {
mEntity.setDisposition(CommonUtil.encryptBASE64(disposition)); mEntity.setDisposition(CommonUtil.encryptBASE64(disposition));
if (disposition.contains(";")) { handleContentDisposition(disposition);
String[] infos = disposition.split(";");
for (String info : infos) {
if (info.startsWith("filename") && info.contains("=")) {
String[] temp = info.split("=");
if (temp.length > 1) {
String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", "");
mEntity.setServerFileName(newName);
renameFile(newName);
break;
}
}
}
}
} }
CookieManager msCookieManager = new CookieManager(); CookieManager msCookieManager = new CookieManager();
List<String> cookiesHeader = headers.get("Set-Cookie"); List<String> cookiesHeader = headers.get("Set-Cookie");
@ -240,6 +227,40 @@ public class HttpFileInfoThread implements Runnable {
} }
} }
/**
* 处理"Content-Disposition"参数
* <a href=https://cloud.tencent.com/developer/section/1189916>Content-Disposition</a></>
*
* @throws UnsupportedEncodingException
*/
private void handleContentDisposition(String disposition) throws UnsupportedEncodingException {
if (disposition.contains(";")) {
String[] infos = disposition.split(";");
if (infos[0].equals("attachment")) {
for (String info : infos) {
if (info.startsWith("filename") && info.contains("=")) {
String[] temp = info.split("=");
if (temp.length > 1) {
String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", "");
mEntity.setServerFileName(newName);
renameFile(newName);
break;
}
}
}
} else if (infos[0].equals("form-data") && infos.length > 2) {
String[] temp = infos[2].split("=");
if (temp.length > 1) {
String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", "");
mEntity.setServerFileName(newName);
renameFile(newName);
}
} else {
ALog.w(TAG, "不识别的Content-Disposition参数");
}
}
}
/** /**
* 重命名文件 * 重命名文件
*/ */
@ -248,10 +269,12 @@ public class HttpFileInfoThread implements Runnable {
ALog.w(TAG, "重命名失败【服务器返回的文件名为空】"); ALog.w(TAG, "重命名失败【服务器返回的文件名为空】");
return; return;
} }
ALog.d(TAG, String.format("文件重命名为:%s", newName));
File oldFile = new File(mEntity.getDownloadPath()); File oldFile = new File(mEntity.getDownloadPath());
String newPath = oldFile.getParent() + "/" + newName; String newPath = oldFile.getParent() + "/" + newName;
if (oldFile.exists()) { if (oldFile.exists()) {
oldFile.renameTo(new File(newPath)); boolean b = oldFile.renameTo(new File(newPath));
ALog.d(TAG, String.format("文件重命名%s", b ? "成功" : "失败"));
} }
mEntity.setFileName(newName); mEntity.setFileName(newName);
mEntity.setDownloadPath(newPath); mEntity.setDownloadPath(newPath);
@ -269,7 +292,7 @@ public class HttpFileInfoThread implements Runnable {
} }
return; return;
} }
if (!CheckUtil.checkUrl(newUrl)) { if (!CheckUtil.checkUrlNotThrow(newUrl)) {
failDownload(new TaskException(TAG, "下载失败,重定向url错误"), false); failDownload(new TaskException(TAG, "下载失败,重定向url错误"), false);
return; return;
} }
@ -296,6 +319,7 @@ public class HttpFileInfoThread implements Runnable {
*/ */
private boolean checkLen(long len) { private boolean checkLen(long len) {
if (len != mEntity.getFileSize()) { if (len != mEntity.getFileSize()) {
ALog.d(TAG, "长度不一致,任务为新任务");
mTaskWrapper.setNewTask(true); mTaskWrapper.setNewTask(true);
} }
return true; return true;

@ -19,7 +19,6 @@ import com.arialyy.aria.core.common.AbsThreadTask;
import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.common.StateConstance; import com.arialyy.aria.core.common.StateConstance;
import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.config.BaseTaskConfig;
import com.arialyy.aria.core.config.DownloadConfig; import com.arialyy.aria.core.config.DownloadConfig;
import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
@ -30,6 +29,7 @@ import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.BufferedRandomAccessFile;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -44,6 +44,7 @@ import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.zip.GZIPInputStream;
/** /**
* Created by lyy on 2017/1/18. 下载线程 * Created by lyy on 2017/1/18. 下载线程
@ -87,7 +88,12 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
ConnectionHelp.setConnectParam(taskDelegate, conn); ConnectionHelp.setConnectParam(taskDelegate, conn);
conn.setConnectTimeout(getTaskConfig().getConnectTimeOut()); conn.setConnectTimeout(getTaskConfig().getConnectTimeOut());
conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数 conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数
if (taskDelegate.isChunked()) {
conn.setDoInput(true);
conn.setChunkedStreamingMode(0);
}
conn.connect(); conn.connect();
// 传递参数
if (taskDelegate.getRequestEnum() == RequestEnum.POST) { if (taskDelegate.getRequestEnum() == RequestEnum.POST) {
Map<String, String> params = taskDelegate.getParams(); Map<String, String> params = taskDelegate.getParams();
if (params != null) { if (params != null) {
@ -106,7 +112,9 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
} }
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn));
if (isOpenDynamicFile) { if (taskDelegate.isChunked()) {
readChunked(is);
} else if (isOpenDynamicFile) {
readDynamicFile(is); readDynamicFile(is);
} else { } else {
//创建可设置位置的文件 //创建可设置位置的文件
@ -114,11 +122,7 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", getTaskConfig().getBuffSize()); new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", getTaskConfig().getBuffSize());
//设置每条线程写入文件的位置 //设置每条线程写入文件的位置
file.seek(mConfig.START_LOCATION); file.seek(mConfig.START_LOCATION);
if (taskDelegate.isChunked()) {
readChunk(is, file);
} else {
readNormal(is, file); readNormal(is, file);
}
handleComplete(); handleComplete();
} }
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
@ -151,6 +155,41 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
return this; return this;
} }
/**
* 读取chunked数据
*/
private void readChunked(InputStream is) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mConfig.TEMP_FILE, true);
byte[] buffer = new byte[getTaskConfig().getBuffSize()];
int len;
while (isLive() && (len = is.read(buffer)) != -1) {
if (isBreak()) {
break;
}
if (mSpeedBandUtil != null) {
mSpeedBandUtil.limitNextBytes(len);
}
fos.write(buffer, 0, len);
progress(len);
}
handleComplete();
} catch (IOException e) {
fail(mChildCurrentLocation, new AriaIOException(TAG,
String.format("文件下载失败,savePath: %s, url: %s", mEntity.getDownloadPath(), mConfig.URL),
e));
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/** /**
* 动态长度文件读取方式 * 动态长度文件读取方式
*/ */
@ -210,16 +249,6 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
} }
} }
/**
* 读取chunk模式的文件流
*
* @deprecated 暂时先这样处理无chun
*/
private void readChunk(InputStream is, BufferedRandomAccessFile file)
throws IOException {
readNormal(is, file);
}
/** /**
* 读取普通的文件流 * 读取普通的文件流
*/ */
@ -249,6 +278,12 @@ final class HttpThreadTask extends AbsThreadTask<DownloadEntity, DTaskWrapper> {
if (!checkBlock()) { if (!checkBlock()) {
return; return;
} }
if (mTaskWrapper.asHttp().isChunked()) {
ALog.i(TAG, "任务下载完成");
STATE.isRunning = false;
mListener.onComplete();
return;
}
if (mChildCurrentLocation == mConfig.END_LOCATION) { if (mChildCurrentLocation == mConfig.END_LOCATION) {
//支持断点的处理 //支持断点的处理

@ -126,6 +126,7 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
case AbsTaskWrapper.D_FTP: case AbsTaskWrapper.D_FTP:
return new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { return new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() {
@Override public void onComplete(String url, CompleteInfo info) { @Override public void onComplete(String url, CompleteInfo info) {
mDownloader.updateTempFile();
mDownloader.start(); mDownloader.start();
} }
@ -137,6 +138,7 @@ public class SimpleDownloadUtil implements IUtil, Runnable {
case AbsTaskWrapper.D_HTTP: case AbsTaskWrapper.D_HTTP:
return new HttpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { return new HttpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() {
@Override public void onComplete(String url, CompleteInfo info) { @Override public void onComplete(String url, CompleteInfo info) {
mDownloader.updateTempFile();
mDownloader.start(); mDownloader.start();
} }

@ -44,14 +44,14 @@ class DGTaskWrapperFactory implements IGTEFactory<DownloadGroupEntity, DGTaskWra
} }
@Override public DGTaskWrapper getGTE(String groupHash, List<String> urls) { @Override public DGTaskWrapper getGTE(String groupHash, List<String> urls) {
DownloadGroupEntity entity = DbDataHelper.getHttpDGEntity(groupHash, urls); DownloadGroupEntity entity = DbDataHelper.getOrCreateHttpDGEntity(groupHash, urls);
DGTaskWrapper wrapper = new DGTaskWrapper(entity); DGTaskWrapper wrapper = new DGTaskWrapper(entity);
wrapper.setSubTaskWrapper(createDGSubTaskWrapper(entity)); wrapper.setSubTaskWrapper(createDGSubTaskWrapper(entity));
return wrapper; return wrapper;
} }
@Override public DGTaskWrapper getFTE(String ftpUrl) { @Override public DGTaskWrapper getFTE(String ftpUrl) {
DownloadGroupEntity entity = DbDataHelper.getFtpDGEntity(ftpUrl); DownloadGroupEntity entity = DbDataHelper.getOrCreateFtpDGEntity(ftpUrl);
DGTaskWrapper fte = new DGTaskWrapper(entity); DGTaskWrapper fte = new DGTaskWrapper(entity);
fte.asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(ftpUrl)); fte.asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(ftpUrl));

@ -73,6 +73,8 @@ class DTaskWrapperFactory implements INormalTEFactory<DownloadEntity, DTaskWrapp
entity.setDownloadPath(UUID.randomUUID().toString().replace("-", "")); //设置临时路径 entity.setDownloadPath(UUID.randomUUID().toString().replace("-", "")); //设置临时路径
} }
File file = new File(entity.getDownloadPath()); File file = new File(entity.getDownloadPath());
if (!entity.isComplete()) {
TaskRecord record = TaskRecord record =
TaskRecord.findFirst(TaskRecord.class, "filePath=?", entity.getDownloadPath()); TaskRecord.findFirst(TaskRecord.class, "filePath=?", entity.getDownloadPath());
if (record == null) { if (record == null) {
@ -93,6 +95,7 @@ class DTaskWrapperFactory implements INormalTEFactory<DownloadEntity, DTaskWrapp
resetEntity(entity); resetEntity(entity);
} }
} }
}
return entity; return entity;
} }

@ -15,11 +15,9 @@
*/ */
package com.arialyy.aria.core.upload; package com.arialyy.aria.core.upload;
import android.content.Context;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.command.ICmd;
import com.arialyy.aria.core.command.normal.CancelAllCmd; import com.arialyy.aria.core.command.normal.CancelAllCmd;

@ -17,7 +17,6 @@ package com.arialyy.aria.orm;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.support.v4.util.LruCache;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -28,60 +27,6 @@ import java.net.URLEncoder;
*/ */
abstract class AbsDelegate { abstract class AbsDelegate {
static final String TAG = "AbsDelegate"; static final String TAG = "AbsDelegate";
static final int CREATE_TABLE = 0;
static final int TABLE_EXISTS = 1;
static final int INSERT_DATA = 2;
static final int MODIFY_DATA = 3;
static final int FIND_DATA = 4;
static final int DEL_DATA = 6;
static final int ROW_ID = 7;
static final int RELATION = 8;
static final int DROP_TABLE = 9;
static LruCache<String, DbEntity> mDataCache = new LruCache<>(1024);
/**
* 打印数据库日志
*
* @param type {@link DelegateWrapper}
*/
static void print(int type, String sql) {
if (ALog.DEBUG) {
return;
}
String str = "";
switch (type) {
case CREATE_TABLE:
str = "创建表 >>>> ";
break;
case TABLE_EXISTS:
str = "表是否存在 >>>> ";
break;
case INSERT_DATA:
str = "插入数据 >>>> ";
break;
case MODIFY_DATA:
str = "修改数据 >>>> ";
break;
case FIND_DATA:
str = "查询数据 >>>> ";
break;
case ROW_ID:
str = "查询RowId >>> ";
break;
case RELATION:
str = "查询关联表 >>> ";
break;
case DROP_TABLE:
str = "删除表 >>> ";
break;
}
ALog.d(TAG, str.concat(sql));
}
String getCacheKey(DbEntity dbEntity) {
return dbEntity.getClass().getName() + "_" + dbEntity.rowID;
}
/** /**
* URL编码字符串 * URL编码字符串
@ -140,10 +85,6 @@ abstract class AbsDelegate {
} }
} }
void close(SQLiteDatabase db) {
//if (db != null && db.isOpen()) db.close();
}
/** /**
* 检查数据库是否关闭已经关闭的话打开数据库 * 检查数据库是否关闭已经关闭的话打开数据库
* *
@ -151,7 +92,7 @@ abstract class AbsDelegate {
*/ */
SQLiteDatabase checkDb(SQLiteDatabase db) { SQLiteDatabase checkDb(SQLiteDatabase db) {
if (db == null || !db.isOpen()) { if (db == null || !db.isOpen()) {
db = SqlHelper.INSTANCE.getWritableDatabase(); db = SqlHelper.getInstance().getDb();
} }
return db; return db;
} }

@ -33,6 +33,7 @@ import java.util.Map;
*/ */
class DBConfig { class DBConfig {
/*adb pull /mnt/sdcard/Android/data/com.arialyy.simple/files/DB/AriaLyyDb d:/db*/ /*adb pull /mnt/sdcard/Android/data/com.arialyy.simple/files/DB/AriaLyyDb d:/db*/
static boolean DEBUG = false;
static Map<String, Class> mapping = new HashMap<>(); static Map<String, Class> mapping = new HashMap<>();
static String DB_NAME; static String DB_NAME;
static int VERSION = 45; static int VERSION = 45;

@ -42,7 +42,6 @@ class DelegateCommon extends AbsDelegate {
void dropTable(SQLiteDatabase db, String tableName) { void dropTable(SQLiteDatabase db, String tableName) {
db = checkDb(db); db = checkDb(db);
String deleteSQL = String.format("DROP TABLE IF EXISTS %s", tableName); String deleteSQL = String.format("DROP TABLE IF EXISTS %s", tableName);
print(DROP_TABLE, deleteSQL);
//db.beginTransaction(); //db.beginTransaction();
db.execSQL(deleteSQL); db.execSQL(deleteSQL);
//db.setTransactionSuccessful(); //db.setTransactionSuccessful();
@ -71,7 +70,6 @@ class DelegateCommon extends AbsDelegate {
return tableExists(db, CommonUtil.getClassName(clazz)); return tableExists(db, CommonUtil.getClassName(clazz));
} }
/** /**
* 查找表是否存在 * 查找表是否存在
* *
@ -85,7 +83,6 @@ class DelegateCommon extends AbsDelegate {
String sql = String sql =
String.format("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'", String.format("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'",
tableName); tableName);
print(TABLE_EXISTS, sql);
cursor = db.rawQuery(sql, null); cursor = db.rawQuery(sql, null);
if (cursor != null && cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) {
int count = cursor.getInt(0); int count = cursor.getInt(0);
@ -97,7 +94,6 @@ class DelegateCommon extends AbsDelegate {
e.printStackTrace(); e.printStackTrace();
} finally { } finally {
closeCursor(cursor); closeCursor(cursor);
close(db);
} }
return false; return false;
} }
@ -119,11 +115,9 @@ class DelegateCommon extends AbsDelegate {
params[i] = String.format("'%s'", encodeStr(expression[i + 1])); params[i] = String.format("'%s'", encodeStr(expression[i + 1]));
} }
sql = String.format(sql, params); sql = String.format(sql, params);
print(FIND_DATA, sql);
Cursor cursor = db.rawQuery(sql, null); Cursor cursor = db.rawQuery(sql, null);
final boolean isExist = cursor.getCount() > 0; final boolean isExist = cursor.getCount() > 0;
closeCursor(cursor); closeCursor(cursor);
close(db);
return isExist; return isExist;
} }
@ -223,9 +217,7 @@ class DelegateCommon extends AbsDelegate {
String str = sb.toString(); String str = sb.toString();
str = str.substring(0, str.length() - 1) + ");"; str = str.substring(0, str.length() - 1) + ");";
print(CREATE_TABLE, str);
db.execSQL(str); db.execSQL(str);
} }
close(db);
} }
} }

@ -221,14 +221,11 @@ class DelegateFind extends AbsDelegate {
if (page != -1 && num != -1) { if (page != -1 && num != -1) {
sql = sql.concat(String.format(" LIMIT %s,%s", (page - 1) * num, num)); sql = sql.concat(String.format(" LIMIT %s,%s", (page - 1) * num, num));
} }
print(RELATION, sql);
Cursor cursor = db.rawQuery(sql, null); Cursor cursor = db.rawQuery(sql, null);
List<T> data = List<T> data =
(List<T>) newInstanceEntity(clazz, parentClazz, childClazz, cursor, pColumn, cColumn, (List<T>) newInstanceEntity(clazz, parentClazz, childClazz, cursor, pColumn, cColumn,
pColumnAlias, cColumnAlias); pColumnAlias, cColumnAlias);
closeCursor(cursor); closeCursor(cursor);
close(db);
return data; return data;
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
@ -468,7 +465,6 @@ class DelegateFind extends AbsDelegate {
*/ */
private <T extends DbEntity> List<T> exeNormalDataSql(SQLiteDatabase db, Class<T> clazz, private <T extends DbEntity> List<T> exeNormalDataSql(SQLiteDatabase db, Class<T> clazz,
String sql, String[] selectionArgs) { String sql, String[] selectionArgs) {
print(FIND_DATA, sql);
String[] temp = new String[selectionArgs.length]; String[] temp = new String[selectionArgs.length];
int i = 0; int i = 0;
for (String arg : selectionArgs){ for (String arg : selectionArgs){
@ -478,7 +474,6 @@ class DelegateFind extends AbsDelegate {
Cursor cursor = db.rawQuery(sql, temp); Cursor cursor = db.rawQuery(sql, temp);
List<T> data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null; List<T> data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null;
closeCursor(cursor); closeCursor(cursor);
close(db);
return data; return data;
} }
@ -586,7 +581,6 @@ class DelegateFind extends AbsDelegate {
i++; i++;
} }
cursor.close(); cursor.close();
close(db);
return ids; return ids;
} }
@ -610,11 +604,9 @@ class DelegateFind extends AbsDelegate {
sb.append(i >= wheres.length - 1 ? "" : ","); sb.append(i >= wheres.length - 1 ? "" : ",");
i++; i++;
} }
print(ROW_ID, sb.toString());
Cursor c = db.rawQuery(sb.toString(), null); Cursor c = db.rawQuery(sb.toString(), null);
int id = c.getColumnIndex("rowid"); int id = c.getColumnIndex("rowid");
c.close(); c.close();
close(db);
return id; return id;
} }
@ -631,7 +623,6 @@ class DelegateFind extends AbsDelegate {
boolean itemExist(SQLiteDatabase db, String tableName, long rowId) { boolean itemExist(SQLiteDatabase db, String tableName, long rowId) {
db = checkDb(db); db = checkDb(db);
String sql = "SELECT rowid FROM " + tableName + " WHERE rowid=" + rowId; String sql = "SELECT rowid FROM " + tableName + " WHERE rowid=" + rowId;
print(ROW_ID, sql);
Cursor cursor = db.rawQuery(sql, null); Cursor cursor = db.rawQuery(sql, null);
boolean isExist = cursor.getCount() > 0; boolean isExist = cursor.getCount() > 0;
cursor.close(); cursor.close();

@ -49,9 +49,7 @@ class DelegateUpdate extends AbsDelegate {
params[i] = String.format("'%s'", encodeStr(expression[i + 1])); params[i] = String.format("'%s'", encodeStr(expression[i + 1]));
} }
sql = String.format(sql, params); sql = String.format(sql, params);
print(DEL_DATA, sql);
db.execSQL(sql); db.execSQL(sql);
close(db);
} }
/** /**
@ -66,7 +64,6 @@ class DelegateUpdate extends AbsDelegate {
} else { } else {
ALog.e(TAG, "更新记录失败,记录没有属性字段"); ALog.e(TAG, "更新记录失败,记录没有属性字段");
} }
close(db);
} }
/** /**
@ -139,7 +136,6 @@ class DelegateUpdate extends AbsDelegate {
} else { } else {
ALog.e(TAG, "保存记录失败,记录没有属性字段"); ALog.e(TAG, "保存记录失败,记录没有属性字段");
} }
close(db);
} }
/** /**

@ -36,7 +36,7 @@ public class DelegateWrapper {
private DelegateWrapper(Context context) { private DelegateWrapper(Context context) {
SqlHelper helper = SqlHelper.init(context.getApplicationContext()); SqlHelper helper = SqlHelper.init(context.getApplicationContext());
mDb = helper.getWritableDatabase(); mDb = helper.getDb();
mDManager = DelegateManager.getInstance(); mDManager = DelegateManager.getInstance();
} }

@ -20,7 +20,9 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -33,7 +35,8 @@ import java.util.Set;
*/ */
final class SqlHelper extends SQLiteOpenHelper { final class SqlHelper extends SQLiteOpenHelper {
private static final String TAG = "SqlHelper"; private static final String TAG = "SqlHelper";
static volatile SqlHelper INSTANCE = null; private static volatile SqlHelper INSTANCE = null;
private Context mContext;
private DelegateCommon mDelegate; private DelegateCommon mDelegate;
@ -42,34 +45,51 @@ final class SqlHelper extends SQLiteOpenHelper {
synchronized (SqlHelper.class) { synchronized (SqlHelper.class) {
DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class); DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class);
INSTANCE = new SqlHelper(context.getApplicationContext(), delegate); INSTANCE = new SqlHelper(context.getApplicationContext(), delegate);
SQLiteDatabase db = INSTANCE.getWritableDatabase();
db = delegate.checkDb(db);
// SQLite在3.6.19版本中开始支持外键约束,
// 而在Android中 2.1以前的版本使用的SQLite版本是3.5.9, 在2.2版本中使用的是3.6.22.
// 但是为了兼容以前的程序,默认并没有启用该功能,如果要启用该功能
// 需要使用如下语句:
db.execSQL("PRAGMA foreign_keys=ON;");
Set<String> tables = DBConfig.mapping.keySet();
for (String tableName : tables) {
Class clazz = DBConfig.mapping.get(tableName);
if (!delegate.tableExists(db, clazz)) {
delegate.createTable(db, clazz);
}
} }
} }
return INSTANCE;
} }
static SqlHelper getInstance() {
return INSTANCE; return INSTANCE;
} }
private SqlHelper(Context context, DelegateCommon delegate) { private SqlHelper(Context context, DelegateCommon delegate) {
super(DBConfig.SAVE_IN_SDCARD ? new DatabaseContext(context) : context, DBConfig.DB_NAME, null, super(DBConfig.SAVE_IN_SDCARD ? new DatabaseContext(context) : context, DBConfig.DB_NAME, null,
DBConfig.VERSION); DBConfig.VERSION);
mContext = context;
mDelegate = delegate; mDelegate = delegate;
} }
@Override public void onCreate(SQLiteDatabase db) { @Override public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
}
@Override public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
db.setForeignKeyConstraintsEnabled(true);
} else {
// SQLite在3.6.19版本中开始支持外键约束,
// 而在Android中 2.1以前的版本使用的SQLite版本是3.5.9, 在2.2版本中使用的是3.6.22.
// 但是为了兼容以前的程序,默认并没有启用该功能,如果要启用该功能
// 需要使用如下语句:
db.execSQL("PRAGMA foreign_keys=ON;");
}
if (DBConfig.DEBUG) {
db.enableWriteAheadLogging();
}
}
@Override public void onCreate(SQLiteDatabase db) {
DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class);
Set<String> tables = DBConfig.mapping.keySet();
for (String tableName : tables) {
Class clazz = DBConfig.mapping.get(tableName);
if (!delegate.tableExists(db, clazz)) {
delegate.createTable(db, clazz);
}
}
} }
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
@ -90,6 +110,34 @@ final class SqlHelper extends SQLiteOpenHelper {
} }
} }
/**
* 获取数据库连接
*/
SQLiteDatabase getDb() {
SQLiteDatabase db;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder().setOpenFlags(
SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.OPEN_READWRITE |
SQLiteDatabase.CREATE_IF_NECESSARY).build();
setOpenParams(params);
db = getWritableDatabase();
} else {
//SQLiteDatabase.openOrCreateDatabase()
File dbFile = mContext.getDatabasePath(DBConfig.DB_NAME);
if (!dbFile.exists()) {
db = getWritableDatabase();
} else {
// 触发一次SQLiteOpenHelper的流程,再使用NO_LOCALIZED_COLLATORS标志打开数据库
db = getReadableDatabase();
db.close();
db = SQLiteDatabase.openDatabase(dbFile.getPath(), null,
SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.OPEN_READWRITE |
SQLiteDatabase.CREATE_IF_NECESSARY);
}
}
return db;
}
/** /**
* 处理数据库升级该段代码无法修改表字段 * 处理数据库升级该段代码无法修改表字段
* *
@ -113,7 +161,6 @@ final class SqlHelper extends SQLiteOpenHelper {
for (String tableName : tables) { for (String tableName : tables) {
Class clazz = DBConfig.mapping.get(tableName); Class clazz = DBConfig.mapping.get(tableName);
if (mDelegate.tableExists(db, clazz)) { if (mDelegate.tableExists(db, clazz)) {
db = mDelegate.checkDb(db);
//修改表名为中介表名 //修改表名为中介表名
String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName); String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName);
db.execSQL(alertSql); db.execSQL(alertSql);
@ -181,8 +228,6 @@ final class SqlHelper extends SQLiteOpenHelper {
} finally { } finally {
db.endTransaction(); db.endTransaction();
} }
mDelegate.close(db);
} }
/** /**

@ -127,12 +127,27 @@ public class CheckUtil {
} }
} }
/**
* 检测url是否合法如果url不合法将抛出{@link IllegalArgumentException}异常
*/
public static void checkUrl(String url) {
if (TextUtils.isEmpty(url)) {
throw new NullPointerException("url为空");
} else if (!url.startsWith("http") && !url.startsWith("ftp")) {
throw new IllegalArgumentException(String.format("url【%s】错误", url));
}
int index = url.indexOf("://");
if (index == -1) {
throw new IllegalArgumentException(String.format("url【%s】不合法", url));
}
}
/** /**
* 检测url是否合法 * 检测url是否合法
* *
* @return {@code true} 合法{@code false} 非法 * @return {@code true} 合法{@code false} 非法
*/ */
public static boolean checkUrl(String url) { public static boolean checkUrlNotThrow(String url) {
if (TextUtils.isEmpty(url)) { if (TextUtils.isEmpty(url)) {
ALog.e(TAG, "url不能为null"); ALog.e(TAG, "url不能为null");
return false; return false;

@ -45,13 +45,27 @@ public class DbDataHelper {
return records.get(0).taskRecord; return records.get(0).taskRecord;
} }
/**
* 获取组合任务实体ftpDir任务实体
*
* @param groupHash 组合任务Hash
* @return 实体不存在返回null
*/
public static DownloadGroupEntity getDGEntity(String groupHash) {
List<DGEntityWrapper> wrapper =
DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?",
groupHash);
return wrapper == null ? null : wrapper.get(0).groupEntity;
}
/** /**
* 获取组合任务实体 如果数据库不存在该实体则新创建一个新的任务组实体 * 获取组合任务实体 如果数据库不存在该实体则新创建一个新的任务组实体
* *
* @param groupHash 组合任务Hash * @param groupHash 组合任务Hash
* @param urls 子任务url列表 * @param urls 子任务url列表
*/ */
public static DownloadGroupEntity getHttpDGEntity(String groupHash, List<String> urls) { public static DownloadGroupEntity getOrCreateHttpDGEntity(String groupHash, List<String> urls) {
List<DGEntityWrapper> wrapper = List<DGEntityWrapper> wrapper =
DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?", DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?",
groupHash); groupHash);
@ -96,7 +110,7 @@ public class DbDataHelper {
* *
* @param ftpUrl ftp下载地址 * @param ftpUrl ftp下载地址
*/ */
public static DownloadGroupEntity getFtpDGEntity(String ftpUrl) { public static DownloadGroupEntity getOrCreateFtpDGEntity(String ftpUrl) {
List<DGEntityWrapper> wrapper = List<DGEntityWrapper> wrapper =
DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?", DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?",
ftpUrl); ftpUrl);

@ -3,6 +3,13 @@
- 优化任务接收器的代码结构 - 优化任务接收器的代码结构
- 修复`DbEntity.saveAll()`失败的问题 - 修复`DbEntity.saveAll()`失败的问题
- 修复分块任务重命名失败的问题 - 修复分块任务重命名失败的问题
- fix bug https://github.com/AriaLyy/Aria/issues/379
- 移除`getDownloadTask(String url)`、`getGroupTask(List<String> urls)`、`getFtpDirTask(String path)`
等获取任务的api,如果你希望获取对应状态,请使用实体的状态判断,如:`getDownloadEntity()`、`getDownloadGroupEntity()`
`getFtpDirEntity()`
- fix bug https://github.com/AriaLyy/Aria/issues/388
- 修复使用`Content-Disposition`的文件名时,第一次下载无法重命名文件的问题
- 修复使用`Content-Disposition`的文件名时,多次重命名文件的问题
+ v_3.6.3 (2019/4/2) + v_3.6.3 (2019/4/2)
- fix bug https://github.com/AriaLyy/Aria/issues/377 - fix bug https://github.com/AriaLyy/Aria/issues/377
+ v_3.6.2 (2019/4/1) + v_3.6.2 (2019/4/1)
@ -71,7 +78,7 @@
.start(); .start();
``` ```
- 增加强制设置文件路径的api, https://github.com/AriaLyy/Aria/issues/311 - 增加强制设置文件路径的api, https://github.com/AriaLyy/Aria/issues/311
``` ```java
Aria.download(SingleTaskActivity.this) Aria.download(SingleTaskActivity.this)
.load(DOWNLOAD_URL) .load(DOWNLOAD_URL)
.setFilePath(path, true) // true表示忽略路径是否被占用 .setFilePath(path, true) // true表示忽略路径是否被占用

@ -97,7 +97,7 @@ protected void onCreate(Bundle savedInstanceState) {
```java ```java
//在这里处理任务执行中的状态,如进度进度条的刷新 //在这里处理任务执行中的状态,如进度进度条的刷新
@Download.onTaskRunning protected void running(DownloadTask task) { @Download.onTaskRunning protected void running(DownloadTask task) {
if(task.getUrl().eques(url)){ if(task.getKey().eques(url)){
.... ....
可以通过url判断是否是指定任务的回调 可以通过url判断是否是指定任务的回调
} }

@ -41,7 +41,7 @@
3、从3.4.1开始,如果线程数为1,文件初始化时将不再预占用对应长度的空间,下载多少byte,则占多大的空间; 3、从3.4.1开始,如果线程数为1,文件初始化时将不再预占用对应长度的空间,下载多少byte,则占多大的空间;
对于3.4.1之前版本的未完成的老任务,依然采用原来的文件空间占用方式; 对于3.4.1之前版本的未完成的老任务,依然采用原来的文件空间占用方式;
--> -->
<threadNum value="1"/> <threadNum value="3"/>
<!--设置下载队列最大任务数, 默认为2--> <!--设置下载队列最大任务数, 默认为2-->
<maxTaskNum value="1"/> <maxTaskNum value="1"/>

@ -31,6 +31,7 @@ import android.widget.Toast;
import com.arialyy.annotations.Download; import com.arialyy.annotations.Download;
import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadTarget; import com.arialyy.aria.core.download.DownloadTarget;
import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.download.DownloadTask;
@ -57,14 +58,11 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
//"http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apks"; //"http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apks";
//"http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip"; //"http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip";
//"https://yizi-kejian.oss-cn-beijing.aliyuncs.com/qimeng/package1/qmtable11.zip"; //"https://yizi-kejian.oss-cn-beijing.aliyuncs.com/qimeng/package1/qmtable11.zip";
"http://rs.0.gaoshouyou.com/d/04/1e/400423a7551e1f3f0eb1812afa1f9b44.apk"; //"http://rs.0.gaoshouyou.com/d/04/1e/400423a7551e1f3f0eb1812afa1f9b44.apk";
//"http://chargepile2.techsum.net/car-manage/file/download?path=2019-04-26/c0242efd18be4ecbb23911b1c509dcad--掌通各系统汇总.xls"; // 无长度的chunked
//"http://58.210.9.131/tpk/sipgt//TDLYZTGH.tpk"; //chunked 下载 //"http://58.210.9.131/tpk/sipgt//TDLYZTGH.tpk"; //chunked 下载
//"https://static.donguo.me/video/ip/course/pfys_1.mp4"; "http://apk500.bce.baidu-mgame.com/game/67000/67734/20170622040827_oem_5502845.apk?r=1";
//"https://www.baidu.com/link?url=_LFCuTPtnzFxVJByJ504QymRywIA1Z_T5xUxe9ZLuxcGM0C_RcdpWyB1eGjbJC-e5wv5wAKM4WmLMAS5KeF6EZJHB8Va3YqZUiaErqK_pxm&wd=&eqid=e8583fe70002d126000000065a99f864";
//"https://d.pcs.baidu.com/file/a02c89a2d479d4fd2756f3313d42491d?fid=4232431903-250528-1114369760340736&dstime=1525491372&rt=sh&sign=FDtAERVY-DCb740ccc5511e5e8fedcff06b081203-3C13vkOkuk4TqXvVYW05zj1K0ao%3D&expires=8h&chkv=1&chkbd=0&chkpc=et&dp-logid=8651730921842106225&dp-callid=0&r=165533013";
//"http://apk500.bce.baidu-mgame.com/game/67000/67734/20170622040827_oem_5502845.apk?r=1";
//"https://dl.genymotion.com/releases/genymotion-2.12.1/genymotion-2.12.1-vbox.exe"; //"https://dl.genymotion.com/releases/genymotion-2.12.1/genymotion-2.12.1-vbox.exe";
//"http://9.9.9.50:5000/download1";
//"http://9.9.9.50:5000/download/CentOS-7-x86_64-Minimal-1804.iso"; //"http://9.9.9.50:5000/download/CentOS-7-x86_64-Minimal-1804.iso";
//"http://v2.qingdian1.com/m_20180730_991/2/2B9FB34A4BCD8CE61481D1C8418EFE36_1080P.m3u8"; //"http://v2.qingdian1.com/m_20180730_991/2/2B9FB34A4BCD8CE61481D1C8418EFE36_1080P.m3u8";
//"https://firmwareapi.azurewebsites.net/firmware-overview?name=A19_Filament_W_IMG0038_00102411-encrypted.ota"; //"https://firmwareapi.azurewebsites.net/firmware-overview?name=A19_Filament_W_IMG0038_00102411-encrypted.ota";
@ -236,18 +234,17 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
} }
/** /**
* @param task *
* @param e
*/ */
@Download.onTaskFail @Download.onTaskFail
void taskFail(DownloadTask task, Exception e) { void taskFail(DownloadTask task, Exception e) {
if (task.getKey().equals(DOWNLOAD_URL)) { if (task.getKey().equals(DOWNLOAD_URL)) {
Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show(); Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show();
setBtState(true); setBtState(true);
Aria.download(this) //Aria.download(this)
.load(DOWNLOAD_URL) // .load(DOWNLOAD_URL)
.updateUrl("http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip") // .updateUrl("http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip")
.start(); // .start();
//DOWNLOAD_URL = "http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip"; //DOWNLOAD_URL = "http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip";
//ALog.d(TAG, ALog.getExceptionString(e)); //ALog.d(TAG, ALog.getExceptionString(e));
} }
@ -262,9 +259,11 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
//mCancel.setEnabled(false); //mCancel.setEnabled(false);
setBtState(true); setBtState(true);
getBinding().setSpeed(""); getBinding().setSpeed("");
L.d(TAG, "path ==> " + task.getDownloadEntity().getDownloadPath()); L.d(TAG, "path = " + task.getDownloadEntity().getDownloadPath());
L.d(TAG, "md5Code ==> " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); L.d(TAG, "md5Code = " + CommonUtil.getFileMD5(new File(task.getDownloadPath())));
L.d(TAG, "data ==> " + Aria.download(this).getDownloadEntity(DOWNLOAD_URL)); L.d(TAG, "data = " + Aria.download(this).getDownloadEntity(DOWNLOAD_URL));
DownloadEntity temp = Aria.download(this).getDownloadEntity(DOWNLOAD_URL);
L.d(TAG, "status = " + temp.getState() + ", isComplete = " + temp.isComplete());
//Intent install = new Intent(Intent.ACTION_VIEW); //Intent install = new Intent(Intent.ACTION_VIEW);
//install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//File apkFile = new File(task.getDownloadPath()); //File apkFile = new File(task.getDownloadPath());
@ -286,8 +285,6 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
return R.layout.activity_single; return R.layout.activity_single;
} }
public void onClick(View view) { public void onClick(View view) {
switch (view.getId()) { switch (view.getId()) {
case R.id.start: case R.id.start:
@ -318,9 +315,9 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
//params.put("filename", "CentOS-7-x86_64-Minimal-1804.iso"); //params.put("filename", "CentOS-7-x86_64-Minimal-1804.iso");
Aria.download(SingleTaskActivity.this) Aria.download(SingleTaskActivity.this)
.load(DOWNLOAD_URL) .load(DOWNLOAD_URL)
//.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8") //.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3")
//.addHeader("Accept-Encoding", "gzip, deflate") .addHeader("Accept-Encoding", "gzip, deflate")
//.addHeader("DNT", "1") .addHeader("DNT", "1")
//.addHeader("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0") //.addHeader("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0")
.useServerFileName(true) .useServerFileName(true)
.setFilePath(path, true) .setFilePath(path, true)

@ -48,16 +48,15 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
setTitle("任务组"); setTitle("任务组");
mChildList = getBinding().childList; mChildList = getBinding().childList;
mUrls = getModule(GroupModule.class).getUrls(); mUrls = getModule(GroupModule.class).getUrls();
DGTaskWrapper entity = Aria.download(this).getGroupTask(mUrls); DownloadGroupEntity entity = Aria.download(this).getDownloadGroupEntity(mUrls);
if (entity != null && entity.getEntity() != null) { if (entity != null) {
DownloadGroupEntity groupEntity = entity.getEntity(); mChildList.addData(entity.getSubEntities());
mChildList.addData(groupEntity.getSubEntities()); getBinding().setFileSize(entity.getConvertFileSize());
getBinding().setFileSize(groupEntity.getConvertFileSize()); if (entity.getFileSize() == 0) {
if (groupEntity.getFileSize() == 0) {
getBinding().setProgress(0); getBinding().setProgress(0);
} else { } else {
getBinding().setProgress(groupEntity.isComplete() ? 100 getBinding().setProgress(entity.isComplete() ? 100
: (int) (groupEntity.getCurrentProgress() * 100 / groupEntity.getFileSize())); : (int) (entity.getCurrentProgress() * 100 / entity.getFileSize()));
} }
} }
@ -82,7 +81,8 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
switch (view.getId()) { switch (view.getId()) {
case R.id.start: case R.id.start:
List<String> temp = new ArrayList<>(); List<String> temp = new ArrayList<>();
temp.add("https://d.pcs.baidu.com/file/130335545f3f4d9cc38afe709c19af5a?fid=1411168371-250528-1010657263806840&dstime=1531134714&rt=sh&sign=FDtAERVY-DCb740ccc5511e5e8fedcff06b081203-h8KgJ6gl4oY9UR6NqvwJsT4nVSM%3D&expires=8h&chkv=1&chkbd=0&chkpc=et&dp-logid=4401996296756616039&dp-callid=0&r=279987343"); temp.add(
"https://d.pcs.baidu.com/file/130335545f3f4d9cc38afe709c19af5a?fid=1411168371-250528-1010657263806840&dstime=1531134714&rt=sh&sign=FDtAERVY-DCb740ccc5511e5e8fedcff06b081203-h8KgJ6gl4oY9UR6NqvwJsT4nVSM%3D&expires=8h&chkv=1&chkbd=0&chkpc=et&dp-logid=4401996296756616039&dp-callid=0&r=279987343");
Aria.download(this) Aria.download(this)
.loadGroup(mUrls) .loadGroup(mUrls)
.setDirPath( .setDirPath(

@ -43,16 +43,15 @@ public class FTPDirDownloadActivity extends BaseActivity<ActivityDownloadGroupBi
Aria.download(this).register(); Aria.download(this).register();
setTitle("FTP文件夹下载"); setTitle("FTP文件夹下载");
mChildList = findViewById(R.id.child_list); mChildList = findViewById(R.id.child_list);
DGTaskWrapper entity = Aria.download(this).getFtpDirTask(dir); DownloadGroupEntity entity = Aria.download(this).getFtpDirEntity(dir);
if (entity != null && entity.getEntity() != null) { if (entity != null) {
DownloadGroupEntity groupEntity = entity.getEntity(); mChildList.addData(entity.getSubEntities());
mChildList.addData(groupEntity.getSubEntities()); getBinding().setFileSize(entity.getConvertFileSize());
getBinding().setFileSize(groupEntity.getConvertFileSize()); if (entity.getFileSize() == 0) {
if (groupEntity.getFileSize() == 0) {
getBinding().setProgress(0); getBinding().setProgress(0);
} else { } else {
getBinding().setProgress(groupEntity.isComplete() ? 100 getBinding().setProgress(entity.isComplete() ? 100
: (int) (groupEntity.getCurrentProgress() * 100 / groupEntity.getFileSize())); : (int) (entity.getCurrentProgress() * 100 / entity.getFileSize()));
} }
} }
} }

@ -43,8 +43,6 @@ public class AnyRunActivity extends BaseActivity<ActivityTestBinding> {
mBar.setVisibility(View.GONE); mBar.setVisibility(View.GONE);
module = new AnyRunnModule(this); module = new AnyRunnModule(this);
urls = getResources().getStringArray(R.array.group_urls); urls = getResources().getStringArray(R.array.group_urls);
DTaskWrapper entity = Aria.download(this).getDownloadTask(URL);
Log.d(TAG, entity + "");
} }
public void onClick(View view) { public void onClick(View view) {

@ -43,7 +43,7 @@ task clean(type: Delete) {
ext { ext {
userOrg = 'arialyy' userOrg = 'arialyy'
groupId = 'com.arialyy.aria' groupId = 'com.arialyy.aria'
publishVersion = '3.6.3' publishVersion = '3.6.4_dev_1'
// publishVersion = '1.0.4' //FTP插件 // publishVersion = '1.0.4' //FTP插件
repoName='maven' repoName='maven'
desc = 'android 下载框架' desc = 'android 下载框架'

Loading…
Cancel
Save