diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java index 521284f5..6aafda03 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java @@ -19,16 +19,17 @@ import android.content.Context; import android.util.SparseArray; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IEventListener; -import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.upload.UploadTaskEntity; +import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; import java.util.Properties; import java.util.Set; import java.util.Timer; @@ -50,10 +51,8 @@ public abstract class AbsFileer records = + DbEntity.findRelationData(RecordWrapper.class, "TaskRecord.filePath=?", + mTaskEntity.getKey()); + if (records == null || records.size() == 0) { + initRecord(); + mTaskEntity.setNewTask(true); + } else { + mRecord = records.get(0).taskRecord; + if (mRecord.threadRecords == null || mRecord.threadRecords.isEmpty()) { + initRecord(); + mTaskEntity.setNewTask(true); + } else { + for (ThreadRecord tr : mRecord.threadRecords) { + if (tr.isComplete) { + mCompleteThreadNum++; + } else { + mStartThreadNum++; + } + } + mTotalThreadNum = mRecord.threadRecords.size(); + } + } + } + } /** - * 检查记录文件,如果是新任务返回{@code true},否则返回{@code false} + * convertDb 为兼容性代码 + * 从3.4.1开始,线程配置信息将存储在数据库中。 + * 将配置文件的内容复制到数据库中,并将配置文件删除 */ - protected boolean checkConfigFile() { - mStartThreadNum = 0; - mCompleteThreadNum = 0; - Properties pro = CommonUtil.loadConfig(mConfigFile); - if (pro.isEmpty()) { - return true; - } - Set keys = pro.keySet(); - int num = 0; - for (Object key : keys) { - String str = String.valueOf(key); - Object state = pro.getProperty(str); - if (str.contains(RECORD)) { - num++; - } else if (state != null && str.contains(STATE) && Integer.parseInt(state + "") == 1) { + private void convertDb() { + List records = + DbEntity.findRelationData(RecordWrapper.class, "TaskRecord.filePath=?", + mTaskEntity.getKey()); + if (records == null || records.size() == 0) { + Properties pro = CommonUtil.loadConfig(mConfigFile); + if (pro.isEmpty()) { + mTaskEntity.setNewTask(true); + return; + } + initRecord(); + Set keys = pro.keySet(); + // 老版本记录是5s存一次,但是5s中内,如果线程执行完成,record记录是没有的,只有state记录... + // 第一步应该是record 和 state去重取正确的线程数 + Set set = new HashSet<>(); + for (Object key : keys) { + String str = String.valueOf(key); int i = Integer.parseInt(str.substring(str.length() - 1, str.length())); - if (pro.getProperty(mTempFile.getName() + RECORD + i) != null) { - mCompleteThreadNum++; - } + set.add(i); } - } - if (num == 0) { - return true; - } - mStartThreadNum = num; - for (int i = 0; i < mStartThreadNum; i++) { - if (pro.getProperty(mTempFile.getName() + RECORD + i) == null) { + int threadNum = set.size(); + if (threadNum == 0) { + mTaskEntity.setNewTask(true); + return; + } + mRecord.threadNum = threadNum; + mTotalThreadNum = threadNum; + + for (int i = 0; i < threadNum; i++) { + ThreadRecord tRecord = new ThreadRecord(); + tRecord.key = mRecord.filePath; Object state = pro.getProperty(mTempFile.getName() + STATE + i); + Object record = pro.getProperty(mTempFile.getName() + RECORD + i); if (state != null && Integer.parseInt(state + "") == 1) { + mCompleteThreadNum++; + tRecord.isComplete = true; continue; } - return true; + mStartThreadNum++; + if (record != null) { + Long temp = Long.parseLong(record + ""); + tRecord.startLocation = temp > 0 ? temp : 0; + } else { + tRecord.startLocation = 0; + } + mRecord.threadRecords.add(tRecord); } + mConfigFile.delete(); } - return false; + } + + /** + * 初始化记录 + */ + private void initRecord() { + mRecord = new TaskRecord(); + mRecord.fileName = mEntity.getFileName(); + mRecord.filePath = mTaskEntity.getKey(); + mRecord.threadRecords = new ArrayList<>(); + mRecord.isGroupRecord = mTaskEntity.getEntity().isGroupChild(); + if (mRecord.isGroupRecord) { + if (mTaskEntity.getEntity() instanceof DownloadEntity) { + mRecord.dGroupName = ((DownloadEntity) mTaskEntity.getEntity()).getGroupName(); + } + } + } + + /** + * 保存任务记录 + */ + private void saveRecord() { + mRecord.save(); + for (ThreadRecord tr : mRecord.threadRecords) { + tr.save(); + } + } + + public TaskRecord getRecord() { + return mRecord; } /** @@ -291,9 +366,7 @@ public abstract class AbsFileer config = new SubThreadConfig<>(); config.FILE_SIZE = fileLength; config.URL = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl(); @@ -317,66 +391,76 @@ public abstract class AbsFileer= 0) { - Long r = Long.parseLong(record + ""); + if (tr.startLocation >= 0) { + Long r = tr.startLocation; //记录的位置需要在线程区间中 if (startL < r && r < (i == (mTotalThreadNum - 1) ? fileLength : endL)) { mConstance.CURRENT_LOCATION += r - startL; startL = r; } ALog.d(TAG, "任务【" + mEntity.getFileName() + "】线程__" + i + "__恢复下载"); - recordL[rl] = i; - rl++; - } else { - recordL[rl] = i; - rl++; } + //最后一个线程的结束位置即为文件的总长度 if (i == (mTotalThreadNum - 1)) { - //最后一个线程的结束位置即为文件的总长度 endL = fileLength; } - AbsThreadTask task = createSingThreadTask(i, startL, endL, fileLength); + // 更新记录 + tr.startLocation = startL; + tr.endLocation = endL; + if (isNewTr) { + mRecord.threadRecords.add(tr); + } + AbsThreadTask task = createSingThreadTask(i, startL, endL, fileLength, tr); if (task == null) return; mTask.put(i, task); + threadId[rl] = i; + rl++; } - startSingleTask(recordL); + saveRecord(); + startThreadTask(threadId); } /** * 启动单线程下载任务 */ - private void startSingleTask(int[] recordL) { + private void startThreadTask(int[] recordL) { if (mConstance.CURRENT_LOCATION > 0) { mListener.onResume(mConstance.CURRENT_LOCATION); } else { @@ -390,7 +474,6 @@ public abstract class AbsFileer mConfig; @@ -84,7 +80,6 @@ public abstract class AbsThreadTask= mConfig.END_LOCATION || isComplete) { - key = mConfig.TEMP_FILE.getName() + AbsFileer.STATE + mConfig.THREAD_ID; - value = "1"; - } else if (0 < record && record < mConfig.END_LOCATION) { - key = mConfig.TEMP_FILE.getName() + AbsFileer.RECORD + mConfig.THREAD_ID; - value = String.valueOf(record); - } - if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) { - File configFile = new File(mConfigFPath); - Properties pro = CommonUtil.loadConfig(configFile); - pro.setProperty(key, value); - CommonUtil.saveConfig(configFile, pro); + if (mConfig.THREAD_RECORD != null) { + mConfig.THREAD_RECORD.isComplete = isComplete; + if (0 < record && record < mConfig.END_LOCATION) { + mConfig.START_LOCATION = record; } + mConfig.THREAD_RECORD.update(); } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/RecordWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/common/RecordWrapper.java new file mode 100644 index 00000000..3d61c124 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/common/RecordWrapper.java @@ -0,0 +1,45 @@ +/* + * 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.core.common; + +import com.arialyy.aria.orm.AbsWrapper; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by laoyuyu on 2018/3/30. + * 任务记录和线程记录的关系 + */ +@Wrapper +public class RecordWrapper extends AbsWrapper { + + @One + public TaskRecord taskRecord; + + @Many(parentColumn = "filePath", entityColumn = "key") + public List threadRecords; + + @Override protected void handleConvert() { + if (threadRecords != null && !threadRecords.isEmpty()) { + taskRecord.threadRecords = threadRecords; + } else { + taskRecord.threadRecords = new ArrayList<>(); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/StateConstance.java b/Aria/src/main/java/com/arialyy/aria/core/common/StateConstance.java index e42e3f86..67b7ddbc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/StateConstance.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/StateConstance.java @@ -26,11 +26,12 @@ public class StateConstance { public int CONNECT_TIME_OUT; //连接超时时间 public int READ_TIME_OUT; //流读取的超时时间 public int COMPLETE_THREAD_NUM = 0; - public int THREAD_NUM; + public int START_THREAD_NUM; //启动的线程数 public long CURRENT_LOCATION = 0; public boolean isRunning = false; public boolean isCancel = false; public boolean isStop = false; + public TaskRecord TASK_RECORD; public StateConstance() { } @@ -49,27 +50,28 @@ public class StateConstance { * 所有子线程是否都已经停止下载 */ public boolean isStop() { - return STOP_NUM == THREAD_NUM; + return STOP_NUM == START_THREAD_NUM; } /** * 所有子线程是否都已经下载失败 */ public boolean isFail() { - return COMPLETE_THREAD_NUM != THREAD_NUM && FAIL_NUM + COMPLETE_THREAD_NUM >= THREAD_NUM; + return COMPLETE_THREAD_NUM != START_THREAD_NUM + && FAIL_NUM + COMPLETE_THREAD_NUM >= START_THREAD_NUM; } /** * 所有子线程是否都已经完成下载 */ public boolean isComplete() { - return COMPLETE_THREAD_NUM >= THREAD_NUM; + return COMPLETE_THREAD_NUM >= START_THREAD_NUM; } /** * 所有子线程是否都已经取消下载 */ public boolean isCancel() { - return CANCEL_NUM == THREAD_NUM; + return CANCEL_NUM == START_THREAD_NUM; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java b/Aria/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java index d2992980..2dc5b647 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java @@ -19,7 +19,7 @@ public class SubThreadConfig { public File TEMP_FILE; //服务器地址 public String URL; - public String CONFIG_FILE_PATH; public TASK_ENTITY TASK_ENTITY; public boolean SUPPORT_BP = true; + public ThreadRecord THREAD_RECORD; } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java b/Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java index 51d63ec0..ea928de3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java @@ -15,18 +15,62 @@ */ package com.arialyy.aria.core.common; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.orm.ActionPolicy; import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Ignore; +import com.arialyy.aria.orm.annotation.NoNull; +import com.arialyy.aria.orm.annotation.Primary; +import java.util.List; /** * Created by laoyuyu on 2018/3/21. - * 任务上传或下载的记录 + * 任务上传或下载的任务记录 */ -class TaskRecord extends DbEntity { +public class TaskRecord extends DbEntity { + + @Ignore + public List threadRecords; + /** * 任务线程数 */ public int threadNum; - //public int re + /** + * 任务文件路径 + */ + @Primary + public String filePath; + + /** + * 文件长度 + */ + public long fileLength; + + /** + * 任务文件名 + */ + @NoNull + public String fileName; + + /** + * 是否是任务组的子任务记录 + * {@code true}是 + */ + public boolean isGroupRecord = false; + /** + * 下载任务组名 + */ + @Foreign(parent = DownloadGroupEntity.class, column = "groupName", onUpdate = ActionPolicy.CASCADE, onDelete = ActionPolicy.CASCADE) + public String dGroupName; + + /** + * 上传组任务名,暂时没有用 + */ + @Ignore + @Deprecated + public String uGroupName; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java b/Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java new file mode 100644 index 00000000..29632e51 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java @@ -0,0 +1,50 @@ +/* + * 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.core.common; + +import com.arialyy.aria.orm.ActionPolicy; +import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.orm.annotation.Foreign; + +/** + * Created by laoyuyu on 2018/5/8. + * 任务的线程记录 + */ +public class ThreadRecord extends DbEntity { + @Foreign(parent = TaskRecord.class, column = "filePath", onUpdate = ActionPolicy.CASCADE, onDelete = ActionPolicy.CASCADE) + public String key; + + /** + * 开始位置 + */ + public long startLocation; + + /** + * 结束位置 + */ + public long endLocation; + + /** + * 线程是否完成 + * {@code true}完成,{@code false}未完成 + */ + public boolean isComplete = false; + + /** + * 线程id + */ + public int threadId = -1; +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java index 800a5974..96f52012 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java @@ -29,12 +29,7 @@ public class DownloadGroupEntity extends AbsGroupEntity { @Ignore private List subEntities; - /** - * 任务组下载文件的文件夹地址 - * - * @see DownloadGroupTarget#setDirPath(String) - */ - private String dirPath; + /** * 子任务实体列表 @@ -47,14 +42,6 @@ public class DownloadGroupEntity extends AbsGroupEntity { this.subEntities = subTasks; } - public String getDirPath() { - return dirPath; - } - - public void setDirPath(String dirPath) { - this.dirPath = dirPath; - } - public void setGroupName(String key) { this.groupName = key; } @@ -73,13 +60,11 @@ public class DownloadGroupEntity extends AbsGroupEntity { @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeTypedList(this.subEntities); - dest.writeString(this.dirPath); } protected DownloadGroupEntity(Parcel in) { super(in); this.subEntities = in.createTypedArrayList(DownloadEntity.CREATOR); - this.dirPath = in.readString(); } public static final Creator CREATOR = new Creator() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java index 8880a09d..45127ccc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java @@ -234,7 +234,7 @@ public abstract class AbsGroupUtil implements IUtil { } } clearState(); - CommonUtil.delDownloadGroupTaskConfig(mGTEntity.isRemoveFile(), mGTEntity.getEntity()); + CommonUtil.delGroupTaskRecord(mGTEntity.isRemoveFile(), mGTEntity.getEntity()); mListener.onCancel(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java index de1780e1..1546b44f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java @@ -45,32 +45,17 @@ class Downloader extends AbsFileer { } @Override protected int setNewTaskThreadNum() { - return mEntity.getFileSize() <= SUB_LEN || mTaskEntity.getRequestType() == AbsTaskEntity.D_FTP_DIR - ? 1 - : AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum(); + return + mEntity.getFileSize() <= SUB_LEN || mTaskEntity.getRequestType() == AbsTaskEntity.D_FTP_DIR + ? 1 + : AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum(); } @Override protected void checkTask() { - mConfigFile = new File(CommonUtil.getFileConfigPath(true, mEntity.getFileName())); + super.checkTask(); mTempFile = new File(mEntity.getDownloadPath()); - if (!mTaskEntity.isSupportBP()) { - isNewTask = true; - return; - } - if (mTaskEntity.isNewTask()) { - isNewTask = true; - return; - } - if (!mConfigFile.exists()) { //记录文件被删除,则重新下载 - isNewTask = true; - CommonUtil.createFile(mConfigFile.getPath()); - } else if (!mTempFile.exists()) { - isNewTask = true; - } else if (DbEntity.findFirst(DownloadEntity.class, "url=?", mEntity.getUrl()) == null) { - isNewTask = true; - } else { - isNewTask = checkConfigFile(); - } + mTaskEntity.setNewTask(!mTempFile.exists() + || DbEntity.findFirst(DownloadEntity.class, "url=?", mEntity.getUrl()) == null); } @Override protected boolean handleNewTask() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java index 4f0e8973..954d6be3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java @@ -23,7 +23,6 @@ import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; -import java.io.File; import java.io.IOException; import java.io.InputStream; import org.apache.commons.net.ftp.FTPClient; @@ -107,10 +106,7 @@ class FtpThreadTask extends AbsFtpThreadTask writeConfig(true, 1); STATE.COMPLETE_THREAD_NUM++; if (STATE.isComplete()) { - File configFile = new File(mConfigFPath); - if (configFile.exists()) { - configFile.delete(); - } + STATE.TASK_RECORD.deleteData(); STATE.isRunning = false; mListener.onComplete(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java index 63634d0f..3db812ad 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java @@ -148,6 +148,7 @@ class HttpFileInfoThread implements Runnable { return; } mEntity.setFileSize(len); + mTaskEntity.setNewTask(true); mTaskEntity.setSupportBP(false); end = true; } else if (code == HttpURLConnection.HTTP_NOT_FOUND) { @@ -180,7 +181,7 @@ class HttpFileInfoThread implements Runnable { File oldFile = new File(mEntity.getDownloadPath()); String oldName = oldFile.getName(); String newPath = oldFile.getParent() + "/" + newName; - if (oldFile.exists()){ + if (oldFile.exists()) { oldFile.renameTo(new File(newPath)); } mEntity.setFileName(newName); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java index 333dfff7..8532dcbe 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java @@ -25,7 +25,6 @@ import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.CommonUtil; import java.io.BufferedInputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; @@ -155,10 +154,7 @@ final class HttpThreadTask extends AbsThreadTask urls = new ArrayList<>(); + public String getDirPath() { + return dirPath; + } + + public void setDirPath(String dirPath) { + this.dirPath = dirPath; + } + public List getUrls() { return urls; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java index c6d7c497..faa8caef 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java @@ -23,7 +23,6 @@ import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; -import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import org.apache.commons.net.ftp.FTPClient; @@ -82,10 +81,7 @@ class FtpThreadTask extends AbsFtpThreadTask { writeConfig(true, 1); STATE.COMPLETE_THREAD_NUM++; if (STATE.isComplete()) { - File configFile = new File(mConfigFPath); - if (configFile.exists()) { - configFile.delete(); - } + STATE.TASK_RECORD.deleteData(); STATE.isRunning = false; mListener.onComplete(); } @@ -114,7 +110,8 @@ class FtpThreadTask extends AbsFtpThreadTask { private void initPath() throws UnsupportedEncodingException { dir = new String(mTaskEntity.getUrlEntity().remotePath.getBytes(charSet), SERVER_CHARSET); remotePath = new String( - ("/" + mTaskEntity.getUrlEntity().remotePath + "/" + mEntity.getFileName()).getBytes(charSet), + ("/" + mTaskEntity.getUrlEntity().remotePath + "/" + mEntity.getFileName()).getBytes( + charSet), SERVER_CHARSET); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java index 48c7aa3b..db8c0cbe 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java @@ -24,7 +24,6 @@ import com.arialyy.aria.core.inf.IUploadListener; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.util.CommonUtil; import java.io.File; /** @@ -40,29 +39,11 @@ class Uploader extends AbsFileer { AriaManager.getInstance(AriaManager.APP).getUploadConfig().getUpdateInterval()); } - /** - * 检查任务是否是新任务,新任务条件: - * 1、文件不存在 - * 2、记录文件不存在 - * 3、记录文件缺失或不匹配 - * 4、数据库记录不存在 - * 5、不支持断点,则是新任务 - */ protected void checkTask() { - mConfigFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName())); - if (!mTaskEntity.isSupportBP()) { - isNewTask = true; - return; - } - if (!mConfigFile.exists()) { //记录文件被删除,则重新下载 - isNewTask = true; - CommonUtil.createFile(mConfigFile.getPath()); - } else if (DbEntity.findFirst(UploadEntity.class, "filePath=?", mEntity.getFilePath()) - == null) { - isNewTask = true; - } else { - isNewTask = checkConfigFile(); - } + super.checkTask(); + mTaskEntity.setNewTask( + DbEntity.findFirst(UploadEntity.class, "filePath=?", mEntity.getFilePath()) + == null); } @Override protected boolean handleNewTask() { diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java b/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java index 8d9fb2ac..08001178 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -16,6 +16,8 @@ package com.arialyy.aria.orm; import android.text.TextUtils; +import com.arialyy.aria.core.common.TaskRecord; +import com.arialyy.aria.core.common.ThreadRecord; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; @@ -33,7 +35,7 @@ class DBConfig { /*adb pull /mnt/sdcard/Android/data/com.arialyy.simple/files/DB/AriaLyyDb d:/db*/ static Map mapping = new HashMap<>(); static String DB_NAME; - static int VERSION = 31; + static int VERSION = 32; /** * 是否将数据库保存在Sd卡,{@code true} 是 @@ -56,5 +58,7 @@ class DBConfig { mapping.put("UploadTaskEntity", UploadTaskEntity.class); mapping.put("DownloadEntity", DownloadEntity.class); mapping.put("UploadEntity", UploadEntity.class); + mapping.put("TaskRecord", TaskRecord.class); + mapping.put("ThreadRecord", ThreadRecord.class); } } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java b/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java index 9466bef0..f6856acc 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java @@ -30,6 +30,13 @@ public abstract class DbEntity { } + ///** + // * 保存关联数据 + // */ + //public static void saveRelationData(AbsWrapper wrapper) { + // DelegateWrapper.getInstance().saveRelationData(wrapper); + //} + /** * 查询关联数据 * diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java index c23fabf6..01e9360a 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java @@ -187,6 +187,10 @@ class DelegateCommon extends AbsDelegate { } } + if (SqlUtil.isUnique(field)){ + sb.append(" UNIQUE"); + } + sb.append(","); } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java index 2b76ec6b..c2946ce1 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java @@ -51,6 +51,7 @@ class DelegateFind extends AbsDelegate { * 如果查找不到数据或实体没有被{@link Wrapper}注解,将返回null * 如果实体中没有{@link One}或{@link Many}注解,将返回null * 如果实体中有多个{@link One}或{@link Many}注解,将返回nul + * {@link One} 的注解对象必须是{@link DbEntity},{@link Many}的注解对象必须是List,并且List中的类型必须是{@link DbEntity} */ List findRelationData(SQLiteDatabase db, Class clazz, String... expression) { @@ -75,6 +76,10 @@ class DelegateFind extends AbsDelegate { ALog.w(TAG, "查询数据失败,实体中有多个@Many 注解"); return null; } + if (!field.getType().isAssignableFrom(List.class)) { + ALog.w(TAG, "查询数据失败,@Many 注解的类型不是List"); + return null; + } hasMany = true; many = field; } @@ -499,10 +504,16 @@ class DelegateFind extends AbsDelegate { /** * 通过rowId判断数据是否存在 */ - boolean itemExist(SQLiteDatabase db, Class clazz, - long rowId) { + boolean itemExist(SQLiteDatabase db, Class clazz, long rowId) { + return itemExist(db, CommonUtil.getClassName(clazz), rowId); + } + + /** + * 通过rowId判断数据是否存在 + */ + boolean itemExist(SQLiteDatabase db, String tableName, long rowId) { db = checkDb(db); - String sql = "SELECT rowid FROM " + CommonUtil.getClassName(clazz) + " WHERE rowid=" + rowId; + String sql = "SELECT rowid FROM " + tableName + " WHERE rowid=" + rowId; print(ROW_ID, sql); Cursor cursor = db.rawQuery(sql, null); boolean isExist = cursor.getCount() > 0; diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java index 4a9ca900..006598d2 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java @@ -18,7 +18,6 @@ package com.arialyy.aria.orm; import android.content.ContentValues; import android.database.sqlite.SQLiteDatabase; import android.text.TextUtils; -import android.util.Log; import com.arialyy.aria.orm.annotation.Primary; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; @@ -36,6 +35,50 @@ class DelegateUpdate extends AbsDelegate { private DelegateUpdate() { } + ///** + // * 添加或更新关联数据 + // */ + //void saveRelationData(SQLiteDatabase db, AbsWrapper wrapper) { + // Class clazz = wrapper.getClass(); + // List fields = CommonUtil.getAllFields(clazz); + // DbEntity one = null; + // Object many = null; + // try { + // for (Field field : fields) { + // if (SqlUtil.isOne(field)) { + // one = (DbEntity) field.get(wrapper); + // } else if (SqlUtil.isMany(field)) { + // many = field.get(wrapper); + // } + // } + // if (one == null) { + // ALog.w(TAG, "保存关联数据失败,@One注解的字段为null"); + // return; + // } + // if (many == null) { + // ALog.w(TAG, "保存关联数据失败,@Many注解的字段为null"); + // return; + // } + // List oneFields = CommonUtil.getAllFields(one.getClass()); + // one.save(); + // if (many.getClass() == List.class) { + // for (DbEntity sub : (List) many) { + // sub.getClass().getA + // sub.save(); + // } + // } else { + // if (DbEntity.class.isInstance(many)) { + // ((DbEntity) many).save(); + // } else { + // ALog.w(TAG, "保存关联数据失败,@Many注解的字段不是DbEntity子类"); + // return; + // } + // } + // } catch (IllegalAccessException e) { + // e.printStackTrace(); + // } + //} + /** * 删除某条数据 */ @@ -62,7 +105,6 @@ class DelegateUpdate extends AbsDelegate { db = checkDb(db); Class clazz = dbEntity.getClass(); List fields = CommonUtil.getAllFields(clazz); - //DbEntity cacheEntity = mDataCache.get(getCacheKey(dbEntity)); if (fields != null && fields.size() > 0) { ContentValues values = new ContentValues(); try { @@ -71,14 +113,6 @@ class DelegateUpdate extends AbsDelegate { if (isIgnore(dbEntity, field)) { continue; } - //if (cacheEntity != null - // && field.get(dbEntity).equals(field.get(cacheEntity)) - // && !field.getName().equals("state")) { //在LruCache中 state字段总是不能重新赋值... - // Log.d(TAG, field.get(dbEntity) + ""); - // Log.d(TAG, field.get(cacheEntity) + ""); - // - // continue; - //} String value; Type type = field.getType(); if (type == Map.class && checkMap(field)) { @@ -101,7 +135,6 @@ class DelegateUpdate extends AbsDelegate { ALog.d(TAG, "没有数据更新"); } } - //mDataCache.put(getCacheKey(dbEntity), dbEntity); close(db); } @@ -137,7 +170,6 @@ class DelegateUpdate extends AbsDelegate { } catch (IllegalAccessException e) { e.printStackTrace(); } - //print(INSERT_DATA, ); dbEntity.rowID = db.insert(CommonUtil.getClassName(dbEntity), null, values); } close(db); diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java index 6a817885..1c909eeb 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java @@ -56,6 +56,13 @@ public class DelegateWrapper { return INSTANCE; } + ///** + // * 保存关联数据 + // */ + //void saveRelationData(AbsWrapper wrapper){ + // + //} + /** * 查询关联表数据 * @@ -125,6 +132,13 @@ public class DelegateWrapper { return mDManager.getDelegate(DelegateFind.class).itemExist(mDb, clazz, rowId); } + /** + * 通过rowId判断数据是否存在 + */ + boolean isExist(String tableName, long rowId) { + return mDManager.getDelegate(DelegateFind.class).itemExist(mDb, tableName, rowId); + } + /** * 插入数据 */ diff --git a/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java b/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java index 525f90cd..857d470a 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java @@ -23,6 +23,7 @@ import com.arialyy.aria.orm.annotation.Many; import com.arialyy.aria.orm.annotation.NoNull; 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.CommonUtil; import java.lang.reflect.Field; @@ -247,6 +248,16 @@ final class SqlUtil { return nn != null; } + /** + * 判断是否是Unique + * + * @return {@code true}为Unique + */ + static boolean isUnique(Field field) { + Unique nn = field.getAnnotation(Unique.class); + return nn != null; + } + private static Object checkData(String type, String data) { if (type.equalsIgnoreCase("java.lang.String")) { return data; diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Unique.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Unique.java new file mode 100644 index 00000000..a0316388 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Unique.java @@ -0,0 +1,29 @@ +/* + * 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by laoyuyu on 2018/5/8. + * 数据库 UNIQUE 约束 + */ +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) +public @interface Unique { +} diff --git a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java index 96f06349..7c8050e1 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -30,11 +30,15 @@ import com.arialyy.aria.core.command.group.AbsGroupCmd; import com.arialyy.aria.core.command.group.GroupCmdFactory; import com.arialyy.aria.core.command.normal.AbsNormalCmd; import com.arialyy.aria.core.command.normal.NormalCmdFactory; +import com.arialyy.aria.core.common.TaskRecord; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.inf.AbsGroupEntity; import com.arialyy.aria.core.inf.AbsGroupTaskEntity; +import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.orm.DbEntity; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; @@ -65,6 +69,7 @@ import java.util.regex.Pattern; /** * Created by lyy on 2016/1/22. + * 通用工具 */ public class CommonUtil { private static final String TAG = "CommonUtil"; @@ -82,7 +87,7 @@ public class CommonUtil { String reg = Regular.REG_WINLOD_REPLACE; Pattern p = Pattern.compile(reg); Matcher m = p.matcher(text); - if (m.find()){ + if (m.find()) { String s = m.group(); s = s.substring(9, s.length() - 2); return s; @@ -356,20 +361,27 @@ public class CommonUtil { } /** - * 删除下载任务组的配置 + * 删除任务组记录 * * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经删除完成的文件 * {@code false}如果任务已经完成,只删除任务数据库记录 */ - public static void delDownloadGroupTaskConfig(boolean removeFile, - DownloadGroupEntity groupEntity) { + public static void delGroupTaskRecord(boolean removeFile, AbsGroupEntity groupEntity) { if (groupEntity == null) { ALog.e(TAG, "删除下载任务组记录失败,任务组实体为null"); return; } + List records = DbEntity.findDatas(TaskRecord.class, + groupEntity instanceof DownloadGroupEntity ? "dGroupName=?" : "uGroupName=?", + groupEntity.getGroupName()); - for (DownloadEntity taskEntity : groupEntity.getSubEntities()) { - delDownloadTaskConfig(removeFile, taskEntity); + if (records == null || records.isEmpty()) { + ALog.w(TAG, "组任务记录删除失败,记录为null"); + return; + } + + for (TaskRecord tr : records) { + tr.deleteData(); } File dir = new File(groupEntity.getDirPath()); @@ -386,38 +398,23 @@ public class CommonUtil { } /** - * 删除上传任务的配置 - * - * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经删除完成的文件 - * {@code false}如果任务已经完成,只删除任务数据库记录 - */ - public static void delUploadTaskConfig(boolean removeFile, UploadEntity uEntity) { - if (uEntity == null) { - return; - } - File file = new File(uEntity.getFilePath()); - if (removeFile) { - if (file.exists()) { - file.delete(); - } - } - File config = new File(getFileConfigPath(false, uEntity.getFileName())); - if (config.exists()) { - config.delete(); - } - //下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除 - uEntity.deleteData(); - } - - /** - * 删除下载任务的配置 + * 删除任务记录 * * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经下载完成的文件 * {@code false}如果任务已经完成,只删除任务数据库记录 */ - public static void delDownloadTaskConfig(boolean removeFile, DownloadEntity dEntity) { + public static void delTaskRecord(TaskRecord record, boolean removeFile, + AbsNormalEntity dEntity) { if (dEntity == null) return; - File file = new File(dEntity.getDownloadPath()); + File file; + if (dEntity instanceof DownloadEntity) { + file = new File(((DownloadEntity) dEntity).getDownloadPath()); + } else if (dEntity instanceof UploadEntity) { + file = new File(((UploadEntity) dEntity).getFilePath()); + } else { + ALog.w(TAG, "删除记录失败,未知类型"); + return; + } if (removeFile) { if (file.exists()) { file.delete(); @@ -430,9 +427,8 @@ public class CommonUtil { } } - File config = new File(getFileConfigPath(true, dEntity.getFileName())); - if (config.exists()) { - config.delete(); + if (record != null) { + record.deleteData(); } //下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除 dEntity.deleteData();