diff --git a/AppFrame/build.gradle b/AppFrame/build.gradle index 2dc8b08a..f95f1802 100644 --- a/AppFrame/build.gradle +++ b/AppFrame/build.gradle @@ -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' diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java index ea3c768a..eb5f9c99 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java @@ -95,8 +95,7 @@ public abstract class AbsTaskQueue { */ public void setTaskHighestPriority(DownloadTask task) { task.setHighestPriority(true); - Map exeTasks = mExecutePool.getAllTask(); + //Map exeTasks = mExecutePool.getAllTask(); + List exeTasks = mExecutePool.getAllTask(); if (exeTasks != null && !exeTasks.isEmpty()) { - Set 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 { return; } } - } - int maxSize = AriaConfig.getInstance().getDConfig().getMaxTaskNum(); - int currentSize = mExecutePool.size(); - if (currentSize == 0 || currentSize < maxSize) { - startTask(task); - } else { - Set 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 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); + } } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java index 2fd7f409..89a442fc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java @@ -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 implements IPool { - 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 mCacheMap; - private LinkedBlockingQueue mCacheQueue; + private Deque mCacheQueue; BaseCachePool() { - mCacheQueue = new LinkedBlockingQueue<>(MAX_NUM); - mCacheMap = new ConcurrentHashMap<>(); + mCacheQueue = new LinkedBlockingDeque<>(MAX_NUM); } /** * 获取被缓存的任务 */ - public Map getAllTask() { - return mCacheMap; + public List 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 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 implements IPool { 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 implements IPool { @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 implements IPool { 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 implements IPool { 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 implements IPool { 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)); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java index 4ae49c99..533105ee 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java @@ -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 implements IPool { - 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 mExecuteQueue; - Map mExecuteMap; + Deque mExecuteQueue; int mSize; BaseExecutePool() { mSize = getMaxSize(); - mExecuteQueue = new ArrayBlockingQueue<>(mSize); - mExecuteMap = new ConcurrentHashMap<>(); + mExecuteQueue = new LinkedBlockingDeque<>(mSize); } /** @@ -55,8 +52,8 @@ public class BaseExecutePool implements IPool { /** * 获取所有正在执行的任务 */ - public Map getAllTask() { - return mExecuteMap; + public List getAllTask() { + return new ArrayList<>(mExecuteQueue); } @Override public boolean putTask(TASK task) { @@ -88,17 +85,13 @@ public class BaseExecutePool implements IPool { */ public void setMaxNum(int maxNum) { synchronized (LOCK) { - try { - ArrayBlockingQueue 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 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 implements IPool { */ 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 implements IPool { */ 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 implements IPool { 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 implements IPool { 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)); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java index 099c1002..ee3ea11e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java @@ -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 extends DLoadExecutePool { - private final String TAG = "DGLoadExecutePool"; + private final String TAG = CommonUtil.getClassName(this); @Override protected int getMaxSize() { return AriaConfig.getInstance().getDGConfig().getMaxTaskNum(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java index d8db105f..bfa8afd3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java @@ -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 extends BaseExecutePool { return false; } else { if (mExecuteQueue.size() >= mSize) { - Set 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 extends BaseExecutePool { } @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; } } diff --git a/DEV_LOG.md b/DEV_LOG.md index bd29e876..2d90f30c 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -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()` diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java index 07e9cae3..5b85dcc7 100644 --- a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java @@ -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 { diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java index f3c604f3..4d029c3a 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java @@ -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(); diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java index af113b53..9ffc376e 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java @@ -42,10 +42,6 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { 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 { try { IFtpUploadInterceptor interceptor = mTaskOption.getUploadInterceptor(); if (interceptor != null) { - useInterceptor = true; + /** + * true 使用拦截器,false 不使用拦截器 + */ List files = new ArrayList<>(); for (FTPFile ftpFile : ftpFiles) { if (ftpFile.isDirectory()) { @@ -118,7 +116,7 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { */ @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; diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java index bd5e4bb7..c8da304c 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java @@ -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, diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java index 9c7103e0..e484c5cc 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java @@ -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(); } } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java index fc66b55a..016576c7 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java @@ -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) { diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java index 45ea172a..1ae4cdee 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java @@ -143,6 +143,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { is.close(); } if (conn != null) { + conn.getInputStream().close(); conn.disconnect(); } } catch (IOException e) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java index 12c2dd3d..2975dc29 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -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)的长度 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java index 12e8bdce..b216b003 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java @@ -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)); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java index c36be997..2dc40426 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java @@ -122,8 +122,9 @@ public abstract class BaseListener> mThreadTasks = new ConcurrentHashMap<>(); + private static final int CORE_POOL_NUM = 20; private static final ReentrantLock LOCK = new ReentrantLock(); + private ThreadPoolExecutor mExePool; + private Map> 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()); + mExePool.allowsCoreThreadTimeOut(); } /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java index 503c0869..46cefc0d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java @@ -79,7 +79,7 @@ public abstract class AbsTask ALog.e(TAG, "key 为空"); return; } else if (obj == null) { - ALog.w(TAG, "扩展数据为空"); + ALog.i(TAG, "扩展数据为空"); return; } mExpand.put(key, obj); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java index 047e6112..7f3acdc8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java @@ -79,6 +79,11 @@ public interface ITaskWrapper { */ int U_TCP_PEER = 11; + /** + * SFTP 下载 + */ + int D_SFTP = 12; + /** * 获取任务类型 * diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java index 132139f3..499e81bb 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java @@ -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} - * - * @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的参数字段"); - return false; - } - } - - /** - * 检查map参数是否合法,map只能是{@code Map} - * - * @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的参数字段"); - 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); } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java deleted file mode 100644 index d0205bf8..00000000 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java +++ /dev/null @@ -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(); - } - - /** - * 清空表数据 - */ - void clean(SQLiteDatabase db, Class 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 fields = CommonUtil.getAllFields(clazz); - if (fields != null && fields.size() > 0) { - //外键Map,在Sqlite3中foreign修饰的字段必须放在最后 - final List 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 getColumns(Class clazz) { - List columns = new ArrayList<>(); - List fields = CommonUtil.getAllFields(clazz); - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.isIgnore(field)) { - continue; - } - columns.add(field.getName()); - } - return columns; - } -} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java index 0e6a26e6..21f2af4f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java @@ -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 pColumn = SqlUtil.getAllNotIgnoreField(parentClazz); @@ -418,6 +421,7 @@ class DelegateFind extends AbsDelegate { */ private List exeNormalDataSql(SQLiteDatabase db, Class clazz, String sql, String[] selectionArgs) { + SqlUtil.checkTable(db, clazz); String[] temp = new String[selectionArgs.length]; int i = 0; for (String arg : selectionArgs) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java index 00e32002..4ee99afe 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java @@ -38,6 +38,7 @@ class DelegateUpdate extends AbsDelegate { */ synchronized void delData(SQLiteDatabase db, Class 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) 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); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java index 53ab959d..15ae2eed 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java @@ -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); } /** * 清空表数据 */ void clean(Class 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); } /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java index 546a84c0..6cfd18cd 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -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 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 tables = DBConfig.mapping.keySet(); for (String tableName : tables) { Class 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 newTabColumns = mDelegate.getColumns(clazz); + List newTabColumns = SqlUtil.getColumns(clazz); // 获取旧表的所有字段名称 List 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]; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java index c112784f..e3141c6d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java @@ -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 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 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} + * + * @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的参数字段"); + return false; + } + } + + /** + * 检查map参数是否合法,map只能是{@code Map} + * + * @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的参数字段"); + 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 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 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 getColumns(Class clazz) { + List columns = new ArrayList<>(); + List 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 clazz) { + db = checkDb(db); + List fields = CommonUtil.getAllFields(clazz); + if (fields != null && fields.size() > 0) { + //外键Map,在Sqlite3中foreign修饰的字段必须放在最后 + final List 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 clazz) { List 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 clazz) { + Wrapper w = clazz.getAnnotation(Wrapper.class); return w != null; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java index 5ea85006..75d9a2a0 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java @@ -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; /** diff --git a/README.md b/README.md index 16f18028..4d93ca41 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.8-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.8-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.8-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.8-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.8.1-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.8.1-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.8.1-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.8.1-orange)](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:'`替换为 ``` @@ -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) diff --git a/app/src/main/assets/aria_config.xml b/app/src/main/assets/aria_config.xml index cb8d1c24..e4ea2d42 100644 --- a/app/src/main/assets/aria_config.xml +++ b/app/src/main/assets/aria_config.xml @@ -32,7 +32,7 @@ 3、只对新的多线程下载任务有效 4、只对多线程的任务有效 --> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/MainActivity.java b/app/src/main/java/com/arialyy/simple/MainActivity.java index 16881fc4..cbf32eb2 100644 --- a/app/src/main/java/com/arialyy/simple/MainActivity.java +++ b/app/src/main/java/com/arialyy/simple/MainActivity.java @@ -117,6 +117,8 @@ public class MainActivity extends BaseActivity { module.startNextActivity(MainActivity.this, data.get(position), M3U8LiveDLoadActivity.class); break; + case 8: // sftp + break; } } }); diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java index 998f1ed5..2b9980a1 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java @@ -97,7 +97,7 @@ public class DownloadGroupActivity extends BaseActivity + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 62666bbf..8a0c0cc9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,14 +32,15 @@ 文件名为空 - http 下载 - http 上传 - http 组合任务下载 + HTTP 下载 + HTTP 上传 + HTTP 组合任务下载 FTP 下载 FTP 文件夹下载 FTP 文件上传 M3U8 点播文件下载 M3U8 直播文件下载 + SFTP 下载 @@ -51,6 +52,7 @@ 上传单个文件到服务器 下载M3U8点播文件 下载M3U8直播文件 + SFTP单文件下载 diff --git a/build.gradle b/build.gradle index c5910ecb..63797bf6 100644 --- a/build.gradle +++ b/build.gradle @@ -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