修复一个表创建失败的问题 https://github.com/AriaLyy/Aria/issues/570
修复一个非分块模式下导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/571 修复一个服务器端无法创建socket连接,却没有返回码导致客户端卡住的问题 https://github.com/AriaLyy/Aria/issues/569 修复文件删除后,组合任务没有重新下载的问题 https://github.com/AriaLyy/Aria/issues/574 优化缓存队列和执行队列
This commit is contained in:
@@ -35,7 +35,7 @@ dependencies {
|
||||
api 'com.google.code.gson:gson:2.8.2'
|
||||
api 'io.reactivex:rxandroid:1.2.0'
|
||||
api 'io.reactivex:rxjava:1.1.5'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.2.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
|
||||
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
|
||||
|
||||
@@ -95,8 +95,7 @@ public abstract class AbsTaskQueue<TASK extends AbsTask, TASK_WRAPPER extends Ab
|
||||
* 停止所有任务
|
||||
*/
|
||||
@Override public void stopAllTask() {
|
||||
for (String key : mExecutePool.getAllTask().keySet()) {
|
||||
TASK task = mExecutePool.getAllTask().get(key);
|
||||
for (TASK task : mExecutePool.getAllTask()) {
|
||||
if (task != null) {
|
||||
int state = task.getState();
|
||||
if (task.isRunning() || (state != IEntity.STATE_COMPLETE
|
||||
@@ -106,8 +105,13 @@ public abstract class AbsTaskQueue<TASK extends AbsTask, TASK_WRAPPER extends Ab
|
||||
}
|
||||
}
|
||||
|
||||
for (String key : mCachePool.getAllTask().keySet()) {
|
||||
TASK task = mCachePool.getAllTask().get(key);
|
||||
//for (String key : mCachePool.getAllTask().keySet()) {
|
||||
// TASK task = mCachePool.getAllTask().get(key);
|
||||
// if (task != null) {
|
||||
// task.stop(TaskSchedulerType.TYPE_STOP_NOT_NEXT);
|
||||
// }
|
||||
//}
|
||||
for (TASK task : mCachePool.getAllTask()) {
|
||||
if (task != null) {
|
||||
task.stop(TaskSchedulerType.TYPE_STOP_NOT_NEXT);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.arialyy.aria.core.scheduler.TaskSchedulers;
|
||||
import com.arialyy.aria.core.task.DownloadTask;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -68,11 +68,10 @@ public class DTaskQueue extends AbsTaskQueue<DownloadTask, DTaskWrapper> {
|
||||
*/
|
||||
public void setTaskHighestPriority(DownloadTask task) {
|
||||
task.setHighestPriority(true);
|
||||
Map<String, DownloadTask> exeTasks = mExecutePool.getAllTask();
|
||||
//Map<String, DownloadTask> exeTasks = mExecutePool.getAllTask();
|
||||
List<DownloadTask> exeTasks = mExecutePool.getAllTask();
|
||||
if (exeTasks != null && !exeTasks.isEmpty()) {
|
||||
Set<String> keys = exeTasks.keySet();
|
||||
for (String key : keys) {
|
||||
DownloadTask temp = exeTasks.get(key);
|
||||
for (DownloadTask temp : exeTasks) {
|
||||
if (temp != null && temp.isRunning() && temp.isHighestPriorityTask() && !temp.getKey()
|
||||
.equals(task.getKey())) {
|
||||
ALog.e(TAG, "设置最高优先级任务失败,失败原因【任务中已经有最高优先级任务,请等待上一个最高优先级任务完成,或手动暂停该任务】");
|
||||
@@ -80,28 +79,28 @@ public class DTaskQueue extends AbsTaskQueue<DownloadTask, DTaskWrapper> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
int maxSize = AriaConfig.getInstance().getDConfig().getMaxTaskNum();
|
||||
int currentSize = mExecutePool.size();
|
||||
if (currentSize == 0 || currentSize < maxSize) {
|
||||
startTask(task);
|
||||
} else {
|
||||
Set<DownloadTask> tempTasks = new LinkedHashSet<>();
|
||||
for (int i = 0; i < maxSize; i++) {
|
||||
DownloadTask oldTsk = mExecutePool.pollTask();
|
||||
if (oldTsk != null && oldTsk.isRunning()) {
|
||||
if (i == maxSize - 1) {
|
||||
oldTsk.stop(TaskSchedulerType.TYPE_STOP_AND_WAIT);
|
||||
mCachePool.putTaskToFirst(oldTsk);
|
||||
break;
|
||||
int maxSize = AriaConfig.getInstance().getDConfig().getMaxTaskNum();
|
||||
int currentSize = mExecutePool.size();
|
||||
if (currentSize == 0 || currentSize < maxSize) {
|
||||
startTask(task);
|
||||
} else {
|
||||
Set<DownloadTask> tempTasks = new LinkedHashSet<>();
|
||||
for (int i = 0; i < maxSize; i++) {
|
||||
DownloadTask oldTsk = mExecutePool.pollTask();
|
||||
if (oldTsk != null && oldTsk.isRunning()) {
|
||||
if (i == maxSize - 1) {
|
||||
oldTsk.stop(TaskSchedulerType.TYPE_STOP_AND_WAIT);
|
||||
mCachePool.putTaskToFirst(oldTsk);
|
||||
break;
|
||||
}
|
||||
tempTasks.add(oldTsk);
|
||||
}
|
||||
tempTasks.add(oldTsk);
|
||||
}
|
||||
}
|
||||
startTask(task);
|
||||
startTask(task);
|
||||
|
||||
for (DownloadTask temp : tempTasks) {
|
||||
mExecutePool.putTask(temp);
|
||||
for (DownloadTask temp : tempTasks) {
|
||||
mExecutePool.putTask(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,65 +20,43 @@ import android.text.TextUtils;
|
||||
import com.arialyy.aria.core.task.AbsTask;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/8/14. 任务缓存池,所有下载任务最先缓存在这个池中
|
||||
*/
|
||||
public class BaseCachePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
private static final String TAG = "BaseCachePool";
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
private static final int MAX_NUM = Integer.MAX_VALUE; //最大下载任务数
|
||||
private static final long TIME_OUT = 1000;
|
||||
private static final Object LOCK = new Object();
|
||||
private Map<String, TASK> mCacheMap;
|
||||
private LinkedBlockingQueue<TASK> mCacheQueue;
|
||||
private Deque<TASK> mCacheQueue;
|
||||
|
||||
BaseCachePool() {
|
||||
mCacheQueue = new LinkedBlockingQueue<>(MAX_NUM);
|
||||
mCacheMap = new ConcurrentHashMap<>();
|
||||
mCacheQueue = new LinkedBlockingDeque<>(MAX_NUM);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取被缓存的任务
|
||||
*/
|
||||
public Map<String, TASK> getAllTask() {
|
||||
return mCacheMap;
|
||||
public List<TASK> getAllTask() {
|
||||
return new ArrayList<>(mCacheQueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有缓存的任务
|
||||
*/
|
||||
public void clear() {
|
||||
for (String key : mCacheMap.keySet()) {
|
||||
TASK task = mCacheMap.get(key);
|
||||
mCacheQueue.remove(task);
|
||||
mCacheMap.remove(key);
|
||||
}
|
||||
mCacheQueue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将任务放在队首
|
||||
*/
|
||||
public boolean putTaskToFirst(TASK task) {
|
||||
if (mCacheQueue.isEmpty()) {
|
||||
return putTask(task);
|
||||
} else {
|
||||
Set<TASK> temps = new LinkedHashSet<>();
|
||||
temps.add(task);
|
||||
for (int i = 0, len = size(); i < len; i++) {
|
||||
TASK temp = pollTask();
|
||||
temps.add(temp);
|
||||
}
|
||||
for (TASK t : temps) {
|
||||
putTask(t);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return mCacheQueue.offerFirst(task);
|
||||
}
|
||||
|
||||
@Override public boolean putTask(TASK task) {
|
||||
@@ -87,16 +65,12 @@ public class BaseCachePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
ALog.e(TAG, "任务不能为空!!");
|
||||
return false;
|
||||
}
|
||||
String key = task.getKey();
|
||||
if (mCacheQueue.contains(task)) {
|
||||
ALog.w(TAG, "任务【" + task.getTaskName() + "】进入缓存队列失败,原因:已经在缓存队列中");
|
||||
return false;
|
||||
} else {
|
||||
boolean s = mCacheQueue.offer(task);
|
||||
ALog.d(TAG, "任务【" + task.getTaskName() + "】进入缓存队列" + (s ? "成功" : "失败"));
|
||||
if (s) {
|
||||
mCacheMap.put(CommonUtil.keyToHashKey(key), task);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -104,19 +78,8 @@ public class BaseCachePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
|
||||
@Override public TASK pollTask() {
|
||||
synchronized (LOCK) {
|
||||
try {
|
||||
TASK task;
|
||||
task = mCacheQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS);
|
||||
if (task != null) {
|
||||
String url = task.getKey();
|
||||
mCacheMap.remove(CommonUtil.keyToHashKey(url));
|
||||
}
|
||||
return task;
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return mCacheQueue.pollFirst();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public TASK getTask(String key) {
|
||||
@@ -125,12 +88,17 @@ public class BaseCachePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
ALog.e(TAG, "key 为null");
|
||||
return null;
|
||||
}
|
||||
return mCacheMap.get(CommonUtil.keyToHashKey(key));
|
||||
for (TASK task : mCacheQueue) {
|
||||
if (task.getKey().equals(key)) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public boolean taskExits(String key) {
|
||||
return mCacheMap.containsKey(CommonUtil.keyToHashKey(key));
|
||||
return getTask(key) != null;
|
||||
}
|
||||
|
||||
@Override public boolean removeTask(TASK task) {
|
||||
@@ -139,8 +107,6 @@ public class BaseCachePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
ALog.e(TAG, "任务不能为空");
|
||||
return false;
|
||||
} else {
|
||||
String key = CommonUtil.keyToHashKey(task.getKey());
|
||||
mCacheMap.remove(key);
|
||||
return mCacheQueue.remove(task);
|
||||
}
|
||||
}
|
||||
@@ -152,10 +118,7 @@ public class BaseCachePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
ALog.e(TAG, "请传入有效的下载链接");
|
||||
return false;
|
||||
}
|
||||
String temp = CommonUtil.keyToHashKey(key);
|
||||
TASK task = mCacheMap.get(temp);
|
||||
mCacheMap.remove(temp);
|
||||
return mCacheQueue.remove(task);
|
||||
return mCacheQueue.remove(getTask(key));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,26 +21,23 @@ import com.arialyy.aria.core.AriaManager;
|
||||
import com.arialyy.aria.core.task.AbsTask;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2016/8/15. 任务执行池,所有当前下载任务都该任务池中,默认下载大小为2
|
||||
*/
|
||||
public class BaseExecutePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
private final String TAG = "BaseExecutePool";
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
private static final Object LOCK = new Object();
|
||||
final long TIME_OUT = 1000;
|
||||
ArrayBlockingQueue<TASK> mExecuteQueue;
|
||||
Map<String, TASK> mExecuteMap;
|
||||
Deque<TASK> mExecuteQueue;
|
||||
int mSize;
|
||||
|
||||
BaseExecutePool() {
|
||||
mSize = getMaxSize();
|
||||
mExecuteQueue = new ArrayBlockingQueue<>(mSize);
|
||||
mExecuteMap = new ConcurrentHashMap<>();
|
||||
mExecuteQueue = new LinkedBlockingDeque<>(mSize);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,8 +52,8 @@ public class BaseExecutePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
/**
|
||||
* 获取所有正在执行的任务
|
||||
*/
|
||||
public Map<String, TASK> getAllTask() {
|
||||
return mExecuteMap;
|
||||
public List<TASK> getAllTask() {
|
||||
return new ArrayList<>(mExecuteQueue);
|
||||
}
|
||||
|
||||
@Override public boolean putTask(TASK task) {
|
||||
@@ -88,17 +85,13 @@ public class BaseExecutePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
*/
|
||||
public void setMaxNum(int maxNum) {
|
||||
synchronized (LOCK) {
|
||||
try {
|
||||
ArrayBlockingQueue<TASK> temp = new ArrayBlockingQueue<>(maxNum);
|
||||
TASK task;
|
||||
while ((task = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS)) != null) {
|
||||
temp.offer(task);
|
||||
}
|
||||
mExecuteQueue = temp;
|
||||
mSize = maxNum;
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Deque<TASK> temp = new LinkedBlockingDeque<>(maxNum);
|
||||
TASK task;
|
||||
while ((task = mExecuteQueue.poll()) != null) {
|
||||
temp.offer(task);
|
||||
}
|
||||
mExecuteQueue = temp;
|
||||
mSize = maxNum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,12 +102,8 @@ public class BaseExecutePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
*/
|
||||
boolean putNewTask(TASK newTask) {
|
||||
synchronized (LOCK) {
|
||||
String url = newTask.getKey();
|
||||
boolean s = mExecuteQueue.offer(newTask);
|
||||
ALog.d(TAG, "任务【" + newTask.getTaskName() + "】进入执行队列" + (s ? "成功" : "失败"));
|
||||
if (s) {
|
||||
mExecuteMap.put(CommonUtil.keyToHashKey(url), newTask);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -124,37 +113,19 @@ public class BaseExecutePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
*/
|
||||
boolean pollFirstTask() {
|
||||
synchronized (LOCK) {
|
||||
try {
|
||||
TASK oldTask = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS);
|
||||
if (oldTask == null) {
|
||||
ALog.w(TAG, "移除任务失败,原因:任务为null");
|
||||
return false;
|
||||
}
|
||||
oldTask.stop();
|
||||
String key = CommonUtil.keyToHashKey(oldTask.getKey());
|
||||
mExecuteMap.remove(key);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
TASK oldTask = mExecuteQueue.pollFirst();
|
||||
if (oldTask == null) {
|
||||
ALog.w(TAG, "移除任务失败,原因:任务为null");
|
||||
return false;
|
||||
}
|
||||
oldTask.stop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public TASK pollTask() {
|
||||
synchronized (LOCK) {
|
||||
try {
|
||||
TASK task;
|
||||
task = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS);
|
||||
if (task != null) {
|
||||
String url = task.getKey();
|
||||
mExecuteMap.remove(CommonUtil.keyToHashKey(url));
|
||||
}
|
||||
return task;
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
return mExecuteQueue.poll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,12 +135,18 @@ public class BaseExecutePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
ALog.e(TAG, "key为null");
|
||||
return null;
|
||||
}
|
||||
return mExecuteMap.get(CommonUtil.keyToHashKey(key));
|
||||
for (TASK task : mExecuteQueue) {
|
||||
if (task.getKey().equals(key)) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public boolean taskExits(String key) {
|
||||
return mExecuteMap.containsKey(CommonUtil.keyToHashKey(key));
|
||||
return getTask(key) != null;
|
||||
}
|
||||
|
||||
@Override public boolean removeTask(TASK task) {
|
||||
@@ -189,16 +166,8 @@ public class BaseExecutePool<TASK extends AbsTask> implements IPool<TASK> {
|
||||
ALog.e(TAG, "key 为null");
|
||||
return false;
|
||||
}
|
||||
String convertKey = CommonUtil.keyToHashKey(key);
|
||||
TASK task = mExecuteMap.get(convertKey);
|
||||
final int oldQueueSize = mExecuteQueue.size();
|
||||
boolean isSuccess = mExecuteQueue.remove(task);
|
||||
final int newQueueSize = mExecuteQueue.size();
|
||||
if (isSuccess && newQueueSize != oldQueueSize) {
|
||||
mExecuteMap.remove(convertKey);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return mExecuteQueue.remove(getTask(key));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,14 @@ package com.arialyy.aria.core.queue.pool;
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.core.AriaManager;
|
||||
import com.arialyy.aria.core.task.AbsTask;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/6/29.
|
||||
* 单个下载任务的执行池
|
||||
*/
|
||||
class DGLoadExecutePool<TASK extends AbsTask> extends DLoadExecutePool<TASK> {
|
||||
private final String TAG = "DGLoadExecutePool";
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
|
||||
@Override protected int getMaxSize() {
|
||||
return AriaConfig.getInstance().getDGConfig().getMaxTaskNum();
|
||||
|
||||
@@ -18,9 +18,6 @@ package com.arialyy.aria.core.queue.pool;
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.core.task.AbsTask;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by AriaL on 2017/6/29.
|
||||
@@ -45,9 +42,10 @@ class DLoadExecutePool<TASK extends AbsTask> extends BaseExecutePool<TASK> {
|
||||
return false;
|
||||
} else {
|
||||
if (mExecuteQueue.size() >= mSize) {
|
||||
Set<String> keys = mExecuteMap.keySet();
|
||||
for (String key : keys) {
|
||||
if (mExecuteMap.get(key).isHighestPriorityTask()) return false;
|
||||
for (TASK temp : mExecuteQueue) {
|
||||
if (temp.isHighestPriorityTask()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (pollFirstTask()) {
|
||||
return putNewTask(task);
|
||||
@@ -61,22 +59,15 @@ class DLoadExecutePool<TASK extends AbsTask> extends BaseExecutePool<TASK> {
|
||||
}
|
||||
|
||||
@Override boolean pollFirstTask() {
|
||||
try {
|
||||
TASK oldTask = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS);
|
||||
if (oldTask == null) {
|
||||
ALog.w(TAG, "移除任务失败,错误原因:任务为null");
|
||||
return false;
|
||||
}
|
||||
if (oldTask.isHighestPriorityTask()) {
|
||||
return false;
|
||||
}
|
||||
oldTask.stop();
|
||||
String key = CommonUtil.keyToHashKey(oldTask.getKey());
|
||||
mExecuteMap.remove(key);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
TASK oldTask = mExecuteQueue.pollFirst();
|
||||
if (oldTask == null) {
|
||||
ALog.w(TAG, "移除任务失败,错误原因:任务为null");
|
||||
return false;
|
||||
}
|
||||
if (oldTask.isHighestPriorityTask()) {
|
||||
return false;
|
||||
}
|
||||
oldTask.stop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
## 开发日志
|
||||
+ v_3.8.1 (2019/12/22)
|
||||
- 修复一个表创建失败的问题 https://github.com/AriaLyy/Aria/issues/570
|
||||
- 修复一个非分块模式下导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/571
|
||||
- 修复一个服务器端无法创建socket连接,却没有返回码导致客户端卡住的问题 https://github.com/AriaLyy/Aria/issues/569
|
||||
- 修复文件删除后,组合任务没有重新下载的问题 https://github.com/AriaLyy/Aria/issues/574
|
||||
- 优化缓存队列和执行队列
|
||||
+ v_3.8 (2019/12/17)
|
||||
- 移除androidx和support的依赖,现在无论是哪个版本的appcompat包都可以使用本框架
|
||||
- 修复一个在xml中使用fragment导致的内存泄漏问题
|
||||
@@ -15,7 +21,7 @@
|
||||
+ v_3.7.10 (2019/12/3)
|
||||
- fix bug https://github.com/AriaLyy/Aria/issues/543#issuecomment-559733124
|
||||
- fix bug https://github.com/AriaLyy/Aria/issues/542
|
||||
- fix bug https://github.com/AriaLyy/Aria/issues/547
|
||||
- fix bug https://github.com/AriaLyy/Azria/issues/547
|
||||
- 修复下载失败时,中断重试无效的问题
|
||||
- 增加忽略权限检查的api,`ignoreCheckPermissions()`
|
||||
- 增加通用的的忽略文件路径被占用的api,`isIgnoreFilePathOccupy()`
|
||||
|
||||
@@ -3662,7 +3662,6 @@ public class FTPClient extends FTP implements Configurable {
|
||||
private long time = System.currentTimeMillis();
|
||||
private int notAcked;
|
||||
private OnFtpInputStreamListener listener;
|
||||
private long lTime = time;
|
||||
|
||||
CSL(FTPClient parent, long idleTime, int maxWait) throws SocketException {
|
||||
this(parent, idleTime, maxWait, null);
|
||||
@@ -3696,12 +3695,9 @@ public class FTPClient extends FTP implements Configurable {
|
||||
|
||||
time = now;
|
||||
}
|
||||
//if (now - lTime > 100) {
|
||||
if (listener != null) {
|
||||
listener.onFtpInputStream(parent, totalBytesTransferred, bytesTransferred, streamSize);
|
||||
}
|
||||
//lTime = now;
|
||||
//}
|
||||
}
|
||||
|
||||
void cleanUp() throws IOException {
|
||||
|
||||
@@ -117,7 +117,8 @@ public abstract class BaseFtpThreadTaskAdapter extends AbsThreadTaskAdapter {
|
||||
}
|
||||
}
|
||||
client.setControlEncoding(charSet);
|
||||
client.setDataTimeout(getTaskConfig().getIOTimeOut());
|
||||
//client.setDataTimeout(getTaskConfig().getIOTimeOut());
|
||||
client.setDataTimeout(1000);
|
||||
client.setConnectTimeout(getTaskConfig().getConnectTimeOut());
|
||||
if (mTaskOption.getConnMode() == FtpConnectionMode.DATA_CONNECTION_MODE_ACTIVITY) {
|
||||
client.enterLocalActiveMode();
|
||||
|
||||
@@ -42,10 +42,6 @@ class FtpUFileInfoThread extends AbsFtpInfoThread<UploadEntity, UTaskWrapper> {
|
||||
static final int CODE_COMPLETE = 0xab1;
|
||||
private boolean isComplete = false;
|
||||
private String remotePath;
|
||||
/**
|
||||
* true 使用拦截器,false 不使用拦截器
|
||||
*/
|
||||
private boolean useInterceptor = false;
|
||||
|
||||
FtpUFileInfoThread(UTaskWrapper taskEntity, OnFileInfoCallback callback) {
|
||||
super(taskEntity, callback);
|
||||
@@ -65,7 +61,9 @@ class FtpUFileInfoThread extends AbsFtpInfoThread<UploadEntity, UTaskWrapper> {
|
||||
try {
|
||||
IFtpUploadInterceptor interceptor = mTaskOption.getUploadInterceptor();
|
||||
if (interceptor != null) {
|
||||
useInterceptor = true;
|
||||
/**
|
||||
* true 使用拦截器,false 不使用拦截器
|
||||
*/
|
||||
List<String> files = new ArrayList<>();
|
||||
for (FTPFile ftpFile : ftpFiles) {
|
||||
if (ftpFile.isDirectory()) {
|
||||
@@ -118,7 +116,7 @@ class FtpUFileInfoThread extends AbsFtpInfoThread<UploadEntity, UTaskWrapper> {
|
||||
*/
|
||||
@Override protected void handleFile(String remotePath, FTPFile ftpFile) {
|
||||
super.handleFile(remotePath, ftpFile);
|
||||
if (ftpFile != null && !useInterceptor) {
|
||||
if (ftpFile != null) {
|
||||
//远程文件已完成
|
||||
if (ftpFile.getSize() == mEntity.getFileSize()) {
|
||||
isComplete = true;
|
||||
|
||||
@@ -28,6 +28,8 @@ import com.arialyy.aria.util.BufferedRandomAccessFile;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/7/28. D_FTP 单线程上传任务,需要FTP 服务器给用户打开append和write的权限
|
||||
@@ -35,13 +37,16 @@ import java.io.UnsupportedEncodingException;
|
||||
class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
private String dir, remotePath;
|
||||
private boolean storeFail = false;
|
||||
private ScheduledThreadPoolExecutor timer;
|
||||
private FTPClient client = null;
|
||||
private boolean isTimeOut = true;
|
||||
private FtpFISAdapter fa;
|
||||
|
||||
FtpUThreadTaskAdapter(SubThreadConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override protected void handlerThreadTask() {
|
||||
FTPClient client = null;
|
||||
BufferedRandomAccessFile file = null;
|
||||
try {
|
||||
ALog.d(TAG,
|
||||
@@ -71,7 +76,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
if (getThreadRecord().startLocation > 0) {
|
||||
file.seek(getThreadRecord().startLocation);
|
||||
}
|
||||
boolean complete = upload(client, file);
|
||||
boolean complete = upload(file);
|
||||
if (!complete || getThreadTask().isBreak()) {
|
||||
return;
|
||||
}
|
||||
@@ -94,6 +99,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
e.printStackTrace();
|
||||
}
|
||||
closeClient(client);
|
||||
closeTimer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,15 +118,46 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
String.format("%s/%s", mTaskOption.getUrlEntity().remotePath, fileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动监听定时器,当网络断开时,如果该任务的FTP服务器的传输线程没有断开,当客户端重新连接时,客户端将无法发送数据到服务端
|
||||
* 每隔10s检查一次。
|
||||
*/
|
||||
private void startTimer() {
|
||||
timer = new ScheduledThreadPoolExecutor(1);
|
||||
timer.scheduleWithFixedDelay(new Runnable() {
|
||||
@Override public void run() {
|
||||
try {
|
||||
if (isTimeOut) {
|
||||
fail(new AriaIOException(TAG, "socket连接失败,该问题一般出现于网络断开,客户端重新连接,"
|
||||
+ "但是服务器端无法创建socket缺没有返回错误码的情况。"), false);
|
||||
}
|
||||
if (fa != null) {
|
||||
fa.close();
|
||||
}
|
||||
isTimeOut = true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, 10, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void closeTimer() {
|
||||
if (timer != null && !timer.isShutdown()) {
|
||||
timer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传
|
||||
*
|
||||
* @return {@code true}上传成功、{@code false} 上传失败
|
||||
*/
|
||||
private boolean upload(final FTPClient client, final BufferedRandomAccessFile bis)
|
||||
private boolean upload(final BufferedRandomAccessFile bis)
|
||||
throws IOException {
|
||||
final FtpFISAdapter fa = new FtpFISAdapter(bis);
|
||||
fa = new FtpFISAdapter(bis);
|
||||
storeFail = false;
|
||||
startTimer();
|
||||
try {
|
||||
ALog.d(TAG, String.format("remotePath: %s", remotePath));
|
||||
client.storeFile(remotePath, fa, new OnFtpInputStreamListener() {
|
||||
@@ -129,6 +166,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
@Override public void onFtpInputStream(FTPClient client, long totalBytesTransferred,
|
||||
int bytesTransferred, long streamSize) {
|
||||
try {
|
||||
isTimeOut = false;
|
||||
if (getThreadTask().isBreak() && !isStoped) {
|
||||
isStoped = true;
|
||||
client.abor();
|
||||
@@ -150,9 +188,10 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
String msg = String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", client.getReplyCode(),
|
||||
String msg = String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", client.getReply(),
|
||||
client.getReplyString(), getEntity().getFilePath());
|
||||
closeClient(client);
|
||||
e.printStackTrace();
|
||||
if (e.getMessage().contains("AriaIOException caught while copying")) {
|
||||
e.printStackTrace();
|
||||
} else {
|
||||
@@ -163,7 +202,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
|
||||
if (storeFail) {
|
||||
return false;
|
||||
}
|
||||
int reply = client.getReplyCode();
|
||||
int reply = client.getReply();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
if (reply != FTPReply.TRANSFER_ABORTED) {
|
||||
fail(new AriaIOException(TAG,
|
||||
|
||||
@@ -89,6 +89,11 @@ public class HttpFileInfoThread implements Runnable {
|
||||
true);
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.getInputStream().close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ public class DGroupLoaderUtil extends AbsGroupUtil {
|
||||
getState().setSubSize(getWrapper().getSubTaskWrapper().size());
|
||||
if (getState().getCompleteNum() == getState().getSubSize()) {
|
||||
mListener.onComplete();
|
||||
return false;
|
||||
} else {
|
||||
// 处理组合任务大小未知的情况
|
||||
if (getWrapper().isUnknownSize() && getWrapper().getEntity().getFileSize() < 1) {
|
||||
|
||||
@@ -143,6 +143,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
|
||||
is.close();
|
||||
}
|
||||
if (conn != null) {
|
||||
conn.getInputStream().close();
|
||||
conn.disconnect();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -51,15 +51,12 @@ public class RecordHelper {
|
||||
File temp = new File(mTaskRecord.filePath);
|
||||
boolean fileExists = false;
|
||||
if (!temp.exists()) {
|
||||
BufferedRandomAccessFile tempFile;
|
||||
try {
|
||||
tempFile = new BufferedRandomAccessFile(temp, "rw");
|
||||
tempFile.setLength(mWrapper.getEntity().getFileSize());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
createPlaceHolderFile(temp);
|
||||
} else {
|
||||
if (temp.length() != mWrapper.getEntity().getFileSize()) {
|
||||
FileUtil.deleteFile(temp);
|
||||
createPlaceHolderFile(temp);
|
||||
}
|
||||
fileExists = true;
|
||||
}
|
||||
// 处理文件被删除的情况
|
||||
@@ -81,6 +78,19 @@ public class RecordHelper {
|
||||
mWrapper.setNewTask(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建非分块的占位文件
|
||||
*/
|
||||
private void createPlaceHolderFile(File temp) {
|
||||
BufferedRandomAccessFile tempFile;
|
||||
try {
|
||||
tempFile = new BufferedRandomAccessFile(temp, "rw");
|
||||
tempFile.setLength(mWrapper.getEntity().getFileSize());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理分块任务的记录,分块文件(blockFileLen)长度必须需要小于等于线程区间(threadRectLen)的长度
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.arialyy.aria.core.listener.IEventListener;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
@@ -83,14 +84,24 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
|
||||
private void initState() {
|
||||
mState = new GroupRunState(getWrapper().getKey(), mListener, mSubQueue);
|
||||
for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) {
|
||||
if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE) {
|
||||
File subFile = new File(wrapper.getEntity().getFilePath());
|
||||
if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE
|
||||
&& subFile.exists()
|
||||
&& subFile.length() == wrapper.getEntity().getFileSize()) {
|
||||
mState.updateCompleteNum();
|
||||
mCurrentLocation += wrapper.getEntity().getFileSize();
|
||||
} else {
|
||||
if (!subFile.exists()) {
|
||||
wrapper.getEntity().setCurrentProgress(0);
|
||||
}
|
||||
wrapper.getEntity().setState(IEntity.STATE_POST_PRE);
|
||||
mCache.put(wrapper.getKey(), wrapper);
|
||||
mCurrentLocation += wrapper.getEntity().getCurrentProgress();
|
||||
}
|
||||
}
|
||||
if (getWrapper().getSubTaskWrapper().size() != mState.getCompleteNum()) {
|
||||
getWrapper().setState(IEntity.STATE_POST_PRE);
|
||||
}
|
||||
mState.updateProgress(mCurrentLocation);
|
||||
mScheduler = new Handler(Looper.getMainLooper(), SimpleSchedulers.newInstance(mState));
|
||||
}
|
||||
|
||||
@@ -122,8 +122,9 @@ public abstract class BaseListener<ENTITY extends AbsEntity, TASK_WRAPPER extend
|
||||
mTask.putExpand(AbsTask.ERROR_INFO_KEY, e);
|
||||
sendInState2Target(ISchedulers.FAIL);
|
||||
if (e != null) {
|
||||
e.printStackTrace();
|
||||
ErrorHelp.saveError(e.getTag(), "", ALog.getExceptionString(e));
|
||||
String error = ALog.getExceptionString(e);
|
||||
ALog.w(TAG, error);
|
||||
ErrorHelp.saveError(e.getTag(), "", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package com.arialyy.aria.core.manager;
|
||||
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.core.task.IThreadTask;
|
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.util.HashSet;
|
||||
@@ -25,8 +25,9 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@@ -34,11 +35,12 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
* 线程任务管理器
|
||||
*/
|
||||
public class ThreadTaskManager {
|
||||
private final String TAG = CommonUtil.getClassName(this);
|
||||
private static volatile ThreadTaskManager INSTANCE = null;
|
||||
private final String TAG = "ThreadTaskManager";
|
||||
private ExecutorService mExePool;
|
||||
private Map<String, Set<FutureContainer>> mThreadTasks = new ConcurrentHashMap<>();
|
||||
private static final int CORE_POOL_NUM = 20;
|
||||
private static final ReentrantLock LOCK = new ReentrantLock();
|
||||
private ThreadPoolExecutor mExePool;
|
||||
private Map<String, Set<FutureContainer>> mThreadTasks = new ConcurrentHashMap<>();
|
||||
|
||||
public static synchronized ThreadTaskManager getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
@@ -48,7 +50,10 @@ public class ThreadTaskManager {
|
||||
}
|
||||
|
||||
private ThreadTaskManager() {
|
||||
mExePool = Executors.newCachedThreadPool();
|
||||
mExePool = new ThreadPoolExecutor(CORE_POOL_NUM, Integer.MAX_VALUE,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new SynchronousQueue<Runnable>());
|
||||
mExePool.allowsCoreThreadTimeOut();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,7 +79,7 @@ public abstract class AbsTask<TASK_WRAPPER extends AbsTaskWrapper>
|
||||
ALog.e(TAG, "key 为空");
|
||||
return;
|
||||
} else if (obj == null) {
|
||||
ALog.w(TAG, "扩展数据为空");
|
||||
ALog.i(TAG, "扩展数据为空");
|
||||
return;
|
||||
}
|
||||
mExpand.put(key, obj);
|
||||
|
||||
@@ -79,6 +79,11 @@ public interface ITaskWrapper {
|
||||
*/
|
||||
int U_TCP_PEER = 11;
|
||||
|
||||
/**
|
||||
* SFTP 下载
|
||||
*/
|
||||
int D_SFTP = 12;
|
||||
|
||||
/**
|
||||
* 获取任务类型
|
||||
*
|
||||
|
||||
@@ -17,10 +17,6 @@ package com.arialyy.aria.orm;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Created by laoyuyu on 2018/3/22.
|
||||
@@ -28,52 +24,8 @@ import java.net.URLEncoder;
|
||||
abstract class AbsDelegate {
|
||||
static final String TAG = "AbsDelegate";
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 检查list参数是否合法,list只能是{@code List<String>}
|
||||
*
|
||||
* @return {@code true} 合法
|
||||
*/
|
||||
boolean checkList(Field list) {
|
||||
Class t = CommonUtil.getListParamType(list);
|
||||
if (t != null && t == String.class) {
|
||||
return true;
|
||||
} else {
|
||||
ALog.d(TAG, "map参数错误,支持List<String>的参数字段");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查map参数是否合法,map只能是{@code Map<String, String>}
|
||||
*
|
||||
* @return {@code true} 合法
|
||||
*/
|
||||
boolean checkMap(Field map) {
|
||||
Class[] ts = CommonUtil.getMapParamType(map);
|
||||
if (ts != null
|
||||
&& ts[0] != null
|
||||
&& ts[1] != null
|
||||
&& ts[0] == String.class
|
||||
&& ts[1] == String.class) {
|
||||
return true;
|
||||
} else {
|
||||
ALog.d(TAG, "map参数错误,支持Map<String,String>的参数字段");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void closeCursor(Cursor cursor) {
|
||||
synchronized (AbsDelegate.class) {
|
||||
if (cursor != null && !cursor.isClosed()) {
|
||||
try {
|
||||
cursor.close();
|
||||
} catch (android.database.SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
SqlUtil.closeCursor(cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,9 +34,6 @@ abstract class AbsDelegate {
|
||||
* @return 返回数据库
|
||||
*/
|
||||
SQLiteDatabase checkDb(SQLiteDatabase db) {
|
||||
if (db == null || !db.isOpen()) {
|
||||
db = SqlHelper.getInstance().getDb();
|
||||
}
|
||||
return db;
|
||||
return SqlUtil.checkDb(db);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||||
*
|
||||
* Licensed 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.orm;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.text.TextUtils;
|
||||
import com.arialyy.aria.orm.annotation.Default;
|
||||
import com.arialyy.aria.orm.annotation.Foreign;
|
||||
import com.arialyy.aria.orm.annotation.Primary;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by laoyuyu on 2018/3/22.
|
||||
* 通用委托,创建表,检查字段
|
||||
*/
|
||||
class DelegateCommon extends AbsDelegate {
|
||||
private DelegateCommon() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的表
|
||||
*/
|
||||
void dropTable(SQLiteDatabase db, String tableName) {
|
||||
db = checkDb(db);
|
||||
String deleteSQL = String.format("DROP TABLE IF EXISTS %s", tableName);
|
||||
//db.beginTransaction();
|
||||
db.execSQL(deleteSQL);
|
||||
//db.setTransactionSuccessful();
|
||||
//db.endTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空表数据
|
||||
*/
|
||||
<T extends DbEntity> void clean(SQLiteDatabase db, Class<T> clazz) {
|
||||
db = checkDb(db);
|
||||
String tableName = CommonUtil.getClassName(clazz);
|
||||
if (tableExists(db, clazz)) {
|
||||
String sql = "DELETE FROM " + tableName;
|
||||
db.execSQL(sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找表是否存在
|
||||
*
|
||||
* @param clazz 数据库实体
|
||||
* @return true,该数据库实体对应的表存在;false,不存在
|
||||
*/
|
||||
boolean tableExists(SQLiteDatabase db, Class clazz) {
|
||||
return tableExists(db, CommonUtil.getClassName(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找表是否存在
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return true,该数据库实体对应的表存在;false,不存在
|
||||
*/
|
||||
boolean tableExists(SQLiteDatabase db, String tableName) {
|
||||
db = checkDb(db);
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
String sql =
|
||||
String.format("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'",
|
||||
tableName);
|
||||
cursor = db.rawQuery(sql, null);
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
int count = cursor.getInt(0);
|
||||
if (count > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
closeCursor(cursor);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查某个字段的值是否存在
|
||||
*
|
||||
* @param expression 字段和值"url=xxx"
|
||||
* @return {@code true}该字段的对应的value已存在
|
||||
*/
|
||||
boolean checkDataExist(SQLiteDatabase db, Class clazz, String... expression) {
|
||||
db = checkDb(db);
|
||||
if (!CommonUtil.checkSqlExpression(expression)) {
|
||||
return false;
|
||||
}
|
||||
String sql = String.format("SELECT rowid, * FROM %s WHERE %s ", CommonUtil.getClassName(clazz),
|
||||
expression[0]);
|
||||
sql = sql.replace("?", "%s");
|
||||
Object[] params = new String[expression.length - 1];
|
||||
for (int i = 0, len = params.length; i < len; i++) {
|
||||
params[i] = String.format("'%s'", SqlUtil.encodeStr(expression[i + 1]));
|
||||
}
|
||||
sql = String.format(sql, params);
|
||||
Cursor cursor = db.rawQuery(sql, null);
|
||||
final boolean isExist = cursor.getCount() > 0;
|
||||
closeCursor(cursor);
|
||||
return isExist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表
|
||||
*
|
||||
* @param clazz 数据库实体
|
||||
*/
|
||||
void createTable(SQLiteDatabase db, Class clazz) {
|
||||
db = checkDb(db);
|
||||
List<Field> fields = CommonUtil.getAllFields(clazz);
|
||||
if (fields != null && fields.size() > 0) {
|
||||
//外键Map,在Sqlite3中foreign修饰的字段必须放在最后
|
||||
final List<Field> foreignArray = new ArrayList<>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CREATE TABLE IF NOT EXISTS ")
|
||||
.append(CommonUtil.getClassName(clazz))
|
||||
.append(" (");
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
if (SqlUtil.isIgnore(field)) {
|
||||
continue;
|
||||
}
|
||||
Class<?> type = field.getType();
|
||||
sb.append(field.getName());
|
||||
if (type == String.class || type.isEnum()) {
|
||||
sb.append(" VARCHAR");
|
||||
} else if (type == int.class || type == Integer.class) {
|
||||
sb.append(" INTEGER");
|
||||
} else if (type == float.class || type == Float.class) {
|
||||
sb.append(" FLOAT");
|
||||
} else if (type == double.class || type == Double.class) {
|
||||
sb.append(" DOUBLE");
|
||||
} else if (type == long.class || type == Long.class) {
|
||||
sb.append(" BIGINT");
|
||||
} else if (type == boolean.class || type == Boolean.class) {
|
||||
sb.append(" BOOLEAN");
|
||||
} else if (type == java.util.Date.class || type == java.sql.Date.class) {
|
||||
sb.append(" DATA");
|
||||
} else if (type == byte.class || type == Byte.class) {
|
||||
sb.append(" BLOB");
|
||||
} else if (type == Map.class || type == List.class) {
|
||||
sb.append(" TEXT");
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (SqlUtil.isPrimary(field)) {
|
||||
Primary pk = field.getAnnotation(Primary.class);
|
||||
sb.append(" PRIMARY KEY");
|
||||
if (pk.autoincrement() && (type == int.class || type == Integer.class)) {
|
||||
sb.append(" AUTOINCREMENT");
|
||||
}
|
||||
}
|
||||
|
||||
if (SqlUtil.isForeign(field)) {
|
||||
foreignArray.add(field);
|
||||
}
|
||||
|
||||
if (SqlUtil.isNoNull(field)) {
|
||||
sb.append(" NOT NULL");
|
||||
}
|
||||
|
||||
if (SqlUtil.isDefault(field)) {
|
||||
Default d = field.getAnnotation(Default.class);
|
||||
if (!TextUtils.isEmpty(d.value())) {
|
||||
sb.append(" ERROR ").append("'").append(d.value()).append("'");
|
||||
}
|
||||
}
|
||||
|
||||
if (SqlUtil.isUnique(field)) {
|
||||
sb.append(" UNIQUE");
|
||||
}
|
||||
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
for (Field field : foreignArray) {
|
||||
Foreign foreign = field.getAnnotation(Foreign.class);
|
||||
sb.append("FOREIGN KEY (")
|
||||
.append(field.getName())
|
||||
.append(") REFERENCES ")
|
||||
.append(CommonUtil.getClassName(foreign.parent()))
|
||||
.append("(")
|
||||
.append(foreign.column())
|
||||
.append(")");
|
||||
ActionPolicy update = foreign.onUpdate();
|
||||
ActionPolicy delete = foreign.onDelete();
|
||||
if (update != ActionPolicy.NO_ACTION) {
|
||||
sb.append(" ON UPDATE ").append(update.function);
|
||||
}
|
||||
|
||||
if (delete != ActionPolicy.NO_ACTION) {
|
||||
sb.append(" ON DELETE ").append(update.function);
|
||||
}
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
String str = sb.toString();
|
||||
str = str.substring(0, str.length() - 1) + ");";
|
||||
db.execSQL(str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过class 获取该class的表字段
|
||||
*
|
||||
* @return 表字段列表
|
||||
*/
|
||||
List<String> getColumns(Class<? extends DbEntity> clazz) {
|
||||
List<String> columns = new ArrayList<>();
|
||||
List<Field> fields = CommonUtil.getAllFields(clazz);
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
if (SqlUtil.isIgnore(field)) {
|
||||
continue;
|
||||
}
|
||||
columns.add(field.getName());
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
}
|
||||
@@ -136,6 +136,9 @@ class DelegateFind extends AbsDelegate {
|
||||
Many m = many.getAnnotation(Many.class);
|
||||
Class parentClazz = Class.forName(one.getType().getName());
|
||||
Class childClazz = Class.forName(CommonUtil.getListParamType(many).getName());
|
||||
// 检查表
|
||||
SqlUtil.checkTable(db, parentClazz);
|
||||
SqlUtil.checkTable(db, childClazz);
|
||||
final String pTableName = parentClazz.getSimpleName();
|
||||
final String cTableName = childClazz.getSimpleName();
|
||||
List<Field> pColumn = SqlUtil.getAllNotIgnoreField(parentClazz);
|
||||
@@ -418,6 +421,7 @@ class DelegateFind extends AbsDelegate {
|
||||
*/
|
||||
private <T extends DbEntity> List<T> exeNormalDataSql(SQLiteDatabase db, Class<T> clazz,
|
||||
String sql, String[] selectionArgs) {
|
||||
SqlUtil.checkTable(db, clazz);
|
||||
String[] temp = new String[selectionArgs.length];
|
||||
int i = 0;
|
||||
for (String arg : selectionArgs) {
|
||||
|
||||
@@ -38,6 +38,7 @@ class DelegateUpdate extends AbsDelegate {
|
||||
*/
|
||||
synchronized <T extends DbEntity> void delData(SQLiteDatabase db, Class<T> clazz,
|
||||
String... expression) {
|
||||
SqlUtil.checkTable(db, clazz);
|
||||
db = checkDb(db);
|
||||
if (!CommonUtil.checkSqlExpression(expression)) {
|
||||
return;
|
||||
@@ -57,6 +58,7 @@ class DelegateUpdate extends AbsDelegate {
|
||||
* 修改某行数据
|
||||
*/
|
||||
synchronized void updateData(SQLiteDatabase db, DbEntity dbEntity) {
|
||||
SqlUtil.checkTable(db, dbEntity.getClass());
|
||||
db = checkDb(db);
|
||||
ContentValues values = createValues(dbEntity);
|
||||
if (values != null) {
|
||||
@@ -109,6 +111,7 @@ class DelegateUpdate extends AbsDelegate {
|
||||
if (oldClazz == null || oldClazz != entity.getClass() || table == null) {
|
||||
oldClazz = entity.getClass();
|
||||
table = CommonUtil.getClassName(oldClazz);
|
||||
SqlUtil.checkTable(db, oldClazz);
|
||||
}
|
||||
|
||||
ContentValues value = createValues(entity);
|
||||
@@ -130,6 +133,7 @@ class DelegateUpdate extends AbsDelegate {
|
||||
* 插入数据
|
||||
*/
|
||||
synchronized void insertData(SQLiteDatabase db, DbEntity dbEntity) {
|
||||
SqlUtil.checkTable(db, dbEntity.getClass());
|
||||
db = checkDb(db);
|
||||
ContentValues values = createValues(dbEntity);
|
||||
if (values != null) {
|
||||
@@ -156,9 +160,9 @@ class DelegateUpdate extends AbsDelegate {
|
||||
}
|
||||
String value = null;
|
||||
Type type = field.getType();
|
||||
if (type == Map.class && checkMap(field)) {
|
||||
if (type == Map.class && SqlUtil.checkMap(field)) {
|
||||
value = SqlUtil.map2Str((Map<String, String>) field.get(dbEntity));
|
||||
} else if (type == List.class && checkList(field)) {
|
||||
} else if (type == List.class && SqlUtil.checkList(field)) {
|
||||
value = SqlUtil.list2Str(dbEntity, field);
|
||||
} else {
|
||||
Object obj = field.get(dbEntity);
|
||||
|
||||
@@ -85,15 +85,14 @@ public class DelegateWrapper {
|
||||
* @return {@code true}该字段的对应的value已存在
|
||||
*/
|
||||
boolean checkDataExist(Class clazz, String... expression) {
|
||||
return mDManager.getDelegate(DelegateCommon.class)
|
||||
.checkDataExist(mDb, clazz, expression);
|
||||
return SqlUtil.checkDataExist(mDb, clazz, expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空表数据
|
||||
*/
|
||||
<T extends DbEntity> void clean(Class<T> clazz) {
|
||||
mDManager.getDelegate(DelegateCommon.class).clean(mDb, clazz);
|
||||
SqlUtil.clean(mDb, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +192,7 @@ public class DelegateWrapper {
|
||||
* 查找某张表是否存在
|
||||
*/
|
||||
public boolean tableExists(Class clazz) {
|
||||
return mDManager.getDelegate(DelegateCommon.class).tableExists(mDb, clazz);
|
||||
return SqlUtil.tableExists(mDb, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,13 +41,10 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
private static volatile SqlHelper INSTANCE = null;
|
||||
private Context mContext;
|
||||
|
||||
private DelegateCommon mDelegate;
|
||||
|
||||
synchronized static SqlHelper init(Context context) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (SqlHelper.class) {
|
||||
DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class);
|
||||
INSTANCE = new SqlHelper(context.getApplicationContext(), delegate);
|
||||
INSTANCE = new SqlHelper(context.getApplicationContext());
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
@@ -57,11 +54,10 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private SqlHelper(Context context, DelegateCommon delegate) {
|
||||
private SqlHelper(Context context) {
|
||||
super(DBConfig.SAVE_IN_SDCARD ? new DatabaseContext(context) : context, DBConfig.DB_NAME, null,
|
||||
DBConfig.VERSION);
|
||||
mContext = context;
|
||||
mDelegate = delegate;
|
||||
}
|
||||
|
||||
@Override public void onOpen(SQLiteDatabase db) {
|
||||
@@ -82,12 +78,11 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
|
||||
@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);
|
||||
if (!SqlUtil.tableExists(db, clazz)) {
|
||||
SqlUtil.createTable(db, clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,13 +162,13 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
Set<String> tables = DBConfig.mapping.keySet();
|
||||
for (String tableName : tables) {
|
||||
Class<? extends DbEntity> clazz = DBConfig.mapping.get(tableName);
|
||||
if (mDelegate.tableExists(db, clazz)) {
|
||||
if (SqlUtil.tableExists(db, clazz)) {
|
||||
//修改表名为中介表名
|
||||
String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName);
|
||||
db.execSQL(alertSql);
|
||||
|
||||
//创建新表
|
||||
mDelegate.createTable(db, clazz);
|
||||
SqlUtil.createTable(db, clazz);
|
||||
|
||||
String sql = String.format("SELECT COUNT(*) FROM %s_temp", tableName);
|
||||
Cursor cursor = db.rawQuery(sql, null);
|
||||
@@ -187,7 +182,7 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null);
|
||||
|
||||
// 获取新表的所有字段名称
|
||||
List<String> newTabColumns = mDelegate.getColumns(clazz);
|
||||
List<String> newTabColumns = SqlUtil.getColumns(clazz);
|
||||
// 获取旧表的所有字段名称
|
||||
List<String> oldTabColumns = new ArrayList<>();
|
||||
|
||||
@@ -237,9 +232,9 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
db.execSQL(insertSql);
|
||||
}
|
||||
//删除中介表
|
||||
mDelegate.dropTable(db, tableName + "_temp");
|
||||
SqlUtil.dropTable(db, tableName + "_temp");
|
||||
} else {
|
||||
mDelegate.createTable(db, clazz);
|
||||
SqlUtil.createTable(db, clazz);
|
||||
}
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
@@ -281,7 +276,7 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
if (url.startsWith("ftp") || url.startsWith("sftp")) {
|
||||
type = ITaskWrapper.D_FTP;
|
||||
} else {
|
||||
if (mDelegate.tableExists(db, M3U8Entity.class)) {
|
||||
if (SqlUtil.tableExists(db, M3U8Entity.class)) {
|
||||
Cursor m3u8c = db.rawQuery("SELECT isLive FROM M3U8Entity WHERE filePath=\""
|
||||
+ SqlUtil.encodeStr(filePath)
|
||||
+ "\"", null);
|
||||
@@ -386,8 +381,8 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
String[] taskTables =
|
||||
new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" };
|
||||
for (String taskTable : taskTables) {
|
||||
if (mDelegate.tableExists(db, taskTable)) {
|
||||
mDelegate.dropTable(db, taskTable);
|
||||
if (SqlUtil.tableExists(db, taskTable)) {
|
||||
SqlUtil.dropTable(db, taskTable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,8 +415,8 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
String[] taskTables =
|
||||
new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" };
|
||||
for (String taskTable : taskTables) {
|
||||
if (mDelegate.tableExists(db, taskTable)) {
|
||||
mDelegate.dropTable(db, taskTable);
|
||||
if (SqlUtil.tableExists(db, taskTable)) {
|
||||
SqlUtil.dropTable(db, taskTable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +425,7 @@ final class SqlHelper extends SQLiteOpenHelper {
|
||||
String[] keys = new String[] { "downloadPath", "groupName" };
|
||||
int i = 0;
|
||||
for (String tableName : tables) {
|
||||
if (!mDelegate.tableExists(db, tableName)) {
|
||||
if (!SqlUtil.tableExists(db, tableName)) {
|
||||
continue;
|
||||
}
|
||||
String pColumn = keys[i];
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.arialyy.aria.orm;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.text.TextUtils;
|
||||
import com.arialyy.aria.core.AriaConfig;
|
||||
import com.arialyy.aria.orm.annotation.Default;
|
||||
@@ -26,6 +28,7 @@ import com.arialyy.aria.orm.annotation.One;
|
||||
import com.arialyy.aria.orm.annotation.Primary;
|
||||
import com.arialyy.aria.orm.annotation.Unique;
|
||||
import com.arialyy.aria.orm.annotation.Wrapper;
|
||||
import com.arialyy.aria.util.ALog;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
@@ -41,6 +44,280 @@ import java.util.Set;
|
||||
* sql工具
|
||||
*/
|
||||
final class SqlUtil {
|
||||
private static final String TAG = CommonUtil.getClassName("SqlUtil");
|
||||
|
||||
/**
|
||||
* 检查表是否存在,不存在则创建表
|
||||
*/
|
||||
static void checkTable(SQLiteDatabase db, Class<? extends DbEntity> clazz) {
|
||||
if (!tableExists(db, clazz)) {
|
||||
createTable(db, clazz);
|
||||
}
|
||||
}
|
||||
|
||||
static void closeCursor(Cursor cursor) {
|
||||
synchronized (AbsDelegate.class) {
|
||||
if (cursor != null && !cursor.isClosed()) {
|
||||
try {
|
||||
cursor.close();
|
||||
} catch (android.database.SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找表是否存在
|
||||
*
|
||||
* @param clazz 数据库实体
|
||||
* @return true,该数据库实体对应的表存在;false,不存在
|
||||
*/
|
||||
static boolean tableExists(SQLiteDatabase db, Class<? extends DbEntity> clazz) {
|
||||
return tableExists(db, CommonUtil.getClassName(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找表是否存在
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return true,该数据库实体对应的表存在;false,不存在
|
||||
*/
|
||||
static boolean tableExists(SQLiteDatabase db, String tableName) {
|
||||
db = checkDb(db);
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
String sql =
|
||||
String.format("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'",
|
||||
tableName);
|
||||
cursor = db.rawQuery(sql, null);
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
int count = cursor.getInt(0);
|
||||
if (count > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
closeCursor(cursor);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查list参数是否合法,list只能是{@code List<String>}
|
||||
*
|
||||
* @return {@code true} 合法
|
||||
*/
|
||||
static boolean checkList(Field list) {
|
||||
Class t = CommonUtil.getListParamType(list);
|
||||
if (t == String.class) {
|
||||
return true;
|
||||
} else {
|
||||
ALog.d(TAG, "map参数错误,支持List<String>的参数字段");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查map参数是否合法,map只能是{@code Map<String, String>}
|
||||
*
|
||||
* @return {@code true} 合法
|
||||
*/
|
||||
static boolean checkMap(Field map) {
|
||||
Class[] ts = CommonUtil.getMapParamType(map);
|
||||
if (ts != null
|
||||
&& ts[0] != null
|
||||
&& ts[1] != null
|
||||
&& ts[0] == String.class
|
||||
&& ts[1] == String.class) {
|
||||
return true;
|
||||
} else {
|
||||
ALog.d(TAG, "map参数错误,支持Map<String,String>的参数字段");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的表
|
||||
*/
|
||||
static void dropTable(SQLiteDatabase db, String tableName) {
|
||||
db = checkDb(db);
|
||||
String deleteSQL = String.format("DROP TABLE IF EXISTS %s", tableName);
|
||||
//db.beginTransaction();
|
||||
db.execSQL(deleteSQL);
|
||||
//db.setTransactionSuccessful();
|
||||
//db.endTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空表数据
|
||||
*/
|
||||
static void clean(SQLiteDatabase db, Class<? extends DbEntity> clazz) {
|
||||
db = checkDb(db);
|
||||
String tableName = CommonUtil.getClassName(clazz);
|
||||
if (tableExists(db, clazz)) {
|
||||
String sql = "DELETE FROM " + tableName;
|
||||
db.execSQL(sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查某个字段的值是否存在
|
||||
*
|
||||
* @param expression 字段和值"url=xxx"
|
||||
* @return {@code true}该字段的对应的value已存在
|
||||
*/
|
||||
static boolean checkDataExist(SQLiteDatabase db, Class<? extends DbEntity> clazz,
|
||||
String... expression) {
|
||||
db = checkDb(db);
|
||||
if (!CommonUtil.checkSqlExpression(expression)) {
|
||||
return false;
|
||||
}
|
||||
String sql = String.format("SELECT rowid, * FROM %s WHERE %s ", CommonUtil.getClassName(clazz),
|
||||
expression[0]);
|
||||
sql = sql.replace("?", "%s");
|
||||
Object[] params = new String[expression.length - 1];
|
||||
for (int i = 0, len = params.length; i < len; i++) {
|
||||
params[i] = String.format("'%s'", SqlUtil.encodeStr(expression[i + 1]));
|
||||
}
|
||||
sql = String.format(sql, params);
|
||||
Cursor cursor = db.rawQuery(sql, null);
|
||||
final boolean isExist = cursor.getCount() > 0;
|
||||
closeCursor(cursor);
|
||||
return isExist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过class 获取该class的表字段
|
||||
*
|
||||
* @return 表字段列表
|
||||
*/
|
||||
static List<String> getColumns(Class<? extends DbEntity> clazz) {
|
||||
List<String> columns = new ArrayList<>();
|
||||
List<Field> fields = CommonUtil.getAllFields(clazz);
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
if (SqlUtil.isIgnore(field)) {
|
||||
continue;
|
||||
}
|
||||
columns.add(field.getName());
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查数据库是否关闭,已经关闭的话,打开数据库
|
||||
*
|
||||
* @return 返回数据库
|
||||
*/
|
||||
static SQLiteDatabase checkDb(SQLiteDatabase db) {
|
||||
if (db == null || !db.isOpen()) {
|
||||
db = SqlHelper.getInstance().getDb();
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表
|
||||
*
|
||||
* @param clazz 数据库实体
|
||||
*/
|
||||
static void createTable(SQLiteDatabase db, Class<? extends DbEntity> clazz) {
|
||||
db = checkDb(db);
|
||||
List<Field> fields = CommonUtil.getAllFields(clazz);
|
||||
if (fields != null && fields.size() > 0) {
|
||||
//外键Map,在Sqlite3中foreign修饰的字段必须放在最后
|
||||
final List<Field> foreignArray = new ArrayList<>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CREATE TABLE IF NOT EXISTS ")
|
||||
.append(CommonUtil.getClassName(clazz))
|
||||
.append(" (");
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
if (SqlUtil.isIgnore(field)) {
|
||||
continue;
|
||||
}
|
||||
Class<?> type = field.getType();
|
||||
sb.append(field.getName());
|
||||
if (type == String.class || type.isEnum()) {
|
||||
sb.append(" VARCHAR");
|
||||
} else if (type == int.class || type == Integer.class) {
|
||||
sb.append(" INTEGER");
|
||||
} else if (type == float.class || type == Float.class) {
|
||||
sb.append(" FLOAT");
|
||||
} else if (type == double.class || type == Double.class) {
|
||||
sb.append(" DOUBLE");
|
||||
} else if (type == long.class || type == Long.class) {
|
||||
sb.append(" BIGINT");
|
||||
} else if (type == boolean.class || type == Boolean.class) {
|
||||
sb.append(" BOOLEAN");
|
||||
} else if (type == java.util.Date.class || type == java.sql.Date.class) {
|
||||
sb.append(" DATA");
|
||||
} else if (type == byte.class || type == Byte.class) {
|
||||
sb.append(" BLOB");
|
||||
} else if (type == Map.class || type == List.class) {
|
||||
sb.append(" TEXT");
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (SqlUtil.isPrimary(field)) {
|
||||
Primary pk = field.getAnnotation(Primary.class);
|
||||
sb.append(" PRIMARY KEY");
|
||||
if (pk.autoincrement() && (type == int.class || type == Integer.class)) {
|
||||
sb.append(" AUTOINCREMENT");
|
||||
}
|
||||
}
|
||||
|
||||
if (SqlUtil.isForeign(field)) {
|
||||
foreignArray.add(field);
|
||||
}
|
||||
|
||||
if (SqlUtil.isNoNull(field)) {
|
||||
sb.append(" NOT NULL");
|
||||
}
|
||||
|
||||
if (SqlUtil.isDefault(field)) {
|
||||
Default d = field.getAnnotation(Default.class);
|
||||
if (!TextUtils.isEmpty(d.value())) {
|
||||
sb.append(" ERROR ").append("'").append(d.value()).append("'");
|
||||
}
|
||||
}
|
||||
|
||||
if (SqlUtil.isUnique(field)) {
|
||||
sb.append(" UNIQUE");
|
||||
}
|
||||
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
for (Field field : foreignArray) {
|
||||
Foreign foreign = field.getAnnotation(Foreign.class);
|
||||
sb.append("FOREIGN KEY (")
|
||||
.append(field.getName())
|
||||
.append(") REFERENCES ")
|
||||
.append(CommonUtil.getClassName(foreign.parent()))
|
||||
.append("(")
|
||||
.append(foreign.column())
|
||||
.append(")");
|
||||
ActionPolicy update = foreign.onUpdate();
|
||||
ActionPolicy delete = foreign.onDelete();
|
||||
if (update != ActionPolicy.NO_ACTION) {
|
||||
sb.append(" ON UPDATE ").append(update.function);
|
||||
}
|
||||
|
||||
if (delete != ActionPolicy.NO_ACTION) {
|
||||
sb.append(" ON DELETE ").append(update.function);
|
||||
}
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
String str = sb.toString();
|
||||
str = str.substring(0, str.length() - 1) + ");";
|
||||
db.execSQL(str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* URL编码字符串
|
||||
@@ -56,7 +333,7 @@ final class SqlUtil {
|
||||
/**
|
||||
* 获取主键字段名
|
||||
*/
|
||||
static String getPrimaryName(Class clazz) {
|
||||
static String getPrimaryName(Class<? extends DbEntity> clazz) {
|
||||
List<Field> fields = CommonUtil.getAllFields(clazz);
|
||||
String column;
|
||||
if (fields != null && !fields.isEmpty()) {
|
||||
@@ -201,8 +478,8 @@ final class SqlUtil {
|
||||
*
|
||||
* @return {@code true} 是
|
||||
*/
|
||||
static boolean isWrapper(Class clazz) {
|
||||
Wrapper w = (Wrapper) clazz.getAnnotation(Wrapper.class);
|
||||
static boolean isWrapper(Class<? extends AbsDbWrapper> clazz) {
|
||||
Wrapper w = clazz.getAnnotation(Wrapper.class);
|
||||
return w != null;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.lang.ref.SoftReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
||||
35
README.md
35
README.md
@@ -44,16 +44,16 @@ Aria有以下特点:
|
||||
|
||||
## 引入库
|
||||
[](https://github.com/AriaLyy/Aria/blob/master/LICENSE)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
[](https://github.com/AriaLyy/Aria)
|
||||
|
||||
```java
|
||||
implementation 'com.arialyy.aria:core:3.8'
|
||||
annotationProcessor 'com.arialyy.aria:compiler:3.8'
|
||||
implementation 'com.arialyy.aria:ftpComponent:3.8' # 如果需要使用ftp,请增加该组件
|
||||
implementation 'com.arialyy.aria:m3u8Component:3.8' # 如果需要使用m3u8下载功能,请增加该组件
|
||||
implementation 'com.arialyy.aria:core:3.8.1'
|
||||
annotationProcessor 'com.arialyy.aria:compiler:3.8.1'
|
||||
implementation 'com.arialyy.aria:ftpComponent:3.8.1' # 如果需要使用ftp,请增加该组件
|
||||
implementation 'com.arialyy.aria:m3u8Component:3.8.1' # 如果需要使用m3u8下载功能,请增加该组件
|
||||
```
|
||||
如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:<last-version>'`替换为
|
||||
```
|
||||
@@ -137,19 +137,12 @@ protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
|
||||
### 版本日志
|
||||
+ v_3.8 (2019/12/17)
|
||||
- 移除androidx和support的依赖,现在无论是哪个版本的appcompat包都可以使用本框架
|
||||
- 修复一个在xml中使用fragment导致的内存泄漏问题
|
||||
- m3u8协议的key信息增加了`keyFormat`,`keyFormatVersion`字段
|
||||
- m3u8增加了`ignoreFailureTs`方法,忽略下载失败的ts切片
|
||||
- 修复在dialogFragment的`onCreateDialog()`注册导致的注解不生效问题
|
||||
- 修复组合任务初始化失败时,无法删除的问题
|
||||
- 修复`reStart()`后,无法停止的问题
|
||||
- ftp增加主动模式,开启主动模式:https://aria.laoyuyu.me/aria_doc/api/ftp_params.html
|
||||
- 修复ftp服务器无法响应`abor`命令导致的无法停止上传的问题 https://github.com/AriaLyy/Aria/issues/564
|
||||
- 修复ftp上传时,服务器有长度为0的文件导致上传失败的问题
|
||||
- 修复下载任务和上传任务的文件路径是同一个时,导致的记录混乱问题
|
||||
- 优化提示
|
||||
+ v_3.8.1 (2019/12/22)
|
||||
- 修复一个表创建失败的问题 https://github.com/AriaLyy/Aria/issues/570
|
||||
- 修复一个非分块模式下导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/571
|
||||
- 修复一个服务器端无法创建socket连接,却没有返回码导致客户端卡住的问题 https://github.com/AriaLyy/Aria/issues/569
|
||||
- 修复文件删除后,组合任务没有重新下载的问题 https://github.com/AriaLyy/Aria/issues/574
|
||||
- 优化缓存队列和执行队列
|
||||
|
||||
[更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
3、只对新的多线程下载任务有效
|
||||
4、只对多线程的任务有效
|
||||
-->
|
||||
<useBlock value="true"/>
|
||||
<useBlock value="false"/>
|
||||
|
||||
<!--设置下载线程数,下载线程数不能小于1
|
||||
注意:
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<aria>
|
||||
<!--注意,修改该配置文件中的属性会覆盖代码中所设置的属性-->
|
||||
|
||||
<!--Aria框架配置-->
|
||||
<app>
|
||||
<!--是否使用AriaCrashHandler来捕获异常,异常日志保存在:/mnt/sdcard/Android/data/{package_name}/files/log/-->
|
||||
<useAriaCrashHandler value="true"/>
|
||||
<!--设置Aria的日志级别,{@link ALog#LOG_LEVEL_VERBOSE}-->
|
||||
<logLevel value="2"/>
|
||||
<!-- 是否检查网络 true: 检查网络,false: 不检查网络-->
|
||||
<netCheck value="true"/>
|
||||
<!--除非无法使用注解,否则不建议使用广播来接受任务状态,true:使用广播接收任务状态,false:不适用广播接收状态 -->
|
||||
<!-- http://aria.laoyuyu.me/aria_doc/api/use_broadcast.html -->
|
||||
<useBroadcast value="true"/>
|
||||
<!--断网的时候是否重试,true:断网也重试;false:断网不重试,直接走失败的回调-->
|
||||
<notNetRetry value="false"/>
|
||||
</app>
|
||||
|
||||
|
||||
<!--普通下载任务-->
|
||||
<download>
|
||||
|
||||
<!--设置任务最大下载速度,0表示不限速,单位为:kb-->
|
||||
<maxSpeed value="0"/>
|
||||
|
||||
<!--
|
||||
多线程下载是否使用块下载模式,{@code true}使用,{@code false}不使用
|
||||
注意:
|
||||
1、使用分块模式,在I/O性能底下的手机上,合并文件需要的时间会更加长;
|
||||
2、优点是使用多线程的块下载,初始化时,文件初始化时将不会预占用对应长度的空间;
|
||||
3、只对新的多线程下载任务有效
|
||||
4、只对多线程的任务有效
|
||||
-->
|
||||
<useBlock value="false"/>
|
||||
|
||||
<!--设置下载线程数,下载线程数不能小于1
|
||||
注意:
|
||||
1、线程下载数改变后,新的下载任务才会生效;
|
||||
2、如果任务大小小于1m,该设置不会生效;
|
||||
3、从3.4.1开始,如果线程数为1,文件初始化时将不再预占用对应长度的空间,下载多少byte,则占多大的空间;
|
||||
对于3.4.1之前版本的未完成的老任务,依然采用原来的文件空间占用方式;
|
||||
-->
|
||||
<threadNum value="3"/>
|
||||
|
||||
<!--设置下载队列最大任务数, 默认为2-->
|
||||
<maxTaskNum value="3"/>
|
||||
|
||||
<!--设置下载失败,重试次数,默认为10-->
|
||||
<reTryNum value="5"/>
|
||||
|
||||
<!--设置重试间隔,单位为毫秒,默认2000毫秒-->
|
||||
<reTryInterval value="5000"/>
|
||||
|
||||
<!--设置url连接超时时间,单位为毫秒,默认5000毫秒-->
|
||||
<connectTimeOut value="5000"/>
|
||||
|
||||
<!--设置IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒-->
|
||||
<iOTimeOut value="10000"/>
|
||||
|
||||
<!--设置写文件buff大小,该数值大小不能小于2048,数值变小,下载速度会变慢-->
|
||||
<buffSize value="8192"/>
|
||||
|
||||
<!--设置https ca 证书信息;path 为assets目录下的CA证书完整路径,name 为CA证书名-->
|
||||
<ca name="" path=""/>
|
||||
|
||||
<!--是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度-->
|
||||
<convertSpeed value="true"/>
|
||||
|
||||
<!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait-->
|
||||
<queueMod value="wait"/>
|
||||
|
||||
<!--进度更新更新间隔,默认1000毫秒-->
|
||||
<updateInterval value="1000"/>
|
||||
|
||||
</download>
|
||||
|
||||
<!--普通上传任务-->
|
||||
<upload>
|
||||
<!--设置任务最大上传速度,0表示不限速,单位为:kb-->
|
||||
<maxSpeed value="0"/>
|
||||
|
||||
<!--设置IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒-->
|
||||
<iOTimeOut value="10000"/>
|
||||
|
||||
<!--设置写文件buff大小,该数值大小不能小于2048,数值变小,速度会变慢-->
|
||||
<buffSize value="8192"/>
|
||||
|
||||
<!--是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度-->
|
||||
<convertSpeed value="true"/>
|
||||
|
||||
<!--设置上传队列最大任务数, 默认为2-->
|
||||
<maxTaskNum value="2"/>
|
||||
|
||||
<!--设置上传失败,重试次数,默认为10-->
|
||||
<reTryNum value="3"/>
|
||||
|
||||
<!--设置重试间隔,单位为毫秒-->
|
||||
<reTryInterval value="2000"/>
|
||||
|
||||
<!--设置url连接超时时间,单位为毫秒,默认5000毫秒-->
|
||||
<connectTimeOut value="5000"/>
|
||||
|
||||
<!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait-->
|
||||
<queueMod value="wait"/>
|
||||
|
||||
<!--进度更新更新间隔,默认1000毫秒-->
|
||||
<updateInterval value="1000"/>
|
||||
|
||||
</upload>
|
||||
|
||||
<!-- 下载类组合任务 -->
|
||||
<dGroup>
|
||||
|
||||
<!--组合任务下载队列最大任务数, 默认为2-->
|
||||
<maxTaskNum value="2"/>
|
||||
|
||||
<!--设置下载失败,重试次数,默认为10-->
|
||||
<reTryNum value="1"/>
|
||||
|
||||
<!--设置重试间隔,单位为毫秒,默认2000毫秒-->
|
||||
<reTryInterval value="5000"/>
|
||||
|
||||
<!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait-->
|
||||
<queueMod value="wait"/>
|
||||
|
||||
<!--进度更新更新间隔,默认1000毫秒-->
|
||||
<updateInterval value="1000"/>
|
||||
|
||||
|
||||
<!-- =============================以下为子任务的配置====================================-->
|
||||
|
||||
<!--能同时下载的子任务最大任务数,默认3-->
|
||||
<subMaxTaskNum value="5"/>
|
||||
|
||||
<!--子任务下载失败时的重试次数,默认为5-->
|
||||
<subReTryNum value="5"/>
|
||||
|
||||
<!--子任务下载失败时的重试间隔,单位为毫秒,默认2000毫秒-->
|
||||
<subReTryInterval value="5000"/>
|
||||
|
||||
<!--子任务url连接超时时间,单位为毫秒,默认5000毫秒-->
|
||||
<connectTimeOut value="5000"/>
|
||||
|
||||
<!--子任务IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒-->
|
||||
<iOTimeOut value="10000"/>
|
||||
|
||||
<!--子任务写文件buff大小,该数值大小不能小于2048,数值变小,下载速度会变慢-->
|
||||
<buffSize value="8192"/>
|
||||
|
||||
<!--子任务 https ca 证书信息;path 为assets目录下的CA证书完整路径,name 为CA证书名-->
|
||||
<ca name="" path=""/>
|
||||
|
||||
<!--子任务是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度-->
|
||||
<convertSpeed value="true"/>
|
||||
|
||||
<!--子任务的最大下载速度,0表示不限速,单位为:kb; -->
|
||||
<maxSpeed value="0"/>
|
||||
|
||||
</dGroup>
|
||||
</aria>
|
||||
@@ -117,6 +117,8 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> {
|
||||
module.startNextActivity(MainActivity.this, data.get(position),
|
||||
M3U8LiveDLoadActivity.class);
|
||||
break;
|
||||
case 8: // sftp
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -97,7 +97,7 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
|
||||
.setDirPath(
|
||||
Environment.getExternalStorageDirectory().getPath() + "/Download/group_imgs")
|
||||
//.setSubFileName(getModule(GroupModule.class).getSubName2())
|
||||
//.setSubFileName(getModule(GroupModule.class).getSubName())
|
||||
.setSubFileName(getModule(GroupModule.class).getSubName())
|
||||
.unknownSize()
|
||||
.option(getHttpOption())
|
||||
.ignoreFilePathOccupy()
|
||||
|
||||
@@ -103,7 +103,8 @@ public class CommonModule extends BaseViewModule {
|
||||
R.drawable.ic_ftp_dir,
|
||||
R.drawable.ic_ftp,
|
||||
R.drawable.ic_ts,
|
||||
R.drawable.ic_live
|
||||
R.drawable.ic_live,
|
||||
R.drawable.ic_sftp
|
||||
};
|
||||
int i = 0;
|
||||
for (String title : titles) {
|
||||
|
||||
15
app/src/main/res/drawable/ic_sftp.xml
Normal file
15
app/src/main/res/drawable/ic_sftp.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M694.4,1.6L163.2,1.6C113.6,1.6 73.6,41.6 73.6,91.2v843.2c0,49.6 40,89.6 89.6,89.6h475.2c-20.8,-14.4 -33.6,-36.8 -33.6,-64L604.8,758.4c0,-41.6 32,-84.8 75.2,-84.8h209.6l1.6,-475.2L694.4,1.6zM307.2,545.6c-6.4,9.6 -14.4,16 -25.6,20.8 -11.2,4.8 -24,8 -38.4,8 -24,0 -41.6,-4.8 -54.4,-14.4 -12.8,-9.6 -20.8,-24 -24,-43.2l30.4,-4.8c1.6,12.8 8,20.8 16,27.2 8,6.4 19.2,9.6 33.6,9.6 14.4,0 22.4,-3.2 28.8,-9.6s9.6,-11.2 9.6,-17.6c0,-6.4 -3.2,-12.8 -9.6,-16 -4.8,-3.2 -14.4,-4.8 -30.4,-9.6 -22.4,-6.4 -38.4,-11.2 -46.4,-14.4 -8,-4.8 -16,-9.6 -19.2,-17.6 -4.8,-8 -6.4,-16 -6.4,-24s1.6,-16 4.8,-22.4c3.2,-6.4 8,-12.8 14.4,-17.6 4.8,-3.2 14.4,-9.6 22.4,-12.8 8,-3.2 17.6,-3.2 27.2,-3.2 14.4,0 22.4,1.6 33.6,6.4 6.4,3.2 19.2,12.8 24,20.8 4.8,6.4 8,16 11.2,27.2l-30.4,4.8c-1.6,-9.6 -4.8,-16 -11.2,-22.4 -6.4,-4.8 -16,-8 -27.2,-8 -14.4,0 -22.4,1.6 -27.2,4.8 -6.4,4.8 -11.2,11.2 -11.2,17.6 0,3.2 0,8 1.6,11.2 3.2,3.2 6.4,6.4 11.2,8 3.2,1.6 12.8,3.2 27.2,8 22.4,6.4 36.8,11.2 44.8,14.4 8,3.2 16,9.6 20.8,17.6 4.8,6.4 8,16 8,25.6 0,8 -3.2,17.6 -8,25.6zM465.6,417.6h-91.2v44.8h78.4v32h-78.4v80L336,574.4L336,385.6h129.6v32zM633.6,417.6h-56v156.8h-38.4v-156.8h-56v-32h150.4v32zM790.4,473.6c-4.8,8 -9.6,14.4 -16,19.2 -6.4,4.8 -12.8,8 -19.2,9.6 -9.6,1.6 -22.4,3.2 -40,3.2h-25.6v72h-38.4v-192h60.8c22.4,0 38.4,1.6 44.8,3.2 11.2,3.2 19.2,9.6 27.2,19.2s11.2,22.4 11.2,36.8c0,11.2 -1.6,20.8 -4.8,28.8zM694.4,196.8L694.4,51.2l145.6,145.6h-145.6z"
|
||||
android:fillColor="@color/icon_color"/>
|
||||
<path
|
||||
android:pathData="M736,419.2c-4.8,-1.6 -14.4,-1.6 -27.2,-1.6h-19.2v52.8h20.8c14.4,0 25.6,-1.6 30.4,-3.2 4.8,-1.6 9.6,-4.8 11.2,-9.6 3.2,-4.8 4.8,-9.6 4.8,-14.4 0,-6.4 -1.6,-12.8 -6.4,-17.6 -3.2,-3.2 -9.6,-4.8 -14.4,-6.4zM795.2,867.2c-11.2,0 -22.4,9.6 -22.4,22.4 0,9.6 6.4,17.6 14.4,20.8v30.4h14.4v-30.4c8,-3.2 14.4,-11.2 14.4,-20.8 1.6,-12.8 -8,-22.4 -20.8,-22.4zM795.2,764.8c-16,0 -28.8,12.8 -28.8,28.8v28.8h57.6v-28.8c1.6,-16 -12.8,-28.8 -28.8,-28.8z"
|
||||
android:fillColor="@color/icon_color"/>
|
||||
<path
|
||||
android:pathData="M892.8,705.6L699.2,705.6c-32,0 -57.6,25.6 -57.6,56L641.6,960c0,30.4 25.6,56 57.6,56h195.2c32,0 57.6,-25.6 57.6,-56L952,761.6c-1.6,-30.4 -27.2,-56 -59.2,-56zM883.2,955.2c0,8 -6.4,14.4 -14.4,14.4h-144c-8,0 -14.4,-6.4 -14.4,-14.4L710.4,832c0,-8 6.4,-14.4 14.4,-14.4h22.4v-28.8c0,-27.2 22.4,-51.2 51.2,-51.2 27.2,0 51.2,22.4 51.2,51.2v28.8h22.4c8,0 14.4,6.4 14.4,14.4v123.2z"
|
||||
android:fillColor="@color/icon_color"/>
|
||||
</vector>
|
||||
@@ -32,14 +32,15 @@
|
||||
<string name="error_file_name_null">文件名为空</string>
|
||||
|
||||
<string-array name="main_items">
|
||||
<item>http 下载</item>
|
||||
<item>http 上传</item>
|
||||
<item>http 组合任务下载</item>
|
||||
<item>HTTP 下载</item>
|
||||
<item>HTTP 上传</item>
|
||||
<item>HTTP 组合任务下载</item>
|
||||
<item>FTP 下载</item>
|
||||
<item>FTP 文件夹下载</item>
|
||||
<item>FTP 文件上传</item>
|
||||
<item>M3U8 点播文件下载</item>
|
||||
<item>M3U8 直播文件下载</item>
|
||||
<item>SFTP 下载</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="main_items_desc">
|
||||
@@ -51,6 +52,7 @@
|
||||
<item>上传单个文件到服务器</item>
|
||||
<item>下载M3U8点播文件</item>
|
||||
<item>下载M3U8直播文件</item>
|
||||
<item>SFTP单文件下载</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="download_items">
|
||||
|
||||
@@ -44,8 +44,8 @@ task clean(type: Delete) {
|
||||
}
|
||||
|
||||
ext {
|
||||
versionCode = 380
|
||||
versionName = '3.8'
|
||||
versionCode = 381
|
||||
versionName = '3.8.1'
|
||||
userOrg = 'arialyy'
|
||||
groupId = 'com.arialyy.aria'
|
||||
publishVersion = versionName
|
||||
|
||||
Reference in New Issue
Block a user