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 8f07090d..d4cdf491 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 @@ -141,10 +141,11 @@ public abstract class AbsFileer threads = new HashSet<>(); + // 如果是新任务,检查下历史记录,如果有遗留的记录,删除并删除分块文件 + if (mTaskWrapper.isNewTask()) { + CommonUtil.delTaskRecord(getFilePath(), 1, true); + } mRecord.fileLength = fileLength; if (mTaskWrapper.isNewTask() && !handleNewTask()) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java index 1e552ae2..b1f48642 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java @@ -316,7 +316,7 @@ public abstract class AbsThreadTask mEntity.getFileSize()) { + if (STATE.CURRENT_LOCATION > mEntity.getFileSize() && !mTaskWrapper.asHttp().isChunked()) { String errorMsg = String.format("下载失败,下载长度超出文件真实长度;currentLocation=%s, fileSize=%s", STATE.CURRENT_LOCATION, diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DNormalDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/DNormalDelegate.java index 5b24c8a8..6f5e3d38 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DNormalDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DNormalDelegate.java @@ -145,6 +145,10 @@ class DNormalDelegate implements INormalTarget { File newFile = new File(filePath); mEntity.setDownloadPath(filePath); mEntity.setFileName(newFile.getName()); + // 如过使用Content-Disposition中的文件名,将不会执行重命名工作 + if (mTarget.getTaskWrapper().asHttp().isUseServerFileName()) { + return true; + } if (oldFile.exists()) { // 处理普通任务的重命名 CommonUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath()); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 3ccbeba3..aa04eff8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -1,475 +1,458 @@ -/* - * 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.download; - -import android.support.annotation.CheckResult; -import android.support.annotation.NonNull; -import android.text.TextUtils; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.command.ICmd; -import com.arialyy.aria.core.command.normal.CancelAllCmd; -import com.arialyy.aria.core.command.normal.NormalCmdFactory; -import com.arialyy.aria.core.common.ProxyHelper; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.AbsReceiver; -import com.arialyy.aria.core.inf.ReceiverType; -import com.arialyy.aria.core.manager.TaskWrapperManager; -import com.arialyy.aria.core.scheduler.DownloadGroupSchedulers; -import com.arialyy.aria.core.scheduler.DownloadSchedulers; -import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CheckUtil; -import com.arialyy.aria.util.CommonUtil; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -/** - * Created by lyy on 2016/12/5. - * 下载功能接收器 - */ -public class DownloadReceiver extends AbsReceiver { - private final String TAG = "DownloadReceiver"; - - /** - * 设置最大下载速度,单位:kb - * - * @param maxSpeed 为0表示不限速 - * @deprecated {@code Aria.get(Context).getDownloadConfig().setMaxSpeed(int)} - */ - @Deprecated - public DownloadReceiver setMaxSpeed(int maxSpeed) { - AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setMaxSpeed(maxSpeed); - return this; - } - - /** - * 使用下载实体执行下载操作 - * - * @param entity 下载实体 - */ - @CheckResult - public DownloadTarget load(DownloadEntity entity) { - CheckUtil.checkUrlInvalidThrow(entity.getUrl()); - return new DownloadTarget(entity, targetName); - } - - /** - * 加载Http、https单任务下载地址 - * - * @param url 下载地址 - */ - @CheckResult - public DownloadTarget load(@NonNull String url) { - CheckUtil.checkUrlInvalidThrow(url); - return new DownloadTarget(url, targetName); - } - - /** - * 加载下载地址,如果任务组的中的下载地址改变了,则任务从新的一个任务组 - * - * @param urls 任务组子任务下载地址列表 - * @deprecated {@link #loadGroup(DownloadGroupEntity)} - */ - @Deprecated - @CheckResult - public DownloadGroupTarget load(List urls) { - return loadGroup(urls); - } - - /** - * 加载下载地址,如果任务组的中的下载地址改变了,则任务从新的一个任务组 - */ - @CheckResult - public DownloadGroupTarget loadGroup(List urls) { - CheckUtil.checkDownloadUrls(urls); - return new DownloadGroupTarget(urls, targetName); - } - - /** - * 使用下载实体执行FTP下载操作 - * - * @param entity 下载实体 - */ - @CheckResult - public FtpDownloadTarget loadFtp(DownloadEntity entity) { - CheckUtil.checkUrlInvalidThrow(entity.getUrl()); - return new FtpDownloadTarget(entity, targetName); - } - - /** - * 加载ftp单任务下载地址 - */ - @CheckResult - public FtpDownloadTarget loadFtp(@NonNull String url) { - CheckUtil.checkUrlInvalidThrow(url); - return new FtpDownloadTarget(url, targetName); - } - - /** - * 使用任务组实体执行任务组的实体执行任务组的下载操作,后续版本会删除该api - * - * @param groupEntity 如果加载的任务实体没有子项的下载地址, - * 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址 - * @deprecated 请使用 {@link #loadGroup(DownloadGroupEntity)} - */ - @Deprecated - @CheckResult - public DownloadGroupTarget load(DownloadGroupEntity groupEntity) { - CheckUtil.checkDownloadUrls(groupEntity.getUrls()); - return loadGroup(groupEntity); - } - - /** - * 使用任务组实体执行任务组的实体执行任务组的下载操作 - * - * @param groupEntity 如果加载的任务实体没有子项的下载地址, - * 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址 - */ - @CheckResult - public DownloadGroupTarget loadGroup(DownloadGroupEntity groupEntity) { - CheckUtil.checkDownloadUrls(groupEntity.getUrls()); - return new DownloadGroupTarget(groupEntity, targetName); - } - - /** - * 加载ftp文件夹下载地址 - */ - @CheckResult - public FtpDirDownloadTarget loadFtpDir(@NonNull String dirUrl) { - CheckUtil.checkUrlInvalidThrow(dirUrl); - return new FtpDirDownloadTarget(dirUrl, targetName); - } - - /** - * 将当前类注册到Aria - */ - public void register() { - if (TextUtils.isEmpty(targetName)) { - ALog.e(TAG, "download register target null"); - return; - } - Object obj = OBJ_MAP.get(getKey()); - if (obj == null) { - ALog.e(TAG, String.format("【%s】观察者为空", targetName)); - return; - } - Set set = ProxyHelper.getInstance().checkProxyType(obj.getClass()); - if (set != null && !set.isEmpty()) { - for (Integer type : set) { - if (type == ProxyHelper.PROXY_TYPE_DOWNLOAD) { - DownloadSchedulers.getInstance().register(obj); - } else if (type == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) { - DownloadGroupSchedulers.getInstance().register(obj); - } - } - } else { - ALog.w(TAG, "没有Aria的注解方法"); - } - } - - /** - * 取消注册,如果是Activity或fragment,Aria会界面销毁时自动调用该方法,不需要你手动调用。 - * 注意事项: - * 1、如果在activity中一定要调用该方法,那么请在{@code onDestroy()}中调用 - * 2、不要在activity的{@code onStop()}中调用改方法 - * 3、如果是Dialog或popupwindow,需要你在撤销界面时调用该方法 - * 4、如果你是在Module(非android组件类)中注册了Aria,那么你也需要在Module类中调用该方法,而不是在组件类中 - * 调用销毁,详情见 - * - * @see module类中销毁 - */ - @Override public void unRegister() { - if (needRmListener) { - unRegisterListener(); - } - AriaManager.getInstance(AriaManager.APP).removeReceiver(OBJ_MAP.get(getKey())); - } - - @Override public String getType() { - return ReceiverType.DOWNLOAD; - } - - @Override protected void unRegisterListener() { - if (TextUtils.isEmpty(targetName)) { - ALog.e(TAG, "download unRegisterListener target null"); - return; - } - Object obj = OBJ_MAP.get(getKey()); - if (obj == null) { - ALog.e(TAG, String.format("【%s】观察者为空", targetName)); - return; - } - Set set = ProxyHelper.getInstance().mProxyCache.get(obj.getClass().getName()); - if (set != null) { - for (Integer integer : set) { - if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD) { - DownloadSchedulers.getInstance().unRegister(obj); - } else if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) { - DownloadGroupSchedulers.getInstance().unRegister(obj); - } - } - } - } - - /** - * 通过下载链接获取下载实体 - * - * @return 如果url错误或查找不到数据,则返回null - */ - public DownloadEntity getDownloadEntity(String downloadUrl) { - if (!CheckUtil.checkUrl(downloadUrl)) { - return null; - } - return DbEntity.findFirst(DownloadEntity.class, "url=? and isGroupChild='false'", downloadUrl); - } - - /** - * 通过下载地址和文件保存路径获取下载任务实体 - * - * @param downloadUrl 下载地址 - * @return 如果url错误或查找不到数据,则返回null - */ - public DTaskWrapper getDownloadTask(String downloadUrl) { - if (!CheckUtil.checkUrl(downloadUrl)) { - return null; - } - if (!taskExists(downloadUrl)) { - return null; - } - return TaskWrapperManager.getInstance().getHttpTaskWrapper(DTaskWrapper.class, downloadUrl); - } - - /** - * 通过下载链接获取保存在数据库的下载任务组实体 - * - * @param urls 任务组子任务下载地址列表 - * @return 返回对应的任务组实体;如果查找不到对应的数据或子任务列表为null,返回null - */ - public DGTaskWrapper getGroupTask(List urls) { - if (urls == null || urls.isEmpty()) { - ALog.e(TAG, "获取任务组实体失败:任务组子任务下载地址列表为null"); - return null; - } - if (!taskExists(urls)) { - return null; - } - return TaskWrapperManager.getInstance().getDGTaskWrapper(DGTaskWrapper.class, urls); - } - - /** - * 获取FTP文件夹下载任务实体 - * - * @param dirUrl FTP文件夹本地下载路径 - * @return 返回对应的任务组实体;如果查找不到对应的数据或路径为null,返回null - */ - public DGTaskWrapper getFtpDirTask(String dirUrl) { - if (TextUtils.isEmpty(dirUrl)) { - ALog.e(TAG, "获取FTP文件夹实体失败:下载路径为null"); - return null; - } - boolean b = - DownloadGroupEntity.findFirst(DownloadGroupEntity.class, "groupHash=?", dirUrl) != null; - if (!b) { - return null; - } - return TaskWrapperManager.getInstance().getFtpTaskWrapper(DGTaskWrapper.class, dirUrl); - } - - /** - * 下载任务是否存在 - * - * @return {@code true}存在,{@code false} 不存在 - */ - public boolean taskExists(String downloadUrl) { - return DbEntity.checkDataExist(DownloadEntity.class, "url=?", downloadUrl); - } - - /** - * 判断任务组是否存在 - * - * @return {@code true} 存在;{@code false} 不存在 - */ - public boolean taskExists(List urls) { - if (urls == null || urls.isEmpty()) { - return false; - } - String groupHash = CommonUtil.getMd5Code(urls); - return DbEntity.checkDataExist(DownloadGroupEntity.class, "groupHash=?", groupHash); - } - - /** - * 获取所有普通下载任务 - * 获取未完成的普通任务列表{@link #getAllNotCompleteTask()} - * 获取已经完成的普通任务列表{@link #getAllCompleteTask()} - */ - public List getTaskList() { - return DbEntity.findDatas(DownloadEntity.class, "isGroupChild=? and downloadPath!=''", - "false"); - } - - /** - * 分页获取所有普通下载任务 - * 获取未完成的普通任务列表{@link #getAllNotCompleteTask()} - * 获取已经完成的普通任务列表{@link #getAllCompleteTask()} - * - * @param page 当前页,不能小于1 - * @param num 每页数量,不能小于1 - * @return 如果页数大于总页数,返回null - */ - public List getTaskList(int page, int num) { - CheckUtil.checkPageParams(page, num); - return DbEntity.findDatas(DownloadEntity.class, page, num, - "isGroupChild=? and downloadPath!=''", "false"); - } - - /** - * 获取所有未完成的普通下载任务 - */ - public List getAllNotCompleteTask() { - return DbEntity.findDatas(DownloadEntity.class, - "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "false"); - } - - /** - * 分页获取所有未完成的普通下载任务 - * - * @param page 当前页,不能小于1 - * @param num 每页数量,不能小于1 - * @return 如果页数大于总页数,返回null - */ - public List getAllNotCompleteTask(int page, int num) { - CheckUtil.checkPageParams(page, num); - return DbEntity.findDatas(DownloadEntity.class, page, num, - "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "false"); - } - - /** - * 获取所有已经完成的普通任务 - */ - public List getAllCompleteTask() { - return DbEntity.findDatas(DownloadEntity.class, - "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "true"); - } - - /** - * 分页获取所有已经完成的普通任务 - * - * @param page 当前页,不能小于1 - * @param num 每页数量,不能小于1 - * @return 如果页数大于总页数,返回null - */ - public List getAllCompleteTask(int page, int num) { - CheckUtil.checkPageParams(page, num); - return DbEntity.findDatas(DownloadEntity.class, - "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "true"); - } - - /** - * 获取任务组列表 - * - * @return 如果没有任务组列表,则返回null - */ - public List getGroupTaskList() { - List wrappers = DbEntity.findRelationData(DGEntityWrapper.class); - if (wrappers == null || wrappers.isEmpty()) { - return null; - } - List entities = new ArrayList<>(); - for (DGEntityWrapper wrapper : wrappers) { - entities.add(wrapper.groupEntity); - } - return entities; - } - - /** - * 分页获取祝贺任务列表 - * - * @param page 当前页,不能小于1 - * @param num 每页数量,不能小于1 - * @return 如果没有任务组列表,则返回null - */ - public List getGroupTaskList(int page, int num) { - List wrappers = DbEntity.findRelationData(DGEntityWrapper.class, page, num); - if (wrappers == null || wrappers.isEmpty()) { - return null; - } - List entities = new ArrayList<>(); - for (DGEntityWrapper wrapper : wrappers) { - entities.add(wrapper.groupEntity); - } - return entities; - } - - /** - * 获取普通任务和任务组的任务列表 - */ - public List getTotalTaskList() { - List list = new ArrayList<>(); - List simpleTask = getTaskList(); - List groupTask = getGroupTaskList(); - if (simpleTask != null && !simpleTask.isEmpty()) { - list.addAll(simpleTask); - } - if (groupTask != null && !groupTask.isEmpty()) { - list.addAll(groupTask); - } - return list; - } - - /** - * 停止所有正在下载的任务,并清空等待队列。 - */ - public void stopAllTask() { - AriaManager.getInstance(AriaManager.APP) - .setCmd(NormalCmdFactory.getInstance() - .createCmd(new DTaskWrapper(null), NormalCmdFactory.TASK_STOP_ALL, - ICmd.TASK_TYPE_DOWNLOAD)) - .exe(); - } - - /** - * 恢复所有正在下载的任务 - * 1.如果执行队列没有满,则开始下载任务,直到执行队列满 - * 2.如果队列执行队列已经满了,则将所有任务添加到等待队列中 - */ - public void resumeAllTask() { - AriaManager.getInstance(AriaManager.APP) - .setCmd(NormalCmdFactory.getInstance() - .createCmd(new DTaskWrapper(null), NormalCmdFactory.TASK_RESUME_ALL, - ICmd.TASK_TYPE_DOWNLOAD)) - .exe(); - } - - /** - * 删除所有任务 - * - * @param removeFile {@code true} 删除已经下载完成的任务,不仅删除下载记录,还会删除已经下载完成的文件,{@code false} - * 如果文件已经下载完成,只删除下载记录 - */ - public void removeAllTask(boolean removeFile) { - final AriaManager ariaManager = AriaManager.getInstance(AriaManager.APP); - CancelAllCmd cancelCmd = - (CancelAllCmd) CommonUtil.createNormalCmd(new DTaskWrapper(null), - NormalCmdFactory.TASK_CANCEL_ALL, ICmd.TASK_TYPE_DOWNLOAD); - cancelCmd.removeFile = removeFile; - ariaManager.setCmd(cancelCmd).exe(); - Set keys = ariaManager.getReceiver().keySet(); - for (String key : keys) { - ariaManager.getReceiver().remove(key); - } - } +/* + * 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.download; + +import android.support.annotation.CheckResult; +import android.support.annotation.NonNull; +import android.text.TextUtils; +import com.arialyy.annotations.DownloadGroup; +import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.command.ICmd; +import com.arialyy.aria.core.command.normal.CancelAllCmd; +import com.arialyy.aria.core.command.normal.NormalCmdFactory; +import com.arialyy.aria.core.common.ProxyHelper; +import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.inf.AbsReceiver; +import com.arialyy.aria.core.inf.ReceiverType; +import com.arialyy.aria.core.manager.TaskWrapperManager; +import com.arialyy.aria.core.scheduler.DownloadGroupSchedulers; +import com.arialyy.aria.core.scheduler.DownloadSchedulers; +import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.CommonUtil; +import com.arialyy.aria.util.DbDataHelper; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Created by lyy on 2016/12/5. + * 下载功能接收器 + */ +public class DownloadReceiver extends AbsReceiver { + private final String TAG = "DownloadReceiver"; + + /** + * 设置最大下载速度,单位:kb + * + * @param maxSpeed 为0表示不限速 + * @deprecated {@code Aria.get(Context).getDownloadConfig().setMaxSpeed(int)} + */ + @Deprecated + public DownloadReceiver setMaxSpeed(int maxSpeed) { + AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setMaxSpeed(maxSpeed); + return this; + } + + /** + * 使用下载实体执行下载操作 + * + * @param entity 下载实体 + */ + @CheckResult + public DownloadTarget load(DownloadEntity entity) { + CheckUtil.checkUrlInvalidThrow(entity.getUrl()); + return new DownloadTarget(entity, targetName); + } + + /** + * 加载Http、https单任务下载地址 + * + * @param url 下载地址 + */ + @CheckResult + public DownloadTarget load(@NonNull String url) { + CheckUtil.checkUrlInvalidThrow(url); + return new DownloadTarget(url, targetName); + } + + /** + * 加载下载地址,如果任务组的中的下载地址改变了,则任务从新的一个任务组 + * + * @param urls 任务组子任务下载地址列表 + * @deprecated {@link #loadGroup(DownloadGroupEntity)} + */ + @Deprecated + @CheckResult + public DownloadGroupTarget load(List urls) { + return loadGroup(urls); + } + + /** + * 加载下载地址,如果任务组的中的下载地址改变了,则任务从新的一个任务组 + */ + @CheckResult + public DownloadGroupTarget loadGroup(List urls) { + CheckUtil.checkDownloadUrls(urls); + return new DownloadGroupTarget(urls, targetName); + } + + /** + * 使用下载实体执行FTP下载操作 + * + * @param entity 下载实体 + */ + @CheckResult + public FtpDownloadTarget loadFtp(DownloadEntity entity) { + CheckUtil.checkUrlInvalidThrow(entity.getUrl()); + return new FtpDownloadTarget(entity, targetName); + } + + /** + * 加载ftp单任务下载地址 + */ + @CheckResult + public FtpDownloadTarget loadFtp(@NonNull String url) { + CheckUtil.checkUrlInvalidThrow(url); + return new FtpDownloadTarget(url, targetName); + } + + /** + * 使用任务组实体执行任务组的实体执行任务组的下载操作,后续版本会删除该api + * + * @param groupEntity 如果加载的任务实体没有子项的下载地址, + * 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址 + * @deprecated 请使用 {@link #loadGroup(DownloadGroupEntity)} + */ + @Deprecated + @CheckResult + public DownloadGroupTarget load(DownloadGroupEntity groupEntity) { + CheckUtil.checkDownloadUrls(groupEntity.getUrls()); + return loadGroup(groupEntity); + } + + /** + * 使用任务组实体执行任务组的实体执行任务组的下载操作 + * + * @param groupEntity 如果加载的任务实体没有子项的下载地址, + * 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址 + */ + @CheckResult + public DownloadGroupTarget loadGroup(DownloadGroupEntity groupEntity) { + CheckUtil.checkDownloadUrls(groupEntity.getUrls()); + return new DownloadGroupTarget(groupEntity, targetName); + } + + /** + * 加载ftp文件夹下载地址 + */ + @CheckResult + public FtpDirDownloadTarget loadFtpDir(@NonNull String dirUrl) { + CheckUtil.checkUrlInvalidThrow(dirUrl); + return new FtpDirDownloadTarget(dirUrl, targetName); + } + + /** + * 将当前类注册到Aria + */ + public void register() { + if (TextUtils.isEmpty(targetName)) { + ALog.e(TAG, "download register target null"); + return; + } + Object obj = OBJ_MAP.get(getKey()); + if (obj == null) { + ALog.e(TAG, String.format("【%s】观察者为空", targetName)); + return; + } + Set set = ProxyHelper.getInstance().checkProxyType(obj.getClass()); + if (set != null && !set.isEmpty()) { + for (Integer type : set) { + if (type == ProxyHelper.PROXY_TYPE_DOWNLOAD) { + DownloadSchedulers.getInstance().register(obj); + } else if (type == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) { + DownloadGroupSchedulers.getInstance().register(obj); + } + } + } else { + ALog.w(TAG, "没有Aria的注解方法"); + } + } + + /** + * 取消注册,如果是Activity或fragment,Aria会界面销毁时自动调用该方法,不需要你手动调用。 + * 注意事项: + * 1、如果在activity中一定要调用该方法,那么请在{@code onDestroy()}中调用 + * 2、不要在activity的{@code onStop()}中调用改方法 + * 3、如果是Dialog或popupwindow,需要你在撤销界面时调用该方法 + * 4、如果你是在Module(非android组件类)中注册了Aria,那么你也需要在Module类中调用该方法,而不是在组件类中 + * 调用销毁,详情见 + * + * @see module类中销毁 + */ + @Override public void unRegister() { + if (needRmListener) { + unRegisterListener(); + } + AriaManager.getInstance(AriaManager.APP).removeReceiver(OBJ_MAP.get(getKey())); + } + + @Override public String getType() { + return ReceiverType.DOWNLOAD; + } + + @Override protected void unRegisterListener() { + if (TextUtils.isEmpty(targetName)) { + ALog.e(TAG, "download unRegisterListener target null"); + return; + } + Object obj = OBJ_MAP.get(getKey()); + if (obj == null) { + ALog.e(TAG, String.format("【%s】观察者为空", targetName)); + return; + } + Set set = ProxyHelper.getInstance().mProxyCache.get(obj.getClass().getName()); + if (set != null) { + for (Integer integer : set) { + if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD) { + DownloadSchedulers.getInstance().unRegister(obj); + } else if (integer == ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP) { + DownloadGroupSchedulers.getInstance().unRegister(obj); + } + } + } + } + + /** + * 通过下载链接获取下载实体 + * + * @return 如果url错误或查找不到数据,则返回null + */ + public DownloadEntity getDownloadEntity(String downloadUrl) { + CheckUtil.checkUrl(downloadUrl); + return DbEntity.findFirst(DownloadEntity.class, "url=? and isGroupChild='false'", downloadUrl); + } + + /** + * 获取组合任务实在实体 + * + * @param urls 组合任务的url + * @return 如果实体不存在,返回null + */ + public DownloadGroupEntity getDownloadGroupEntity(List urls) { + CheckUtil.checkDownloadUrls(urls); + return DbDataHelper.getDGEntity(CommonUtil.getMd5Code(urls)); + } + + /** + * 获取组合任务实在实体 + * + * @param key 组合任务的key,{@link DownloadGroupEntity#getKey()} + * @return 如果实体不存在,返回null + */ + public DownloadGroupEntity getDownloadGroupEntity(String key) { + if (TextUtils.isEmpty(key)) { + throw new IllegalArgumentException("key为空"); + } + return DbDataHelper.getDGEntity(key); + } + + /** + * 获取Ftp文件夹任务的实体 + * + * @param url ftp文件夹下载路径 + * @return 如果实体不存在,返回null + */ + public DownloadGroupEntity getFtpDirEntity(String url) { + CheckUtil.checkUrl(url); + return DbDataHelper.getDGEntity(url); + } + + /** + * 下载任务是否存在 + * + * @return {@code true}存在,{@code false} 不存在 + */ + public boolean taskExists(String downloadUrl) { + return DbEntity.checkDataExist(DownloadEntity.class, "url=?", downloadUrl); + } + + /** + * 判断任务组是否存在 + * + * @return {@code true} 存在;{@code false} 不存在 + */ + public boolean taskExists(List urls) { + if (urls == null || urls.isEmpty()) { + return false; + } + String groupHash = CommonUtil.getMd5Code(urls); + return DbEntity.checkDataExist(DownloadGroupEntity.class, "groupHash=?", groupHash); + } + + /** + * 获取所有普通下载任务 + * 获取未完成的普通任务列表{@link #getAllNotCompleteTask()} + * 获取已经完成的普通任务列表{@link #getAllCompleteTask()} + */ + public List getTaskList() { + return DbEntity.findDatas(DownloadEntity.class, "isGroupChild=? and downloadPath!=''", + "false"); + } + + /** + * 分页获取所有普通下载任务 + * 获取未完成的普通任务列表{@link #getAllNotCompleteTask()} + * 获取已经完成的普通任务列表{@link #getAllCompleteTask()} + * + * @param page 当前页,不能小于1 + * @param num 每页数量,不能小于1 + * @return 如果页数大于总页数,返回null + */ + public List getTaskList(int page, int num) { + CheckUtil.checkPageParams(page, num); + return DbEntity.findDatas(DownloadEntity.class, page, num, + "isGroupChild=? and downloadPath!=''", "false"); + } + + /** + * 获取所有未完成的普通下载任务 + */ + public List getAllNotCompleteTask() { + return DbEntity.findDatas(DownloadEntity.class, + "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "false"); + } + + /** + * 分页获取所有未完成的普通下载任务 + * + * @param page 当前页,不能小于1 + * @param num 每页数量,不能小于1 + * @return 如果页数大于总页数,返回null + */ + public List getAllNotCompleteTask(int page, int num) { + CheckUtil.checkPageParams(page, num); + return DbEntity.findDatas(DownloadEntity.class, page, num, + "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "false"); + } + + /** + * 获取所有已经完成的普通任务 + */ + public List getAllCompleteTask() { + return DbEntity.findDatas(DownloadEntity.class, + "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "true"); + } + + /** + * 分页获取所有已经完成的普通任务 + * + * @param page 当前页,不能小于1 + * @param num 每页数量,不能小于1 + * @return 如果页数大于总页数,返回null + */ + public List getAllCompleteTask(int page, int num) { + CheckUtil.checkPageParams(page, num); + return DbEntity.findDatas(DownloadEntity.class, + "isGroupChild=? and downloadPath!='' and isComplete=?", "false", "true"); + } + + /** + * 获取任务组列表 + * + * @return 如果没有任务组列表,则返回null + */ + public List getGroupTaskList() { + List wrappers = DbEntity.findRelationData(DGEntityWrapper.class); + if (wrappers == null || wrappers.isEmpty()) { + return null; + } + List entities = new ArrayList<>(); + for (DGEntityWrapper wrapper : wrappers) { + entities.add(wrapper.groupEntity); + } + return entities; + } + + /** + * 分页获取祝贺任务列表 + * + * @param page 当前页,不能小于1 + * @param num 每页数量,不能小于1 + * @return 如果没有任务组列表,则返回null + */ + public List getGroupTaskList(int page, int num) { + List wrappers = DbEntity.findRelationData(DGEntityWrapper.class, page, num); + if (wrappers == null || wrappers.isEmpty()) { + return null; + } + List entities = new ArrayList<>(); + for (DGEntityWrapper wrapper : wrappers) { + entities.add(wrapper.groupEntity); + } + return entities; + } + + /** + * 获取普通任务和任务组的任务列表 + */ + public List getTotalTaskList() { + List list = new ArrayList<>(); + List simpleTask = getTaskList(); + List groupTask = getGroupTaskList(); + if (simpleTask != null && !simpleTask.isEmpty()) { + list.addAll(simpleTask); + } + if (groupTask != null && !groupTask.isEmpty()) { + list.addAll(groupTask); + } + return list; + } + + /** + * 停止所有正在下载的任务,并清空等待队列。 + */ + public void stopAllTask() { + AriaManager.getInstance(AriaManager.APP) + .setCmd(NormalCmdFactory.getInstance() + .createCmd(new DTaskWrapper(null), NormalCmdFactory.TASK_STOP_ALL, + ICmd.TASK_TYPE_DOWNLOAD)) + .exe(); + } + + /** + * 恢复所有正在下载的任务 + * 1.如果执行队列没有满,则开始下载任务,直到执行队列满 + * 2.如果队列执行队列已经满了,则将所有任务添加到等待队列中 + */ + public void resumeAllTask() { + AriaManager.getInstance(AriaManager.APP) + .setCmd(NormalCmdFactory.getInstance() + .createCmd(new DTaskWrapper(null), NormalCmdFactory.TASK_RESUME_ALL, + ICmd.TASK_TYPE_DOWNLOAD)) + .exe(); + } + + /** + * 删除所有任务 + * + * @param removeFile {@code true} 删除已经下载完成的任务,不仅删除下载记录,还会删除已经下载完成的文件,{@code false} + * 如果文件已经下载完成,只删除下载记录 + */ + public void removeAllTask(boolean removeFile) { + final AriaManager ariaManager = AriaManager.getInstance(AriaManager.APP); + CancelAllCmd cancelCmd = + (CancelAllCmd) CommonUtil.createNormalCmd(new DTaskWrapper(null), + NormalCmdFactory.TASK_CANCEL_ALL, ICmd.TASK_TYPE_DOWNLOAD); + cancelCmd.removeFile = removeFile; + ariaManager.setCmd(cancelCmd).exe(); + Set keys = ariaManager.getReceiver().keySet(); + for (String key : keys) { + ariaManager.getReceiver().remove(key); + } + } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChunkedInputStream.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChunkedInputStream.java new file mode 100644 index 00000000..04ce9390 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChunkedInputStream.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.arialyy.aria.core.download.downloader; + +import com.arialyy.aria.util.ALog; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +// Data is expected to be a series of data chunks in the form +// The final data chunk should be a 0-length chunk which will indicate end of input. +@Deprecated +public class ChunkedInputStream extends InputStream { + private static final String TAG = "ChunkedInputStream"; + + private DataInputStream din; + private int unreadBytes = 0; // Bytes remaining in the current chunk of data + private byte[] singleByte = new byte[1]; + private boolean endOfData = false; + private String id; + + public ChunkedInputStream(InputStream in, String id) { + din = new DataInputStream(in); + this.id = id; + ALog.d(TAG, String.format("Creating chunked input for %s", id)); + } + + @Override + public void close() throws IOException { + ALog.d(TAG, String.format("%s: Closing chunked input.", id)); + din.close(); + } + + @Override + public int read() throws IOException { + int bytesRead = read(singleByte, 0, 1); + return (bytesRead == -1) ? -1 : (int) singleByte[0]; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int bytesRead = 0; + + if (len < 0) { + throw new IllegalArgumentException(id + ": Negative read length"); + } else if (len == 0) { + return 0; + } + + // If there is a current unread chunk, read from that, or else get the next chunk. + if (unreadBytes == 0) { + try { + // Find the next chunk size + unreadBytes = din.readInt(); + if (ALog.DEBUG) { + ALog.d(TAG, String.format("%s: Chunk size %s", id, unreadBytes)); + } + if (unreadBytes == 0) { + ALog.d(TAG, String.format("%s: Hit end of data", id)); + endOfData = true; + return -1; + } + } catch (IOException err) { + throw new IOException(id + ": Error while attempting to read chunk length", err); + } + } + + int bytesToRead = Math.min(len, unreadBytes); + try { + din.readFully(b, off, bytesToRead); + } catch (IOException err) { + throw new IOException( + id + ": Error while attempting to read " + bytesToRead + " bytes from current chunk", + err); + } + unreadBytes -= bytesToRead; + bytesRead += bytesToRead; + + return bytesRead; + } + + public boolean isEndOfData() { + return endOfData; + } +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java index eff27ff4..8d035fac 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java @@ -163,27 +163,27 @@ class ConnectionHelp { "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"); } if (conn.getRequestProperty("Accept") == null) { - //StringBuilder accept = new StringBuilder(); - //accept - //.append("image/gif, ") - //.append("image/jpeg, ") - //.append("image/pjpeg, ") - //.append("image/webp, ") - //.append("image/apng, ") - //.append("application/xml, ") - //.append("application/xaml+xml, ") - //.append("application/xhtml+xml, ") - //.append("application/x-shockwave-flash, ") - //.append("application/x-ms-xbap, ") - //.append("application/x-ms-application, ") - //.append("application/msword, ") - //.append("application/vnd.ms-excel, ") - //.append("application/vnd.ms-xpsdocument, ") - //.append("application/vnd.ms-powerpoint, ") - //.append("text/plain, ") - //.append("text/html, ") - //.append("*/*"); - conn.setRequestProperty("Accept", "*/*"); + StringBuilder accept = new StringBuilder(); + accept.append("image/gif, ") + .append("image/jpeg, ") + .append("image/pjpeg, ") + .append("image/webp, ") + .append("image/apng, ") + .append("application/xml, ") + .append("application/xaml+xml, ") + .append("application/xhtml+xml, ") + .append("application/x-shockwave-flash, ") + .append("application/x-ms-xbap, ") + .append("application/x-ms-application, ") + .append("application/msword, ") + .append("application/vnd.ms-excel, ") + .append("application/vnd.ms-xpsdocument, ") + .append("application/vnd.ms-powerpoint, ") + .append("application/signed-exchange, ") + .append("text/plain, ") + .append("text/html, ") + .append("*/*"); + conn.setRequestProperty("Accept", accept.toString()); } //302获取重定向地址 conn.setInstanceFollowRedirects(false); 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 51c3bd5b..c76c5578 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 @@ -24,6 +24,7 @@ import com.arialyy.aria.core.inf.AbsTaskWrapper; import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.TaskException; +import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; import java.io.File; import java.io.IOException; @@ -83,6 +84,20 @@ public class Downloader extends AbsFileer { return false; } + /** + * 如果使用"Content-Disposition"中的文件名,需要更新{@link #mTempFile}的路径 + */ + void updateTempFile() { + if (!mTempFile.getPath().equals(mEntity.getDownloadPath())) { + if (!mTempFile.exists()) { + mTempFile = new File(mEntity.getDownloadPath()); + } else { + boolean b = mTempFile.renameTo(new File(mEntity.getDownloadPath())); + ALog.d(TAG, String.format("更新tempFile文件名%s", b ? "成功" : "失败")); + } + } + } + @Override protected int getType() { return DOWNLOAD; } 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 7a7359aa..18e19f16 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 @@ -36,6 +36,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; import java.net.CookieManager; import java.net.HttpCookie; import java.net.HttpURLConnection; @@ -77,7 +78,6 @@ public class HttpFileInfoThread implements Runnable { ConnectionHelp.setConnectParam(mTaskDelegate, conn); conn.setRequestProperty("Range", "bytes=" + 0 + "-"); conn.setConnectTimeout(mConnectTimeOut); - //conn.setChunkedStreamingMode(0); conn.connect(); handleConnect(conn); } catch (IOException e) { @@ -149,20 +149,7 @@ public class HttpFileInfoThread implements Runnable { String disposition = conn.getHeaderField("Content-Disposition"); if (mTaskDelegate.isUseServerFileName() && !TextUtils.isEmpty(disposition)) { mEntity.setDisposition(CommonUtil.encryptBASE64(disposition)); - if (disposition.contains(";")) { - String[] infos = disposition.split(";"); - for (String info : infos) { - if (info.startsWith("filename") && info.contains("=")) { - String[] temp = info.split("="); - if (temp.length > 1) { - String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", ""); - mEntity.setServerFileName(newName); - renameFile(newName); - break; - } - } - } - } + handleContentDisposition(disposition); } CookieManager msCookieManager = new CookieManager(); List cookiesHeader = headers.get("Set-Cookie"); @@ -240,6 +227,40 @@ public class HttpFileInfoThread implements Runnable { } } + /** + * 处理"Content-Disposition"参数 + * Content-Disposition + * + * @throws UnsupportedEncodingException + */ + private void handleContentDisposition(String disposition) throws UnsupportedEncodingException { + if (disposition.contains(";")) { + String[] infos = disposition.split(";"); + if (infos[0].equals("attachment")) { + for (String info : infos) { + if (info.startsWith("filename") && info.contains("=")) { + String[] temp = info.split("="); + if (temp.length > 1) { + String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", ""); + mEntity.setServerFileName(newName); + renameFile(newName); + break; + } + } + } + } else if (infos[0].equals("form-data") && infos.length > 2) { + String[] temp = infos[2].split("="); + if (temp.length > 1) { + String newName = URLDecoder.decode(temp[1], "utf-8").replaceAll("\"", ""); + mEntity.setServerFileName(newName); + renameFile(newName); + } + } else { + ALog.w(TAG, "不识别的Content-Disposition参数"); + } + } + } + /** * 重命名文件 */ @@ -248,10 +269,12 @@ public class HttpFileInfoThread implements Runnable { ALog.w(TAG, "重命名失败【服务器返回的文件名为空】"); return; } + ALog.d(TAG, String.format("文件重命名为:%s", newName)); File oldFile = new File(mEntity.getDownloadPath()); String newPath = oldFile.getParent() + "/" + newName; if (oldFile.exists()) { - oldFile.renameTo(new File(newPath)); + boolean b = oldFile.renameTo(new File(newPath)); + ALog.d(TAG, String.format("文件重命名%s", b ? "成功" : "失败")); } mEntity.setFileName(newName); mEntity.setDownloadPath(newPath); @@ -269,7 +292,7 @@ public class HttpFileInfoThread implements Runnable { } return; } - if (!CheckUtil.checkUrl(newUrl)) { + if (!CheckUtil.checkUrlNotThrow(newUrl)) { failDownload(new TaskException(TAG, "下载失败,重定向url错误"), false); return; } @@ -296,6 +319,7 @@ public class HttpFileInfoThread implements Runnable { */ private boolean checkLen(long len) { if (len != mEntity.getFileSize()) { + ALog.d(TAG, "长度不一致,任务为新任务"); mTaskWrapper.setNewTask(true); } return true; 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 3945c8d7..130255f0 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 @@ -19,7 +19,6 @@ import com.arialyy.aria.core.common.AbsThreadTask; import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.StateConstance; import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.config.BaseTaskConfig; import com.arialyy.aria.core.config.DownloadConfig; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; @@ -30,6 +29,7 @@ import com.arialyy.aria.exception.TaskException; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; import java.io.BufferedInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -44,6 +44,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.util.Map; import java.util.Set; +import java.util.zip.GZIPInputStream; /** * Created by lyy on 2017/1/18. 下载线程 @@ -87,7 +88,12 @@ final class HttpThreadTask extends AbsThreadTask { ConnectionHelp.setConnectParam(taskDelegate, conn); conn.setConnectTimeout(getTaskConfig().getConnectTimeOut()); conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数 + if (taskDelegate.isChunked()) { + conn.setDoInput(true); + conn.setChunkedStreamingMode(0); + } conn.connect(); + // 传递参数 if (taskDelegate.getRequestEnum() == RequestEnum.POST) { Map params = taskDelegate.getParams(); if (params != null) { @@ -106,7 +112,9 @@ final class HttpThreadTask extends AbsThreadTask { } is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); - if (isOpenDynamicFile) { + if (taskDelegate.isChunked()) { + readChunked(is); + } else if (isOpenDynamicFile) { readDynamicFile(is); } else { //创建可设置位置的文件 @@ -114,11 +122,7 @@ final class HttpThreadTask extends AbsThreadTask { new BufferedRandomAccessFile(mConfig.TEMP_FILE, "rwd", getTaskConfig().getBuffSize()); //设置每条线程写入文件的位置 file.seek(mConfig.START_LOCATION); - if (taskDelegate.isChunked()) { - readChunk(is, file); - } else { - readNormal(is, file); - } + readNormal(is, file); handleComplete(); } } catch (MalformedURLException e) { @@ -151,6 +155,41 @@ final class HttpThreadTask extends AbsThreadTask { return this; } + /** + * 读取chunked数据 + */ + private void readChunked(InputStream is) { + FileOutputStream fos = null; + try { + fos = new FileOutputStream(mConfig.TEMP_FILE, true); + byte[] buffer = new byte[getTaskConfig().getBuffSize()]; + int len; + while (isLive() && (len = is.read(buffer)) != -1) { + if (isBreak()) { + break; + } + if (mSpeedBandUtil != null) { + mSpeedBandUtil.limitNextBytes(len); + } + fos.write(buffer, 0, len); + progress(len); + } + handleComplete(); + } catch (IOException e) { + fail(mChildCurrentLocation, new AriaIOException(TAG, + String.format("文件下载失败,savePath: %s, url: %s", mEntity.getDownloadPath(), mConfig.URL), + e)); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + /** * 动态长度文件读取方式 */ @@ -210,16 +249,6 @@ final class HttpThreadTask extends AbsThreadTask { } } - /** - * 读取chunk模式的文件流 - * - * @deprecated 暂时先这样处理,无chun - */ - private void readChunk(InputStream is, BufferedRandomAccessFile file) - throws IOException { - readNormal(is, file); - } - /** * 读取普通的文件流 */ @@ -249,6 +278,12 @@ final class HttpThreadTask extends AbsThreadTask { if (!checkBlock()) { return; } + if (mTaskWrapper.asHttp().isChunked()) { + ALog.i(TAG, "任务下载完成"); + STATE.isRunning = false; + mListener.onComplete(); + return; + } if (mChildCurrentLocation == mConfig.END_LOCATION) { //支持断点的处理 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java index 0e5e0267..e35a3801 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java @@ -126,6 +126,7 @@ public class SimpleDownloadUtil implements IUtil, Runnable { case AbsTaskWrapper.D_FTP: return new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { @Override public void onComplete(String url, CompleteInfo info) { + mDownloader.updateTempFile(); mDownloader.start(); } @@ -137,6 +138,7 @@ public class SimpleDownloadUtil implements IUtil, Runnable { case AbsTaskWrapper.D_HTTP: return new HttpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { @Override public void onComplete(String url, CompleteInfo info) { + mDownloader.updateTempFile(); mDownloader.start(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java index 7655a08a..8cec20e2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java @@ -44,14 +44,14 @@ class DGTaskWrapperFactory implements IGTEFactory urls) { - DownloadGroupEntity entity = DbDataHelper.getHttpDGEntity(groupHash, urls); + DownloadGroupEntity entity = DbDataHelper.getOrCreateHttpDGEntity(groupHash, urls); DGTaskWrapper wrapper = new DGTaskWrapper(entity); wrapper.setSubTaskWrapper(createDGSubTaskWrapper(entity)); return wrapper; } @Override public DGTaskWrapper getFTE(String ftpUrl) { - DownloadGroupEntity entity = DbDataHelper.getFtpDGEntity(ftpUrl); + DownloadGroupEntity entity = DbDataHelper.getOrCreateFtpDGEntity(ftpUrl); DGTaskWrapper fte = new DGTaskWrapper(entity); fte.asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(ftpUrl)); diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java index d19c50ba..b39ff432 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java @@ -73,24 +73,27 @@ class DTaskWrapperFactory implements INormalTEFactory mDataCache = new LruCache<>(1024); - - /** - * 打印数据库日志 - * - * @param type {@link DelegateWrapper} - */ - static void print(int type, String sql) { - if (ALog.DEBUG) { - return; - } - String str = ""; - switch (type) { - case CREATE_TABLE: - str = "创建表 >>>> "; - break; - case TABLE_EXISTS: - str = "表是否存在 >>>> "; - break; - case INSERT_DATA: - str = "插入数据 >>>> "; - break; - case MODIFY_DATA: - str = "修改数据 >>>> "; - break; - case FIND_DATA: - str = "查询数据 >>>> "; - break; - case ROW_ID: - str = "查询RowId >>> "; - break; - case RELATION: - str = "查询关联表 >>> "; - break; - case DROP_TABLE: - str = "删除表 >>> "; - break; - } - ALog.d(TAG, str.concat(sql)); - } - - String getCacheKey(DbEntity dbEntity) { - return dbEntity.getClass().getName() + "_" + dbEntity.rowID; - } /** * URL编码字符串 @@ -140,10 +85,6 @@ abstract class AbsDelegate { } } - void close(SQLiteDatabase db) { - //if (db != null && db.isOpen()) db.close(); - } - /** * 检查数据库是否关闭,已经关闭的话,打开数据库 * @@ -151,7 +92,7 @@ abstract class AbsDelegate { */ SQLiteDatabase checkDb(SQLiteDatabase db) { if (db == null || !db.isOpen()) { - db = SqlHelper.INSTANCE.getWritableDatabase(); + db = SqlHelper.getInstance().getDb(); } return db; } 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 59e3394a..d742aaf5 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -33,6 +33,7 @@ import java.util.Map; */ class DBConfig { /*adb pull /mnt/sdcard/Android/data/com.arialyy.simple/files/DB/AriaLyyDb d:/db*/ + static boolean DEBUG = false; static Map mapping = new HashMap<>(); static String DB_NAME; static int VERSION = 45; 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 adea7c9b..b1e8c319 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java @@ -42,7 +42,6 @@ class DelegateCommon extends AbsDelegate { void dropTable(SQLiteDatabase db, String tableName) { db = checkDb(db); String deleteSQL = String.format("DROP TABLE IF EXISTS %s", tableName); - print(DROP_TABLE, deleteSQL); //db.beginTransaction(); db.execSQL(deleteSQL); //db.setTransactionSuccessful(); @@ -71,7 +70,6 @@ class DelegateCommon extends AbsDelegate { return tableExists(db, CommonUtil.getClassName(clazz)); } - /** * 查找表是否存在 * @@ -85,7 +83,6 @@ class DelegateCommon extends AbsDelegate { String sql = String.format("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'", tableName); - print(TABLE_EXISTS, sql); cursor = db.rawQuery(sql, null); if (cursor != null && cursor.moveToNext()) { int count = cursor.getInt(0); @@ -97,7 +94,6 @@ class DelegateCommon extends AbsDelegate { e.printStackTrace(); } finally { closeCursor(cursor); - close(db); } return false; } @@ -119,11 +115,9 @@ class DelegateCommon extends AbsDelegate { params[i] = String.format("'%s'", encodeStr(expression[i + 1])); } sql = String.format(sql, params); - print(FIND_DATA, sql); Cursor cursor = db.rawQuery(sql, null); final boolean isExist = cursor.getCount() > 0; closeCursor(cursor); - close(db); return isExist; } @@ -223,9 +217,7 @@ class DelegateCommon extends AbsDelegate { String str = sb.toString(); str = str.substring(0, str.length() - 1) + ");"; - print(CREATE_TABLE, str); db.execSQL(str); } - close(db); } } 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 9b1ec3ab..f977bd74 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java @@ -1,640 +1,631 @@ -/* - * 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.annotation.TargetApi; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.os.Build; -import android.text.TextUtils; -import com.arialyy.aria.orm.annotation.Many; -import com.arialyy.aria.orm.annotation.One; -import com.arialyy.aria.orm.annotation.Wrapper; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CheckUtil; -import com.arialyy.aria.util.CommonUtil; -import java.lang.reflect.Field; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Created by laoyuyu on 2018/3/22. - * 查询数据 - */ -class DelegateFind extends AbsDelegate { - private final String PARENT_COLUMN_ALIAS = "p"; - private final String CHILD_COLUMN_ALIAS = "c"; - - private DelegateFind() { - } - - /** - * 获取{@link One}和{@link Many}注解的字段 - * - * @return 返回[OneField, ManyField] ,如果注解依赖错误返回null - */ - private Field[] getOneAndManyField(Class clazz) { - Field[] om = new Field[2]; - Field[] fields = clazz.getDeclaredFields(); - Field one = null, many = null; - boolean hasOne = false, hasMany = false; - for (Field field : fields) { - if (SqlUtil.isOne(field)) { - if (hasOne) { - ALog.w(TAG, "查询数据失败,实体中有多个@One 注解"); - return null; - } - hasOne = true; - one = field; - } - if (SqlUtil.isMany(field)) { - if (hasMany) { - ALog.w(TAG, "查询数据失败,实体中有多个@Many 注解"); - return null; - } - if (!field.getType().isAssignableFrom(List.class)) { - ALog.w(TAG, "查询数据失败,@Many 注解的类型不是List"); - return null; - } - hasMany = true; - many = field; - } - } - - if (one == null || many == null) { - ALog.w(TAG, "查询数据失败,实体中没有@One或@Many注解"); - return null; - } - - if (many.getType() != List.class) { - ALog.w(TAG, "查询数据失败,@Many注解的字段必须是List"); - return null; - } - om[0] = one; - om[1] = many; - return om; - } - - /** - * 查找一对多的关联数据 - * 如果查找不到数据或实体没有被{@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) { - return exeRelationSql(db, clazz, -1, -1, expression); - } - - /** - * 查找一对多的关联数据 - * 如果查找不到数据或实体没有被{@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, - int page, int num, String... expression) { - if (page < 1 || num < 1) { - return null; - } - return exeRelationSql(db, clazz, page, num, expression); - } - - /** - * 执行关联查询,如果不需要分页,page和num传-1 - * - * @param page 当前页 - * @param num 一页的数量 - */ - private List exeRelationSql(SQLiteDatabase db, Class clazz, - int page, int num, String... expression) { - db = checkDb(db); - if (SqlUtil.isWrapper(clazz)) { - Field[] om = getOneAndManyField(clazz); - if (om == null) { - return null; - } - StringBuilder sb = new StringBuilder(); - Field one = om[0], many = om[1]; - try { - Many m = many.getAnnotation(Many.class); - Class parentClazz = Class.forName(one.getType().getName()); - Class childClazz = Class.forName(CommonUtil.getListParamType(many).getName()); - final String pTableName = parentClazz.getSimpleName(); - final String cTableName = childClazz.getSimpleName(); - List pColumn = SqlUtil.getAllNotIgnoreField(parentClazz); - List cColumn = SqlUtil.getAllNotIgnoreField(childClazz); - List pColumnAlias = new ArrayList<>(); - List cColumnAlias = new ArrayList<>(); - StringBuilder pSb = new StringBuilder(); - StringBuilder cSb = new StringBuilder(); - - if (pColumn != null) { - pSb.append(pTableName.concat(".rowid AS ").concat(PARENT_COLUMN_ALIAS).concat("rowid,")); - for (Field f : pColumn) { - String temp = PARENT_COLUMN_ALIAS.concat(f.getName()); - pColumnAlias.add(temp); - pSb.append(pTableName.concat(".").concat(f.getName())) - .append(" AS ") - .append(temp) - .append(","); - } - } - - if (cColumn != null) { - pSb.append(cTableName.concat(".rowid AS ").concat(CHILD_COLUMN_ALIAS).concat("rowid,")); - for (Field f : cColumn) { - String temp = CHILD_COLUMN_ALIAS.concat(f.getName()); - cColumnAlias.add(temp); - cSb.append(cTableName.concat(".").concat(f.getName())) - .append(" AS ") - .append(temp) - .append(","); - } - } - - String pColumnAlia = pSb.toString(); - String cColumnAlia = cSb.toString(); - if (!TextUtils.isEmpty(pColumnAlia)) { - pColumnAlia = pColumnAlia.substring(0, pColumnAlia.length() - 1); - } - - if (!TextUtils.isEmpty(cColumnAlia)) { - cColumnAlia = cColumnAlia.substring(0, cColumnAlia.length() - 1); - } - - sb.append("SELECT "); - - if (!TextUtils.isEmpty(pColumnAlia)) { - sb.append(pColumnAlia).append(","); - } - if (!TextUtils.isEmpty(cColumnAlia)) { - sb.append(cColumnAlia); - } - if (TextUtils.isEmpty(pColumnAlia) && TextUtils.isEmpty(cColumnAlia)) { - sb.append(" * "); - } - - sb.append(" FROM ") - .append(pTableName) - .append(" INNER JOIN ") - .append(cTableName) - .append(" ON ") - .append(pTableName.concat(".").concat(m.parentColumn())) - .append(" = ") - .append(cTableName.concat(".").concat(m.entityColumn())); - String sql; - if (expression != null && expression.length > 0) { - CheckUtil.checkSqlExpression(expression); - sb.append(" WHERE ").append(expression[0]).append(" "); - sql = sb.toString(); - 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'", encodeStr(expression[i + 1])); - } - sql = String.format(sql, params); - } else { - sql = sb.toString(); - } - if (page != -1 && num != -1) { - sql = sql.concat(String.format(" LIMIT %s,%s", (page - 1) * num, num)); - } - - print(RELATION, sql); - Cursor cursor = db.rawQuery(sql, null); - List data = - (List) newInstanceEntity(clazz, parentClazz, childClazz, cursor, pColumn, cColumn, - pColumnAlias, cColumnAlias); - closeCursor(cursor); - close(db); - return data; - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - } else { - ALog.e(TAG, "查询数据失败,实体类没有使用@Wrapper 注解"); - return null; - } - return null; - } - - /** - * 创建关联查询的数据 - * - * @param pColumn 父表的所有字段 - * @param cColumn 字表的所有字段 - * @param pColumnAlias 关联查询父表别名 - * @param cColumnAlias 关联查询子表别名 - */ - private synchronized List newInstanceEntity( - Class clazz, Class

parent, - Class child, - Cursor cursor, - List pColumn, List cColumn, - List pColumnAlias, List cColumnAlias) { - try { - String parentPrimary = ""; //父表主键别名 - for (Field f : pColumn) { - if (SqlUtil.isPrimary(f)) { - parentPrimary = PARENT_COLUMN_ALIAS.concat(f.getName()); - break; - } - } - - List wrappers = new ArrayList<>(); - Map tempParent = new LinkedHashMap<>(); // 所有父表元素,key为父表主键的值 - Map> tempChild = new LinkedHashMap<>(); // 所有的字表元素,key为父表主键的值 - - Object old = null; - while (cursor.moveToNext()) { - //创建父实体 - Object ppValue = setPPValue(parentPrimary, cursor); - if (old == null || ppValue != old) { //当主键不同时,表示是不同的父表数据 - old = ppValue; - if (tempParent.get(old) == null) { - P pEntity = parent.newInstance(); - String pPrimaryName = ""; - for (int i = 0, len = pColumnAlias.size(); i < len; i++) { - Field pField = pColumn.get(i); - pField.setAccessible(true); - Class type = pField.getType(); - int column = cursor.getColumnIndex(pColumnAlias.get(i)); - if (column == -1) continue; - setFieldValue(type, pField, column, cursor, pEntity); - - if (SqlUtil.isPrimary(pField) && (type == int.class || type == Integer.class)) { - pPrimaryName = pField.getName(); - } - } - - //当设置了主键,而且主键的类型为integer时,查询RowID等于主键 - pEntity.rowID = cursor.getInt( - cursor.getColumnIndex( - TextUtils.isEmpty(pPrimaryName) ? PARENT_COLUMN_ALIAS.concat("rowid") - : pPrimaryName)); - - tempParent.put(ppValue, pEntity); - } - } - - // 创建子实体 - C cEntity = child.newInstance(); - String cPrimaryName = ""; - for (int i = 0, len = cColumnAlias.size(); i < len; i++) { - Field cField = cColumn.get(i); - cField.setAccessible(true); - Class type = cField.getType(); - - int column = cursor.getColumnIndex(cColumnAlias.get(i)); - if (column == -1) continue; - setFieldValue(type, cField, column, cursor, cEntity); - - if (SqlUtil.isPrimary(cField) && (type == int.class || type == Integer.class)) { - cPrimaryName = cField.getName(); - } - } - //当设置了主键,而且主键的类型为integer时,查询RowID等于主键 - cEntity.rowID = cursor.getInt( - cursor.getColumnIndex( - TextUtils.isEmpty(cPrimaryName) ? CHILD_COLUMN_ALIAS.concat("rowid") - : cPrimaryName)); - if (tempChild.get(old) == null) { - tempChild.put(old, new ArrayList()); - } - tempChild.get(old).add(cEntity); - } - - List wFields = SqlUtil.getAllNotIgnoreField(clazz); - if (wFields != null && !wFields.isEmpty()) { - Set pKeys = tempParent.keySet(); - for (Object pk : pKeys) { - T wrapper = clazz.newInstance(); - P p = tempParent.get(pk); - boolean isPSet = false, isCSet = false; - for (Field f : wFields) { - if (!isPSet && f.getAnnotation(One.class) != null) { - f.set(wrapper, p); - isPSet = true; - } - if (!isCSet && f.getAnnotation(Many.class) != null) { - f.set(wrapper, tempChild.get(pk)); - isCSet = true; - } - } - wrapper.handleConvert(); //处理下转换 - wrappers.add(wrapper); - } - } - return wrappers; - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - return null; - } - - /** - * 获取父表主键数据 - * - * @param parentPrimary 父表主键别名 - */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) private Object setPPValue(String parentPrimary, - Cursor cursor) { - Object ppValue = null; - int ppColumn = cursor.getColumnIndex(parentPrimary); //父表主键所在的列 - int type = cursor.getType(ppColumn); - switch (type) { - case Cursor.FIELD_TYPE_INTEGER: - ppValue = cursor.getLong(ppColumn); - break; - case Cursor.FIELD_TYPE_FLOAT: - ppValue = cursor.getFloat(ppColumn); - break; - case Cursor.FIELD_TYPE_STRING: - ppValue = cursor.getString(ppColumn); - break; - } - return ppValue; - } - - /** - * 条件查寻数据 - */ - List findData(SQLiteDatabase db, Class clazz, String... expression) { - db = checkDb(db); - CheckUtil.checkSqlExpression(expression); - String sql = String.format("SELECT rowid, * FROM %s WHERE %s", CommonUtil.getClassName(clazz), - expression[0]); - String[] params = new String[expression.length - 1]; - System.arraycopy(expression, 1, params, 0, params.length); - - return exeNormalDataSql(db, clazz, sql, params); - } - - /** - * 获取分页数据 - */ - List findData(SQLiteDatabase db, Class clazz, int page, int num, - String... expression) { - if (page < 1 || num < 1) { - return null; - } - db = checkDb(db); - CheckUtil.checkSqlExpression(expression); - String sql = String.format("SELECT rowid, * FROM %s WHERE %s LIMIT %s,%s", - CommonUtil.getClassName(clazz), - expression[0], (page - 1) * num, num); - - String[] params = new String[expression.length - 1]; - System.arraycopy(expression, 1, params, 0, params.length); - - return exeNormalDataSql(db, clazz, sql, params); - } - - /** - * 模糊查寻数据 - */ - List findDataByFuzzy(SQLiteDatabase db, Class clazz, - String conditions) { - db = checkDb(db); - if (TextUtils.isEmpty(conditions)) { - throw new IllegalArgumentException("sql语句表达式不能为null或\"\""); - } - if (!conditions.toUpperCase().contains("LIKE")) { - throw new IllegalArgumentException("sql语句表达式未包含LIEK"); - } - String sql = String.format("SELECT rowid, * FROM %s, WHERE %s", CommonUtil.getClassName(clazz), - conditions); - return exeNormalDataSql(db, clazz, sql, null); - } - - /** - * 分页、模糊搜索数据 - */ - List findDataByFuzzy(SQLiteDatabase db, Class clazz, - int page, int num, String conditions) { - if (page < 1 || num < 1) { - return null; - } - db = checkDb(db); - if (TextUtils.isEmpty(conditions)) { - throw new IllegalArgumentException("sql语句表达式不能为null或\"\""); - } - if (!conditions.toUpperCase().contains("LIKE")) { - throw new IllegalArgumentException("sql语句表达式未包含LIEK"); - } - String sql = String.format("SELECT rowid, * FROM %s WHERE %s LIMIT %s,%s", - CommonUtil.getClassName(clazz), conditions, (page - 1) * num, num); - return exeNormalDataSql(db, clazz, sql, null); - } - - /** - * 查找表的所有数据 - */ - List findAllData(SQLiteDatabase db, Class clazz) { - db = checkDb(db); - String sql = String.format("SELECT rowid, * FROM %s", CommonUtil.getClassName(clazz)); - return exeNormalDataSql(db, clazz, sql, null); - } - - /** - * 执行查询普通数据的sql语句,并创建对象 - * - * @param sql sql 查询语句 - * @param selectionArgs 查询参数,如何sql语句中查询条件含有'?'则该参数不能为空 - */ - private List exeNormalDataSql(SQLiteDatabase db, Class clazz, - String sql, String[] selectionArgs) { - print(FIND_DATA, sql); - String[] temp = new String[selectionArgs.length]; - int i = 0; - for (String arg : selectionArgs){ - temp[i] = encodeStr(arg); - i ++; - } - Cursor cursor = db.rawQuery(sql, temp); - List data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null; - closeCursor(cursor); - close(db); - return data; - } - - /** - * 根据数据游标创建一个具体的对象 - */ - private synchronized List newInstanceEntity(Class clazz, - Cursor cursor) { - List fields = CommonUtil.getAllFields(clazz); - List entitys = new ArrayList<>(); - if (fields != null && fields.size() > 0) { - try { - while (cursor.moveToNext()) { - T entity = clazz.newInstance(); - String primaryName = ""; - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.isIgnore(field)) { - continue; - } - - Class type = field.getType(); - if (SqlUtil.isPrimary(field) && (type == int.class || type == Integer.class)) { - primaryName = field.getName(); - } - - int column = cursor.getColumnIndex(field.getName()); - if (column == -1) continue; - setFieldValue(type, field, column, cursor, entity); - } - //当设置了主键,而且主键的类型为integer时,查询RowID等于主键 - entity.rowID = cursor.getInt( - cursor.getColumnIndex(TextUtils.isEmpty(primaryName) ? "rowid" : primaryName)); - //mDataCache.put(getCacheKey(entity), entity); - entitys.add(entity); - } - closeCursor(cursor); - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - return entitys; - } - - /** - * 设置字段的值 - * - * @throws IllegalAccessException - */ - private void setFieldValue(Class type, Field field, int column, Cursor cursor, Object entity) - throws IllegalAccessException { - if (cursor == null || cursor.isClosed()) { - ALog.e(TAG, "cursor没有初始化"); - return; - } - if (type == String.class) { - String temp = cursor.getString(column); - if (!TextUtils.isEmpty(temp)) { - field.set(entity, URLDecoder.decode(temp)); - } - } else if (type == int.class || type == Integer.class) { - field.setInt(entity, cursor.getInt(column)); - } else if (type == float.class || type == Float.class) { - field.setFloat(entity, cursor.getFloat(column)); - } else if (type == double.class || type == Double.class) { - field.setDouble(entity, cursor.getDouble(column)); - } else if (type == long.class || type == Long.class) { - field.setLong(entity, cursor.getLong(column)); - } else if (type == boolean.class || type == Boolean.class) { - String temp = cursor.getString(column); - if (TextUtils.isEmpty(temp)) { - field.setBoolean(entity, false); - } else { - field.setBoolean(entity, !temp.equalsIgnoreCase("false")); - } - } else if (type == java.util.Date.class || type == java.sql.Date.class) { - field.set(entity, new Date(URLDecoder.decode(cursor.getString(column)))); - } else if (type == byte[].class) { - field.set(entity, cursor.getBlob(column)); - } else if (type == Map.class) { - String temp = cursor.getString(column); - if (!TextUtils.isEmpty(temp)) { - field.set(entity, SqlUtil.str2Map(URLDecoder.decode(temp))); - } - } else if (type == List.class) { - String value = cursor.getString(column); - if (!TextUtils.isEmpty(value)) { - field.set(entity, SqlUtil.str2List(URLDecoder.decode(value), field)); - } - } - } - - /** - * 获取所在行Id - */ - int[] getRowId(SQLiteDatabase db, Class clazz) { - db = checkDb(db); - Cursor cursor = db.rawQuery("SELECT rowid, * FROM " + CommonUtil.getClassName(clazz), null); - int[] ids = new int[cursor.getCount()]; - int i = 0; - while (cursor.moveToNext()) { - ids[i] = cursor.getInt(cursor.getColumnIndex("rowid")); - i++; - } - cursor.close(); - close(db); - return ids; - } - - /** - * 获取行Id - */ - int getRowId(SQLiteDatabase db, Class clazz, Object[] wheres, Object[] values) { - db = checkDb(db); - if (wheres.length <= 0 || values.length <= 0) { - ALog.e(TAG, "请输入删除条件"); - return -1; - } else if (wheres.length != values.length) { - ALog.e(TAG, "groupHash 和 vaule 长度不相等"); - return -1; - } - StringBuilder sb = new StringBuilder(); - sb.append("SELECT rowid FROM ").append(CommonUtil.getClassName(clazz)).append(" WHERE "); - int i = 0; - for (Object where : wheres) { - sb.append(where).append("=").append("'").append(values[i]).append("'"); - sb.append(i >= wheres.length - 1 ? "" : ","); - i++; - } - print(ROW_ID, sb.toString()); - Cursor c = db.rawQuery(sb.toString(), null); - int id = c.getColumnIndex("rowid"); - c.close(); - close(db); - return id; - } - - /** - * 通过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 " + tableName + " WHERE rowid=" + rowId; - print(ROW_ID, sql); - Cursor cursor = db.rawQuery(sql, null); - boolean isExist = cursor.getCount() > 0; - cursor.close(); - return isExist; - } -} +/* + * 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.annotation.TargetApi; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.Build; +import android.text.TextUtils; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.CommonUtil; +import java.lang.reflect.Field; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created by laoyuyu on 2018/3/22. + * 查询数据 + */ +class DelegateFind extends AbsDelegate { + private final String PARENT_COLUMN_ALIAS = "p"; + private final String CHILD_COLUMN_ALIAS = "c"; + + private DelegateFind() { + } + + /** + * 获取{@link One}和{@link Many}注解的字段 + * + * @return 返回[OneField, ManyField] ,如果注解依赖错误返回null + */ + private Field[] getOneAndManyField(Class clazz) { + Field[] om = new Field[2]; + Field[] fields = clazz.getDeclaredFields(); + Field one = null, many = null; + boolean hasOne = false, hasMany = false; + for (Field field : fields) { + if (SqlUtil.isOne(field)) { + if (hasOne) { + ALog.w(TAG, "查询数据失败,实体中有多个@One 注解"); + return null; + } + hasOne = true; + one = field; + } + if (SqlUtil.isMany(field)) { + if (hasMany) { + ALog.w(TAG, "查询数据失败,实体中有多个@Many 注解"); + return null; + } + if (!field.getType().isAssignableFrom(List.class)) { + ALog.w(TAG, "查询数据失败,@Many 注解的类型不是List"); + return null; + } + hasMany = true; + many = field; + } + } + + if (one == null || many == null) { + ALog.w(TAG, "查询数据失败,实体中没有@One或@Many注解"); + return null; + } + + if (many.getType() != List.class) { + ALog.w(TAG, "查询数据失败,@Many注解的字段必须是List"); + return null; + } + om[0] = one; + om[1] = many; + return om; + } + + /** + * 查找一对多的关联数据 + * 如果查找不到数据或实体没有被{@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) { + return exeRelationSql(db, clazz, -1, -1, expression); + } + + /** + * 查找一对多的关联数据 + * 如果查找不到数据或实体没有被{@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, + int page, int num, String... expression) { + if (page < 1 || num < 1) { + return null; + } + return exeRelationSql(db, clazz, page, num, expression); + } + + /** + * 执行关联查询,如果不需要分页,page和num传-1 + * + * @param page 当前页 + * @param num 一页的数量 + */ + private List exeRelationSql(SQLiteDatabase db, Class clazz, + int page, int num, String... expression) { + db = checkDb(db); + if (SqlUtil.isWrapper(clazz)) { + Field[] om = getOneAndManyField(clazz); + if (om == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + Field one = om[0], many = om[1]; + try { + Many m = many.getAnnotation(Many.class); + Class parentClazz = Class.forName(one.getType().getName()); + Class childClazz = Class.forName(CommonUtil.getListParamType(many).getName()); + final String pTableName = parentClazz.getSimpleName(); + final String cTableName = childClazz.getSimpleName(); + List pColumn = SqlUtil.getAllNotIgnoreField(parentClazz); + List cColumn = SqlUtil.getAllNotIgnoreField(childClazz); + List pColumnAlias = new ArrayList<>(); + List cColumnAlias = new ArrayList<>(); + StringBuilder pSb = new StringBuilder(); + StringBuilder cSb = new StringBuilder(); + + if (pColumn != null) { + pSb.append(pTableName.concat(".rowid AS ").concat(PARENT_COLUMN_ALIAS).concat("rowid,")); + for (Field f : pColumn) { + String temp = PARENT_COLUMN_ALIAS.concat(f.getName()); + pColumnAlias.add(temp); + pSb.append(pTableName.concat(".").concat(f.getName())) + .append(" AS ") + .append(temp) + .append(","); + } + } + + if (cColumn != null) { + pSb.append(cTableName.concat(".rowid AS ").concat(CHILD_COLUMN_ALIAS).concat("rowid,")); + for (Field f : cColumn) { + String temp = CHILD_COLUMN_ALIAS.concat(f.getName()); + cColumnAlias.add(temp); + cSb.append(cTableName.concat(".").concat(f.getName())) + .append(" AS ") + .append(temp) + .append(","); + } + } + + String pColumnAlia = pSb.toString(); + String cColumnAlia = cSb.toString(); + if (!TextUtils.isEmpty(pColumnAlia)) { + pColumnAlia = pColumnAlia.substring(0, pColumnAlia.length() - 1); + } + + if (!TextUtils.isEmpty(cColumnAlia)) { + cColumnAlia = cColumnAlia.substring(0, cColumnAlia.length() - 1); + } + + sb.append("SELECT "); + + if (!TextUtils.isEmpty(pColumnAlia)) { + sb.append(pColumnAlia).append(","); + } + if (!TextUtils.isEmpty(cColumnAlia)) { + sb.append(cColumnAlia); + } + if (TextUtils.isEmpty(pColumnAlia) && TextUtils.isEmpty(cColumnAlia)) { + sb.append(" * "); + } + + sb.append(" FROM ") + .append(pTableName) + .append(" INNER JOIN ") + .append(cTableName) + .append(" ON ") + .append(pTableName.concat(".").concat(m.parentColumn())) + .append(" = ") + .append(cTableName.concat(".").concat(m.entityColumn())); + String sql; + if (expression != null && expression.length > 0) { + CheckUtil.checkSqlExpression(expression); + sb.append(" WHERE ").append(expression[0]).append(" "); + sql = sb.toString(); + 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'", encodeStr(expression[i + 1])); + } + sql = String.format(sql, params); + } else { + sql = sb.toString(); + } + if (page != -1 && num != -1) { + sql = sql.concat(String.format(" LIMIT %s,%s", (page - 1) * num, num)); + } + Cursor cursor = db.rawQuery(sql, null); + List data = + (List) newInstanceEntity(clazz, parentClazz, childClazz, cursor, pColumn, cColumn, + pColumnAlias, cColumnAlias); + closeCursor(cursor); + return data; + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } else { + ALog.e(TAG, "查询数据失败,实体类没有使用@Wrapper 注解"); + return null; + } + return null; + } + + /** + * 创建关联查询的数据 + * + * @param pColumn 父表的所有字段 + * @param cColumn 字表的所有字段 + * @param pColumnAlias 关联查询父表别名 + * @param cColumnAlias 关联查询子表别名 + */ + private synchronized List newInstanceEntity( + Class clazz, Class

parent, + Class child, + Cursor cursor, + List pColumn, List cColumn, + List pColumnAlias, List cColumnAlias) { + try { + String parentPrimary = ""; //父表主键别名 + for (Field f : pColumn) { + if (SqlUtil.isPrimary(f)) { + parentPrimary = PARENT_COLUMN_ALIAS.concat(f.getName()); + break; + } + } + + List wrappers = new ArrayList<>(); + Map tempParent = new LinkedHashMap<>(); // 所有父表元素,key为父表主键的值 + Map> tempChild = new LinkedHashMap<>(); // 所有的字表元素,key为父表主键的值 + + Object old = null; + while (cursor.moveToNext()) { + //创建父实体 + Object ppValue = setPPValue(parentPrimary, cursor); + if (old == null || ppValue != old) { //当主键不同时,表示是不同的父表数据 + old = ppValue; + if (tempParent.get(old) == null) { + P pEntity = parent.newInstance(); + String pPrimaryName = ""; + for (int i = 0, len = pColumnAlias.size(); i < len; i++) { + Field pField = pColumn.get(i); + pField.setAccessible(true); + Class type = pField.getType(); + int column = cursor.getColumnIndex(pColumnAlias.get(i)); + if (column == -1) continue; + setFieldValue(type, pField, column, cursor, pEntity); + + if (SqlUtil.isPrimary(pField) && (type == int.class || type == Integer.class)) { + pPrimaryName = pField.getName(); + } + } + + //当设置了主键,而且主键的类型为integer时,查询RowID等于主键 + pEntity.rowID = cursor.getInt( + cursor.getColumnIndex( + TextUtils.isEmpty(pPrimaryName) ? PARENT_COLUMN_ALIAS.concat("rowid") + : pPrimaryName)); + + tempParent.put(ppValue, pEntity); + } + } + + // 创建子实体 + C cEntity = child.newInstance(); + String cPrimaryName = ""; + for (int i = 0, len = cColumnAlias.size(); i < len; i++) { + Field cField = cColumn.get(i); + cField.setAccessible(true); + Class type = cField.getType(); + + int column = cursor.getColumnIndex(cColumnAlias.get(i)); + if (column == -1) continue; + setFieldValue(type, cField, column, cursor, cEntity); + + if (SqlUtil.isPrimary(cField) && (type == int.class || type == Integer.class)) { + cPrimaryName = cField.getName(); + } + } + //当设置了主键,而且主键的类型为integer时,查询RowID等于主键 + cEntity.rowID = cursor.getInt( + cursor.getColumnIndex( + TextUtils.isEmpty(cPrimaryName) ? CHILD_COLUMN_ALIAS.concat("rowid") + : cPrimaryName)); + if (tempChild.get(old) == null) { + tempChild.put(old, new ArrayList()); + } + tempChild.get(old).add(cEntity); + } + + List wFields = SqlUtil.getAllNotIgnoreField(clazz); + if (wFields != null && !wFields.isEmpty()) { + Set pKeys = tempParent.keySet(); + for (Object pk : pKeys) { + T wrapper = clazz.newInstance(); + P p = tempParent.get(pk); + boolean isPSet = false, isCSet = false; + for (Field f : wFields) { + if (!isPSet && f.getAnnotation(One.class) != null) { + f.set(wrapper, p); + isPSet = true; + } + if (!isCSet && f.getAnnotation(Many.class) != null) { + f.set(wrapper, tempChild.get(pk)); + isCSet = true; + } + } + wrapper.handleConvert(); //处理下转换 + wrappers.add(wrapper); + } + } + return wrappers; + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取父表主键数据 + * + * @param parentPrimary 父表主键别名 + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) private Object setPPValue(String parentPrimary, + Cursor cursor) { + Object ppValue = null; + int ppColumn = cursor.getColumnIndex(parentPrimary); //父表主键所在的列 + int type = cursor.getType(ppColumn); + switch (type) { + case Cursor.FIELD_TYPE_INTEGER: + ppValue = cursor.getLong(ppColumn); + break; + case Cursor.FIELD_TYPE_FLOAT: + ppValue = cursor.getFloat(ppColumn); + break; + case Cursor.FIELD_TYPE_STRING: + ppValue = cursor.getString(ppColumn); + break; + } + return ppValue; + } + + /** + * 条件查寻数据 + */ + List findData(SQLiteDatabase db, Class clazz, String... expression) { + db = checkDb(db); + CheckUtil.checkSqlExpression(expression); + String sql = String.format("SELECT rowid, * FROM %s WHERE %s", CommonUtil.getClassName(clazz), + expression[0]); + String[] params = new String[expression.length - 1]; + System.arraycopy(expression, 1, params, 0, params.length); + + return exeNormalDataSql(db, clazz, sql, params); + } + + /** + * 获取分页数据 + */ + List findData(SQLiteDatabase db, Class clazz, int page, int num, + String... expression) { + if (page < 1 || num < 1) { + return null; + } + db = checkDb(db); + CheckUtil.checkSqlExpression(expression); + String sql = String.format("SELECT rowid, * FROM %s WHERE %s LIMIT %s,%s", + CommonUtil.getClassName(clazz), + expression[0], (page - 1) * num, num); + + String[] params = new String[expression.length - 1]; + System.arraycopy(expression, 1, params, 0, params.length); + + return exeNormalDataSql(db, clazz, sql, params); + } + + /** + * 模糊查寻数据 + */ + List findDataByFuzzy(SQLiteDatabase db, Class clazz, + String conditions) { + db = checkDb(db); + if (TextUtils.isEmpty(conditions)) { + throw new IllegalArgumentException("sql语句表达式不能为null或\"\""); + } + if (!conditions.toUpperCase().contains("LIKE")) { + throw new IllegalArgumentException("sql语句表达式未包含LIEK"); + } + String sql = String.format("SELECT rowid, * FROM %s, WHERE %s", CommonUtil.getClassName(clazz), + conditions); + return exeNormalDataSql(db, clazz, sql, null); + } + + /** + * 分页、模糊搜索数据 + */ + List findDataByFuzzy(SQLiteDatabase db, Class clazz, + int page, int num, String conditions) { + if (page < 1 || num < 1) { + return null; + } + db = checkDb(db); + if (TextUtils.isEmpty(conditions)) { + throw new IllegalArgumentException("sql语句表达式不能为null或\"\""); + } + if (!conditions.toUpperCase().contains("LIKE")) { + throw new IllegalArgumentException("sql语句表达式未包含LIEK"); + } + String sql = String.format("SELECT rowid, * FROM %s WHERE %s LIMIT %s,%s", + CommonUtil.getClassName(clazz), conditions, (page - 1) * num, num); + return exeNormalDataSql(db, clazz, sql, null); + } + + /** + * 查找表的所有数据 + */ + List findAllData(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + String sql = String.format("SELECT rowid, * FROM %s", CommonUtil.getClassName(clazz)); + return exeNormalDataSql(db, clazz, sql, null); + } + + /** + * 执行查询普通数据的sql语句,并创建对象 + * + * @param sql sql 查询语句 + * @param selectionArgs 查询参数,如何sql语句中查询条件含有'?'则该参数不能为空 + */ + private List exeNormalDataSql(SQLiteDatabase db, Class clazz, + String sql, String[] selectionArgs) { + String[] temp = new String[selectionArgs.length]; + int i = 0; + for (String arg : selectionArgs){ + temp[i] = encodeStr(arg); + i ++; + } + Cursor cursor = db.rawQuery(sql, temp); + List data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null; + closeCursor(cursor); + return data; + } + + /** + * 根据数据游标创建一个具体的对象 + */ + private synchronized List newInstanceEntity(Class clazz, + Cursor cursor) { + List fields = CommonUtil.getAllFields(clazz); + List entitys = new ArrayList<>(); + if (fields != null && fields.size() > 0) { + try { + while (cursor.moveToNext()) { + T entity = clazz.newInstance(); + String primaryName = ""; + for (Field field : fields) { + field.setAccessible(true); + if (SqlUtil.isIgnore(field)) { + continue; + } + + Class type = field.getType(); + if (SqlUtil.isPrimary(field) && (type == int.class || type == Integer.class)) { + primaryName = field.getName(); + } + + int column = cursor.getColumnIndex(field.getName()); + if (column == -1) continue; + setFieldValue(type, field, column, cursor, entity); + } + //当设置了主键,而且主键的类型为integer时,查询RowID等于主键 + entity.rowID = cursor.getInt( + cursor.getColumnIndex(TextUtils.isEmpty(primaryName) ? "rowid" : primaryName)); + //mDataCache.put(getCacheKey(entity), entity); + entitys.add(entity); + } + closeCursor(cursor); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + return entitys; + } + + /** + * 设置字段的值 + * + * @throws IllegalAccessException + */ + private void setFieldValue(Class type, Field field, int column, Cursor cursor, Object entity) + throws IllegalAccessException { + if (cursor == null || cursor.isClosed()) { + ALog.e(TAG, "cursor没有初始化"); + return; + } + if (type == String.class) { + String temp = cursor.getString(column); + if (!TextUtils.isEmpty(temp)) { + field.set(entity, URLDecoder.decode(temp)); + } + } else if (type == int.class || type == Integer.class) { + field.setInt(entity, cursor.getInt(column)); + } else if (type == float.class || type == Float.class) { + field.setFloat(entity, cursor.getFloat(column)); + } else if (type == double.class || type == Double.class) { + field.setDouble(entity, cursor.getDouble(column)); + } else if (type == long.class || type == Long.class) { + field.setLong(entity, cursor.getLong(column)); + } else if (type == boolean.class || type == Boolean.class) { + String temp = cursor.getString(column); + if (TextUtils.isEmpty(temp)) { + field.setBoolean(entity, false); + } else { + field.setBoolean(entity, !temp.equalsIgnoreCase("false")); + } + } else if (type == java.util.Date.class || type == java.sql.Date.class) { + field.set(entity, new Date(URLDecoder.decode(cursor.getString(column)))); + } else if (type == byte[].class) { + field.set(entity, cursor.getBlob(column)); + } else if (type == Map.class) { + String temp = cursor.getString(column); + if (!TextUtils.isEmpty(temp)) { + field.set(entity, SqlUtil.str2Map(URLDecoder.decode(temp))); + } + } else if (type == List.class) { + String value = cursor.getString(column); + if (!TextUtils.isEmpty(value)) { + field.set(entity, SqlUtil.str2List(URLDecoder.decode(value), field)); + } + } + } + + /** + * 获取所在行Id + */ + int[] getRowId(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + Cursor cursor = db.rawQuery("SELECT rowid, * FROM " + CommonUtil.getClassName(clazz), null); + int[] ids = new int[cursor.getCount()]; + int i = 0; + while (cursor.moveToNext()) { + ids[i] = cursor.getInt(cursor.getColumnIndex("rowid")); + i++; + } + cursor.close(); + return ids; + } + + /** + * 获取行Id + */ + int getRowId(SQLiteDatabase db, Class clazz, Object[] wheres, Object[] values) { + db = checkDb(db); + if (wheres.length <= 0 || values.length <= 0) { + ALog.e(TAG, "请输入删除条件"); + return -1; + } else if (wheres.length != values.length) { + ALog.e(TAG, "groupHash 和 vaule 长度不相等"); + return -1; + } + StringBuilder sb = new StringBuilder(); + sb.append("SELECT rowid FROM ").append(CommonUtil.getClassName(clazz)).append(" WHERE "); + int i = 0; + for (Object where : wheres) { + sb.append(where).append("=").append("'").append(values[i]).append("'"); + sb.append(i >= wheres.length - 1 ? "" : ","); + i++; + } + Cursor c = db.rawQuery(sb.toString(), null); + int id = c.getColumnIndex("rowid"); + c.close(); + return id; + } + + /** + * 通过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 " + tableName + " WHERE rowid=" + rowId; + Cursor cursor = db.rawQuery(sql, null); + boolean isExist = cursor.getCount() > 0; + cursor.close(); + return isExist; + } +} 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 b2d05d97..1af103a2 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java @@ -49,9 +49,7 @@ class DelegateUpdate extends AbsDelegate { params[i] = String.format("'%s'", encodeStr(expression[i + 1])); } sql = String.format(sql, params); - print(DEL_DATA, sql); db.execSQL(sql); - close(db); } /** @@ -66,7 +64,6 @@ class DelegateUpdate extends AbsDelegate { } else { ALog.e(TAG, "更新记录失败,记录没有属性字段"); } - close(db); } /** @@ -139,7 +136,6 @@ class DelegateUpdate extends AbsDelegate { } else { ALog.e(TAG, "保存记录失败,记录没有属性字段"); } - 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 9209c202..86a36406 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java @@ -36,7 +36,7 @@ public class DelegateWrapper { private DelegateWrapper(Context context) { SqlHelper helper = SqlHelper.init(context.getApplicationContext()); - mDb = helper.getWritableDatabase(); + mDb = helper.getDb(); mDManager = DelegateManager.getInstance(); } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java index fd8be86f..3bbc34fa 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -1,257 +1,302 @@ -/* - * 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.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import com.arialyy.aria.util.ALog; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Created by lyy on 2015/11/2. - * sql帮助类 - */ -final class SqlHelper extends SQLiteOpenHelper { - private static final String TAG = "SqlHelper"; - static volatile SqlHelper INSTANCE = null; - - 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); - SQLiteDatabase db = INSTANCE.getWritableDatabase(); - db = delegate.checkDb(db); - // SQLite在3.6.19版本中开始支持外键约束, - // 而在Android中 2.1以前的版本使用的SQLite版本是3.5.9, 在2.2版本中使用的是3.6.22. - // 但是为了兼容以前的程序,默认并没有启用该功能,如果要启用该功能 - // 需要使用如下语句: - db.execSQL("PRAGMA foreign_keys=ON;"); - Set tables = DBConfig.mapping.keySet(); - for (String tableName : tables) { - Class clazz = DBConfig.mapping.get(tableName); - - if (!delegate.tableExists(db, clazz)) { - delegate.createTable(db, clazz); - } - } - } - } - return INSTANCE; - } - - private SqlHelper(Context context, DelegateCommon delegate) { - super(DBConfig.SAVE_IN_SDCARD ? new DatabaseContext(context) : context, DBConfig.DB_NAME, null, - DBConfig.VERSION); - mDelegate = delegate; - } - - @Override public void onCreate(SQLiteDatabase db) { - - } - - @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion < newVersion) { - if (oldVersion < 31) { - handle314AriaUpdate(db); - } else if (oldVersion < 45) { - handle360AriaUpdate(db); - } else { - handleDbUpdate(db, null, null); - } - } - } - - @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion > newVersion) { - handleDbUpdate(db, null, null); - } - } - - /** - * 处理数据库升级,该段代码无法修改表字段 - * - * @param modifyColumns 需要修改的表字段的映射,key为表名, - * value{@code Map}中的Map的key为老字段名称,value为该老字段对应的新字段名称 - * @param delColumns 需要删除的表字段,key为表名,value{@code List}为需要删除的字段列表 - */ - private void handleDbUpdate(SQLiteDatabase db, Map> modifyColumns, - Map> delColumns) { - if (db == null) { - ALog.e("SqlHelper", "db 为 null"); - return; - } else if (!db.isOpen()) { - ALog.e("SqlHelper", "db已关闭"); - return; - } - - try { - db.beginTransaction(); - Set tables = DBConfig.mapping.keySet(); - for (String tableName : tables) { - Class clazz = DBConfig.mapping.get(tableName); - if (mDelegate.tableExists(db, clazz)) { - db = mDelegate.checkDb(db); - //修改表名为中介表名 - String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName); - db.execSQL(alertSql); - - //创建新表 - mDelegate.createTable(db, clazz); - - String sql = String.format("SELECT COUNT(*) FROM %s_temp", tableName); - Cursor cursor = db.rawQuery(sql, null); - cursor.moveToFirst(); - long count = cursor.getLong(0); - cursor.close(); - - if (count > 0) { - // 获取所有表字段名称 - Cursor columnC = - db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null); - StringBuilder params = new StringBuilder(); - - while (columnC.moveToNext()) { - String columnName = columnC.getString(columnC.getColumnIndex("name")); - if (delColumns != null && delColumns.get(tableName) != null) { - List delColumn = delColumns.get(tableName); - if (delColumn != null && !delColumn.isEmpty()) { - if (delColumn.contains(columnName)) { - continue; - } - } - } - - params.append(columnName).append(","); - } - columnC.close(); - - String oldParamStr = params.toString(); - oldParamStr = oldParamStr.substring(0, oldParamStr.length() - 1); - String newParamStr = oldParamStr; - // 处理字段名称改变 - if (modifyColumns != null) { - //newParamStr = params.toString(); - Map columnMap = modifyColumns.get(tableName); - if (columnMap != null && !columnMap.isEmpty()) { - Set keys = columnMap.keySet(); - for (String key : keys) { - if (newParamStr.contains(key)) { - newParamStr = newParamStr.replace(key, columnMap.get(key)); - } - } - } - } - //恢复数据 - String insertSql = - String.format("INSERT INTO %s (%s) SELECT %s FROM %s_temp", tableName, newParamStr, - oldParamStr, tableName); - ALog.d(TAG, "insertSql = " + insertSql); - db.execSQL(insertSql); - } - //删除中介表 - mDelegate.dropTable(db, tableName + "_temp"); - } - } - db.setTransactionSuccessful(); - } catch (Exception e) { - ALog.e(TAG, e); - } finally { - db.endTransaction(); - } - - mDelegate.close(db); - } - - /** - * 处理3.6以下版本的数据库升级 - */ - private void handle360AriaUpdate(SQLiteDatabase db) { - String[] taskTables = - new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" }; - for (String taskTable : taskTables) { - if (mDelegate.tableExists(db, taskTable)) { - mDelegate.dropTable(db, taskTable); - } - } - Map> columnMap = new HashMap<>(); - Map map = new HashMap<>(); - map.put("groupName", "groupHash"); - columnMap.put("DownloadEntity", map); - columnMap.put("DownloadGroupEntity", map); - handleDbUpdate(db, columnMap, null); - } - - /** - * 处理3.4版本之前数据库迁移,主要是修改子表外键字段对应的值 - */ - private void handle314AriaUpdate(SQLiteDatabase db) { - String[] taskTables = - new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" }; - for (String taskTable : taskTables) { - if (mDelegate.tableExists(db, taskTable)) { - mDelegate.dropTable(db, taskTable); - } - } - - //删除所有主键为null和逐渐重复的数据 - String[] tables = new String[] { "DownloadEntity", "DownloadGroupEntity" }; - String[] keys = new String[] { "downloadPath", "groupName" }; - int i = 0; - for (String tableName : tables) { - String pColumn = keys[i]; - String nullSql = - String.format("DELETE FROM %s WHERE %s='' OR %s IS NULL", tableName, pColumn, pColumn); - ALog.d(TAG, nullSql); - db.execSQL(nullSql); - - //删除所有主键重复的数据 - String repeatSql = - String.format( - "DELETE FROM %s WHERE %s IN(SELECT %s FROM %s GROUP BY %s HAVING COUNT(%s) > 1)", - tableName, pColumn, pColumn, tableName, pColumn, pColumn); - - ALog.d(TAG, repeatSql); - db.execSQL(repeatSql); - i++; - } - - Map> modifyColumnMap = new HashMap<>(); - Map map = new HashMap<>(); - map.put("groupName", "groupHash"); - modifyColumnMap.put("DownloadEntity", map); - modifyColumnMap.put("DownloadGroupEntity", map); - - Map> delColumnMap = new HashMap<>(); - List dEntityDel = new ArrayList<>(); - dEntityDel.add("taskKey"); - delColumnMap.put("DownloadEntity", dEntityDel); - List dgEntityDel = new ArrayList<>(); - dgEntityDel.add("subtask"); - delColumnMap.put("DownloadGroupEntity", dgEntityDel); - - handleDbUpdate(db, modifyColumnMap, delColumnMap); - } +/* + * 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.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.os.Build; +import com.arialyy.aria.util.ALog; +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created by lyy on 2015/11/2. + * sql帮助类 + */ +final class SqlHelper extends SQLiteOpenHelper { + private static final String TAG = "SqlHelper"; + 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); + } + } + return INSTANCE; + } + + static SqlHelper getInstance() { + return INSTANCE; + } + + private SqlHelper(Context context, DelegateCommon delegate) { + 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) { + super.onOpen(db); + } + + @Override public void onConfigure(SQLiteDatabase db) { + super.onConfigure(db); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + db.setForeignKeyConstraintsEnabled(true); + } else { + // SQLite在3.6.19版本中开始支持外键约束, + // 而在Android中 2.1以前的版本使用的SQLite版本是3.5.9, 在2.2版本中使用的是3.6.22. + // 但是为了兼容以前的程序,默认并没有启用该功能,如果要启用该功能 + // 需要使用如下语句: + db.execSQL("PRAGMA foreign_keys=ON;"); + } + if (DBConfig.DEBUG) { + db.enableWriteAheadLogging(); + } + } + + @Override public void onCreate(SQLiteDatabase db) { + DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class); + Set tables = DBConfig.mapping.keySet(); + for (String tableName : tables) { + Class clazz = DBConfig.mapping.get(tableName); + if (!delegate.tableExists(db, clazz)) { + delegate.createTable(db, clazz); + } + } + } + + @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion < newVersion) { + if (oldVersion < 31) { + handle314AriaUpdate(db); + } else if (oldVersion < 45) { + handle360AriaUpdate(db); + } else { + handleDbUpdate(db, null, null); + } + } + } + + @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion > newVersion) { + handleDbUpdate(db, null, null); + } + } + + /** + * 获取数据库连接 + */ + SQLiteDatabase getDb() { + SQLiteDatabase db; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder().setOpenFlags( + SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.OPEN_READWRITE | + SQLiteDatabase.CREATE_IF_NECESSARY).build(); + setOpenParams(params); + db = getWritableDatabase(); + } else { + //SQLiteDatabase.openOrCreateDatabase() + File dbFile = mContext.getDatabasePath(DBConfig.DB_NAME); + if (!dbFile.exists()) { + db = getWritableDatabase(); + } else { + // 触发一次SQLiteOpenHelper的流程,再使用NO_LOCALIZED_COLLATORS标志打开数据库 + db = getReadableDatabase(); + db.close(); + db = SQLiteDatabase.openDatabase(dbFile.getPath(), null, + SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.OPEN_READWRITE | + SQLiteDatabase.CREATE_IF_NECESSARY); + } + } + return db; + } + + /** + * 处理数据库升级,该段代码无法修改表字段 + * + * @param modifyColumns 需要修改的表字段的映射,key为表名, + * value{@code Map}中的Map的key为老字段名称,value为该老字段对应的新字段名称 + * @param delColumns 需要删除的表字段,key为表名,value{@code List}为需要删除的字段列表 + */ + private void handleDbUpdate(SQLiteDatabase db, Map> modifyColumns, + Map> delColumns) { + if (db == null) { + ALog.e("SqlHelper", "db 为 null"); + return; + } else if (!db.isOpen()) { + ALog.e("SqlHelper", "db已关闭"); + return; + } + + try { + db.beginTransaction(); + Set tables = DBConfig.mapping.keySet(); + for (String tableName : tables) { + Class clazz = DBConfig.mapping.get(tableName); + if (mDelegate.tableExists(db, clazz)) { + //修改表名为中介表名 + String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName); + db.execSQL(alertSql); + + //创建新表 + mDelegate.createTable(db, clazz); + + String sql = String.format("SELECT COUNT(*) FROM %s_temp", tableName); + Cursor cursor = db.rawQuery(sql, null); + cursor.moveToFirst(); + long count = cursor.getLong(0); + cursor.close(); + + if (count > 0) { + // 获取所有表字段名称 + Cursor columnC = + db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null); + StringBuilder params = new StringBuilder(); + + while (columnC.moveToNext()) { + String columnName = columnC.getString(columnC.getColumnIndex("name")); + if (delColumns != null && delColumns.get(tableName) != null) { + List delColumn = delColumns.get(tableName); + if (delColumn != null && !delColumn.isEmpty()) { + if (delColumn.contains(columnName)) { + continue; + } + } + } + + params.append(columnName).append(","); + } + columnC.close(); + + String oldParamStr = params.toString(); + oldParamStr = oldParamStr.substring(0, oldParamStr.length() - 1); + String newParamStr = oldParamStr; + // 处理字段名称改变 + if (modifyColumns != null) { + //newParamStr = params.toString(); + Map columnMap = modifyColumns.get(tableName); + if (columnMap != null && !columnMap.isEmpty()) { + Set keys = columnMap.keySet(); + for (String key : keys) { + if (newParamStr.contains(key)) { + newParamStr = newParamStr.replace(key, columnMap.get(key)); + } + } + } + } + //恢复数据 + String insertSql = + String.format("INSERT INTO %s (%s) SELECT %s FROM %s_temp", tableName, newParamStr, + oldParamStr, tableName); + ALog.d(TAG, "insertSql = " + insertSql); + db.execSQL(insertSql); + } + //删除中介表 + mDelegate.dropTable(db, tableName + "_temp"); + } + } + db.setTransactionSuccessful(); + } catch (Exception e) { + ALog.e(TAG, e); + } finally { + db.endTransaction(); + } + } + + /** + * 处理3.6以下版本的数据库升级 + */ + private void handle360AriaUpdate(SQLiteDatabase db) { + String[] taskTables = + new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" }; + for (String taskTable : taskTables) { + if (mDelegate.tableExists(db, taskTable)) { + mDelegate.dropTable(db, taskTable); + } + } + Map> columnMap = new HashMap<>(); + Map map = new HashMap<>(); + map.put("groupName", "groupHash"); + columnMap.put("DownloadEntity", map); + columnMap.put("DownloadGroupEntity", map); + handleDbUpdate(db, columnMap, null); + } + + /** + * 处理3.4版本之前数据库迁移,主要是修改子表外键字段对应的值 + */ + private void handle314AriaUpdate(SQLiteDatabase db) { + String[] taskTables = + new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" }; + for (String taskTable : taskTables) { + if (mDelegate.tableExists(db, taskTable)) { + mDelegate.dropTable(db, taskTable); + } + } + + //删除所有主键为null和逐渐重复的数据 + String[] tables = new String[] { "DownloadEntity", "DownloadGroupEntity" }; + String[] keys = new String[] { "downloadPath", "groupName" }; + int i = 0; + for (String tableName : tables) { + String pColumn = keys[i]; + String nullSql = + String.format("DELETE FROM %s WHERE %s='' OR %s IS NULL", tableName, pColumn, pColumn); + ALog.d(TAG, nullSql); + db.execSQL(nullSql); + + //删除所有主键重复的数据 + String repeatSql = + String.format( + "DELETE FROM %s WHERE %s IN(SELECT %s FROM %s GROUP BY %s HAVING COUNT(%s) > 1)", + tableName, pColumn, pColumn, tableName, pColumn, pColumn); + + ALog.d(TAG, repeatSql); + db.execSQL(repeatSql); + i++; + } + + Map> modifyColumnMap = new HashMap<>(); + Map map = new HashMap<>(); + map.put("groupName", "groupHash"); + modifyColumnMap.put("DownloadEntity", map); + modifyColumnMap.put("DownloadGroupEntity", map); + + Map> delColumnMap = new HashMap<>(); + List dEntityDel = new ArrayList<>(); + dEntityDel.add("taskKey"); + delColumnMap.put("DownloadEntity", dEntityDel); + List dgEntityDel = new ArrayList<>(); + dgEntityDel.add("subtask"); + delColumnMap.put("DownloadGroupEntity", dgEntityDel); + + handleDbUpdate(db, modifyColumnMap, delColumnMap); + } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java b/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java index 394b8ddd..e35449a0 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -1,254 +1,269 @@ -/* - * 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.util; - -import android.text.TextUtils; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.upload.UTaskWrapper; -import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.exception.ParamException; -import java.io.File; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Created by Lyy on 2016/9/23. - * 检查帮助类 - */ -public class CheckUtil { - private static final String TAG = "CheckUtil"; - - /** - * 检查ftp上传路径,如果ftp上传路径为空,抛出空指针异常 - * 如果ftp上传路径不是以"ftp"或"sftp",抛出参数异常 - * - * @param ftpUrl ftp上传路径 - */ - public static void checkFtpUploadUrl(String ftpUrl) { - if (TextUtils.isEmpty(ftpUrl)) { - throw new ParamException("ftp上传路径为空"); - } else if (!ftpUrl.startsWith("ftp") || !ftpUrl.startsWith("sftp")) { - throw new ParamException("ftp上传路径无效"); - } - } - - /** - * 判空 - */ - public static void checkNull(Object obj) { - if (obj == null) throw new IllegalArgumentException("不能传入空对象"); - } - - /** - * 检查分页数据,需要查询的页数,从1开始,如果page小于1 或 num 小于1,则抛出{@link NullPointerException} - * - * @param page 从1 开始 - * @param num 每页数量 - */ - public static void checkPageParams(int page, int num) { - if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1"); - } - - /** - * 检查sql的expression是否合法 - */ - public static void checkSqlExpression(String... expression) { - if (expression.length == 0) { - throw new IllegalArgumentException("sql语句表达式不能为null"); - } - if (expression.length == 1) { - throw new IllegalArgumentException("表达式需要写入参数"); - } - String where = expression[0]; - if (!where.contains("?")) { - throw new IllegalArgumentException("请在where语句的'='后编写?"); - } - Pattern pattern = Pattern.compile("\\?"); - Matcher matcher = pattern.matcher(where); - int count = 0; - while (matcher.find()) { - count++; - } - if (count < expression.length - 1) { - throw new IllegalArgumentException("条件语句的?个数不能小于参数个数"); - } - if (count > expression.length - 1) { - throw new IllegalArgumentException("条件语句的?个数不能大于参数个数"); - } - } - - /** - * 检查下载实体 - */ - public static void checkDownloadEntity(DownloadEntity entity) { - checkUrlInvalidThrow(entity.getUrl()); - entity.setUrl(entity.getUrl()); - checkPath(entity.getDownloadPath()); - } - - /** - * 检测下载链接是否为null - */ - public static void checkPath(String path) { - if (TextUtils.isEmpty(path)) { - throw new IllegalArgumentException("保存路径不能为null"); - } - } - - /** - * 检测url是否合法,如果url不合法,将抛异常 - */ - public static void checkUrlInvalidThrow(String url) { - if (TextUtils.isEmpty(url)) { - throw new IllegalArgumentException("url不能为null"); - } else if (!url.startsWith("http") && !url.startsWith("ftp")) { - throw new IllegalArgumentException("url错误"); - } - int index = url.indexOf("://"); - if (index == -1) { - throw new IllegalArgumentException("url不合法"); - } - } - - /** - * 检测url是否合法 - * - * @return {@code true} 合法,{@code false} 非法 - */ - public static boolean checkUrl(String url) { - if (TextUtils.isEmpty(url)) { - ALog.e(TAG, "url不能为null"); - return false; - } else if (!url.startsWith("http") && !url.startsWith("ftp")) { - ALog.e(TAG, "url【" + url + "】错误"); - return false; - } - int index = url.indexOf("://"); - if (index == -1) { - ALog.e(TAG, "url【" + url + "】不合法"); - } - return true; - } - - /** - * 检测下载链接组是否为null - */ - public static void checkDownloadUrls(List urls) { - if (urls == null || urls.isEmpty()) { - throw new IllegalArgumentException("链接组不能为null"); - } - } - - /** - * 检查下载任务组保存路径 - */ - public static void checkDownloadPaths(List paths) { - if (paths == null || paths.isEmpty()) { - throw new IllegalArgumentException("链接保存路径不能为null"); - } - } - - /** - * 检测上传地址是否为null - */ - public static void checkUploadPath(String uploadPath) { - if (TextUtils.isEmpty(uploadPath)) { - throw new IllegalArgumentException("上传地址不能为null"); - } - File file = new File(uploadPath); - if (!file.exists()) { - throw new IllegalArgumentException("上传文件不存在"); - } - } - - /** - * 检查任务实体 - */ - public static void checkTaskEntity(AbsTaskWrapper entity) { - if (entity instanceof DTaskWrapper) { - checkDownloadTaskEntity(((DTaskWrapper) entity).getEntity()); - } else if (entity instanceof UTaskWrapper) { - checkUploadTaskEntity(((UTaskWrapper) entity).getEntity()); - } - } - - /** - * 检查命令实体 - * - * @param checkType 删除命令和停止命令不需要检查下载链接和保存路径 - * @return {@code false}实体无效 - */ - public static boolean checkCmdEntity(AbsTaskWrapper entity, boolean checkType) { - boolean b = false; - if (entity instanceof DTaskWrapper) { - DownloadEntity entity1 = ((DTaskWrapper) entity).getEntity(); - if (entity1 == null) { - ALog.e(TAG, "下载实体不能为空"); - } else if (checkType && TextUtils.isEmpty(entity1.getUrl())) { - ALog.e(TAG, "下载链接不能为空"); - } else if (checkType && TextUtils.isEmpty(entity1.getDownloadPath())) { - ALog.e(TAG, "保存路径不能为空"); - } else { - b = true; - } - } else if (entity instanceof UTaskWrapper) { - UploadEntity entity1 = ((UTaskWrapper) entity).getEntity(); - if (entity1 == null) { - ALog.e(TAG, "上传实体不能为空"); - } else if (TextUtils.isEmpty(entity1.getFilePath())) { - ALog.e(TAG, "上传文件路径不能为空"); - } else { - b = true; - } - } - return b; - } - - /** - * 检查上传实体是否合法 - */ - private static void checkUploadTaskEntity(UploadEntity entity) { - if (entity == null) { - throw new NullPointerException("上传实体不能为空"); - } else if (TextUtils.isEmpty(entity.getFilePath())) { - throw new IllegalArgumentException("上传文件路径不能为空"); - } else if (TextUtils.isEmpty(entity.getFileName())) { - throw new IllegalArgumentException("上传文件名不能为空"); - } - } - - /** - * 检测下载实体是否合法 - * 合法(true) - * - * @param entity 下载实体 - */ - private static void checkDownloadTaskEntity(DownloadEntity entity) { - if (entity == null) { - throw new NullPointerException("下载实体不能为空"); - } else if (TextUtils.isEmpty(entity.getUrl())) { - throw new IllegalArgumentException("下载链接不能为空"); - } else if (TextUtils.isEmpty(entity.getFileName())) { - throw new NullPointerException("文件名不能为null"); - } else if (TextUtils.isEmpty(entity.getDownloadPath())) { - throw new NullPointerException("文件保存路径不能为null"); - } - } +/* + * 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.util; + +import android.text.TextUtils; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.upload.UTaskWrapper; +import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.exception.ParamException; +import java.io.File; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by Lyy on 2016/9/23. + * 检查帮助类 + */ +public class CheckUtil { + private static final String TAG = "CheckUtil"; + + /** + * 检查ftp上传路径,如果ftp上传路径为空,抛出空指针异常 + * 如果ftp上传路径不是以"ftp"或"sftp",抛出参数异常 + * + * @param ftpUrl ftp上传路径 + */ + public static void checkFtpUploadUrl(String ftpUrl) { + if (TextUtils.isEmpty(ftpUrl)) { + throw new ParamException("ftp上传路径为空"); + } else if (!ftpUrl.startsWith("ftp") || !ftpUrl.startsWith("sftp")) { + throw new ParamException("ftp上传路径无效"); + } + } + + /** + * 判空 + */ + public static void checkNull(Object obj) { + if (obj == null) throw new IllegalArgumentException("不能传入空对象"); + } + + /** + * 检查分页数据,需要查询的页数,从1开始,如果page小于1 或 num 小于1,则抛出{@link NullPointerException} + * + * @param page 从1 开始 + * @param num 每页数量 + */ + public static void checkPageParams(int page, int num) { + if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1"); + } + + /** + * 检查sql的expression是否合法 + */ + public static void checkSqlExpression(String... expression) { + if (expression.length == 0) { + throw new IllegalArgumentException("sql语句表达式不能为null"); + } + if (expression.length == 1) { + throw new IllegalArgumentException("表达式需要写入参数"); + } + String where = expression[0]; + if (!where.contains("?")) { + throw new IllegalArgumentException("请在where语句的'='后编写?"); + } + Pattern pattern = Pattern.compile("\\?"); + Matcher matcher = pattern.matcher(where); + int count = 0; + while (matcher.find()) { + count++; + } + if (count < expression.length - 1) { + throw new IllegalArgumentException("条件语句的?个数不能小于参数个数"); + } + if (count > expression.length - 1) { + throw new IllegalArgumentException("条件语句的?个数不能大于参数个数"); + } + } + + /** + * 检查下载实体 + */ + public static void checkDownloadEntity(DownloadEntity entity) { + checkUrlInvalidThrow(entity.getUrl()); + entity.setUrl(entity.getUrl()); + checkPath(entity.getDownloadPath()); + } + + /** + * 检测下载链接是否为null + */ + public static void checkPath(String path) { + if (TextUtils.isEmpty(path)) { + throw new IllegalArgumentException("保存路径不能为null"); + } + } + + /** + * 检测url是否合法,如果url不合法,将抛异常 + */ + public static void checkUrlInvalidThrow(String url) { + if (TextUtils.isEmpty(url)) { + throw new IllegalArgumentException("url不能为null"); + } else if (!url.startsWith("http") && !url.startsWith("ftp")) { + throw new IllegalArgumentException("url错误"); + } + int index = url.indexOf("://"); + if (index == -1) { + throw new IllegalArgumentException("url不合法"); + } + } + + /** + * 检测url是否合法,如果url不合法,将抛出{@link IllegalArgumentException}异常 + */ + public static void checkUrl(String url) { + if (TextUtils.isEmpty(url)) { + throw new NullPointerException("url为空"); + } else if (!url.startsWith("http") && !url.startsWith("ftp")) { + throw new IllegalArgumentException(String.format("url【%s】错误", url)); + } + int index = url.indexOf("://"); + if (index == -1) { + throw new IllegalArgumentException(String.format("url【%s】不合法", url)); + } + } + + /** + * 检测url是否合法 + * + * @return {@code true} 合法,{@code false} 非法 + */ + public static boolean checkUrlNotThrow(String url) { + if (TextUtils.isEmpty(url)) { + ALog.e(TAG, "url不能为null"); + return false; + } else if (!url.startsWith("http") && !url.startsWith("ftp")) { + ALog.e(TAG, "url【" + url + "】错误"); + return false; + } + int index = url.indexOf("://"); + if (index == -1) { + ALog.e(TAG, "url【" + url + "】不合法"); + } + return true; + } + + /** + * 检测下载链接组是否为null + */ + public static void checkDownloadUrls(List urls) { + if (urls == null || urls.isEmpty()) { + throw new IllegalArgumentException("链接组不能为null"); + } + } + + /** + * 检查下载任务组保存路径 + */ + public static void checkDownloadPaths(List paths) { + if (paths == null || paths.isEmpty()) { + throw new IllegalArgumentException("链接保存路径不能为null"); + } + } + + /** + * 检测上传地址是否为null + */ + public static void checkUploadPath(String uploadPath) { + if (TextUtils.isEmpty(uploadPath)) { + throw new IllegalArgumentException("上传地址不能为null"); + } + File file = new File(uploadPath); + if (!file.exists()) { + throw new IllegalArgumentException("上传文件不存在"); + } + } + + /** + * 检查任务实体 + */ + public static void checkTaskEntity(AbsTaskWrapper entity) { + if (entity instanceof DTaskWrapper) { + checkDownloadTaskEntity(((DTaskWrapper) entity).getEntity()); + } else if (entity instanceof UTaskWrapper) { + checkUploadTaskEntity(((UTaskWrapper) entity).getEntity()); + } + } + + /** + * 检查命令实体 + * + * @param checkType 删除命令和停止命令不需要检查下载链接和保存路径 + * @return {@code false}实体无效 + */ + public static boolean checkCmdEntity(AbsTaskWrapper entity, boolean checkType) { + boolean b = false; + if (entity instanceof DTaskWrapper) { + DownloadEntity entity1 = ((DTaskWrapper) entity).getEntity(); + if (entity1 == null) { + ALog.e(TAG, "下载实体不能为空"); + } else if (checkType && TextUtils.isEmpty(entity1.getUrl())) { + ALog.e(TAG, "下载链接不能为空"); + } else if (checkType && TextUtils.isEmpty(entity1.getDownloadPath())) { + ALog.e(TAG, "保存路径不能为空"); + } else { + b = true; + } + } else if (entity instanceof UTaskWrapper) { + UploadEntity entity1 = ((UTaskWrapper) entity).getEntity(); + if (entity1 == null) { + ALog.e(TAG, "上传实体不能为空"); + } else if (TextUtils.isEmpty(entity1.getFilePath())) { + ALog.e(TAG, "上传文件路径不能为空"); + } else { + b = true; + } + } + return b; + } + + /** + * 检查上传实体是否合法 + */ + private static void checkUploadTaskEntity(UploadEntity entity) { + if (entity == null) { + throw new NullPointerException("上传实体不能为空"); + } else if (TextUtils.isEmpty(entity.getFilePath())) { + throw new IllegalArgumentException("上传文件路径不能为空"); + } else if (TextUtils.isEmpty(entity.getFileName())) { + throw new IllegalArgumentException("上传文件名不能为空"); + } + } + + /** + * 检测下载实体是否合法 + * 合法(true) + * + * @param entity 下载实体 + */ + private static void checkDownloadTaskEntity(DownloadEntity entity) { + if (entity == null) { + throw new NullPointerException("下载实体不能为空"); + } else if (TextUtils.isEmpty(entity.getUrl())) { + throw new IllegalArgumentException("下载链接不能为空"); + } else if (TextUtils.isEmpty(entity.getFileName())) { + throw new NullPointerException("文件名不能为null"); + } else if (TextUtils.isEmpty(entity.getDownloadPath())) { + throw new NullPointerException("文件保存路径不能为null"); + } + } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/util/DbDataHelper.java b/Aria/src/main/java/com/arialyy/aria/util/DbDataHelper.java index 01f9857c..cd5825f9 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/DbDataHelper.java +++ b/Aria/src/main/java/com/arialyy/aria/util/DbDataHelper.java @@ -45,13 +45,27 @@ public class DbDataHelper { return records.get(0).taskRecord; } + /** + * 获取组合任务实体、ftpDir任务实体 + * + * @param groupHash 组合任务Hash + * @return 实体不存在,返回null + */ + public static DownloadGroupEntity getDGEntity(String groupHash) { + List wrapper = + DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?", + groupHash); + + return wrapper == null ? null : wrapper.get(0).groupEntity; + } + /** * 获取组合任务实体 如果数据库不存在该实体,则新创建一个新的任务组实体 * * @param groupHash 组合任务Hash * @param urls 子任务url列表 */ - public static DownloadGroupEntity getHttpDGEntity(String groupHash, List urls) { + public static DownloadGroupEntity getOrCreateHttpDGEntity(String groupHash, List urls) { List wrapper = DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?", groupHash); @@ -96,7 +110,7 @@ public class DbDataHelper { * * @param ftpUrl ftp下载地址 */ - public static DownloadGroupEntity getFtpDGEntity(String ftpUrl) { + public static DownloadGroupEntity getOrCreateFtpDGEntity(String ftpUrl) { List wrapper = DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?", ftpUrl); diff --git a/DEV_LOG.md b/DEV_LOG.md index a4b01c9c..f387c733 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,219 +1,226 @@ -## 开发日志 - + v_3.6.4 - - 优化任务接收器的代码结构 - - 修复`DbEntity.saveAll()`失败的问题 - - 修复分块任务重命名失败的问题 - + v_3.6.3 (2019/4/2) - - fix bug https://github.com/AriaLyy/Aria/issues/377 - + v_3.6.2 (2019/4/1) - - fix bug https://github.com/AriaLyy/Aria/issues/368 - - 增加gradle 5.0支持 - - fix bug https://github.com/AriaLyy/Aria/issues/374 - - 增加分页功能,详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html#%E4%BB%BB%E5%8A%A1%E5%88%97%E8%A1%A8%E5%88%86%E9%A1%B5%EF%BC%88362%E4%BB%A5%E4%B8%8A%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81%EF%BC%89 - + v_3.6.1 (2019/3/5) - - fix bug https://github.com/AriaLyy/Aria/issues/367 - + v_3.6 (2019/2/27) - - 优化数据库写入\修改的速度 - - 精减任务实体的存储 - - 增加下载组合任务的配置 - - useBroadcast\notNetRetry这两个配置,统一在AppConfig中配置 - - fix bug https://github.com/AriaLyy/Aria/issues/361 - - fix bug https://github.com/AriaLyy/Aria/issues/365 - + v_3.5.4 (2019/1/8) - - 修复不支持断点的下载地址,重复下载出现的数据库主键冲突问题 - + v_3.5.3 (2018/12/23) - - 修复ftps不能加载默认证书的bug https://github.com/AriaLyy/Aria/issues/334 - - 优化注解性能,移除不必要的判断代码 - - 增加广播支持,详情见:http://aria.laoyuyu.me/aria_doc/api/use_broadcast.html - - 增加get参数支持 - ```java - Aria.download(SingleTaskActivity.this) - .load(DOWNLOAD_URL) // url 必须是主体url,也就是?前面的内容 - .setFilePath(path, true) - .asGet() - .setParams(params) // 设置参数 - .start(); - ``` - - fix bug https://github.com/AriaLyy/Aria/issues/335 - - 新增进度百分比保存 https://github.com/AriaLyy/Aria/issues/336 - - fix bug https://github.com/AriaLyy/Aria/issues/335 - + v_3.5.2 - - 添加Serializable接口支持 https://github.com/AriaLyy/Aria/issues/320 - - 失败回调增加错误原因 https://github.com/AriaLyy/Aria/issues/310 - ``` - @Download.onTaskFail void taskFail(DownloadTask task, Exception e) { - e.getMessage(); - ... - } - ``` - - fix bug https://github.com/AriaLyy/Aria/issues/322 - - 新增201 重定向支持 https://github.com/AriaLyy/Aria/issues/318 - - 修复使用`useServerFileName(true)`中含有`"`导致的文件后缀名错误问题 - - 优化logcat日志提示 - - 修改下载线程的优先级为`Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);` - - fix bug https://github.com/AriaLyy/Aria/issues/319 - - 修复分卡下载失败的问题 https://github.com/AriaLyy/Aria/issues/326 - - 初始化Aria时会将所有数据库状态为下载中的任务改为已停止,防止应用被kill后,任务状态错误 - - 初始化时自动判断文件是否被删除,文件被删除的任务将自动重置默认值 - - 修复刷新url后,文件无法删除的 bug - - fix bug https://github.com/AriaLyy/Aria/issues/309 - - 优化配置文件的读取 - + v_3.5.1 - - 优化`taskExists`方法 - - 添加`post`参数请求支持 - ```java - Aria.download(SingleTaskActivity.this) - .load(DOWNLOAD_URL) - .setFilePath(path) - .asPost() // post请求 - .setParam("key", "value") //传递参数 - //.setParams(Map) // 传递多参数 - .start(); - ``` - - 增加强制设置文件路径的api, https://github.com/AriaLyy/Aria/issues/311 - ``` - Aria.download(SingleTaskActivity.this) - .load(DOWNLOAD_URL) - .setFilePath(path, true) // true表示忽略路径是否被占用 - .start(); - ``` - + v_3.5 - - fix bug https://github.com/AriaLyy/Aria/issues/302 - - fix bug https://github.com/AriaLyy/Aria/issues/283 - - fix bug https://github.com/AriaLyy/Aria/issues/305 - - fix bug https://github.com/AriaLyy/Aria/issues/306 - - fix bug https://github.com/AriaLyy/Aria/issues/272 (现在,停止所有任务,未开始的任务状态将变为停止) - - fix bug https://github.com/AriaLyy/Aria/issues/277 - - fix bug https://github.com/AriaLyy/Aria/issues/303 - - 优化停止任务的速度 - - 修复组合任务修改子任务文件名失败的问题 - + v_3.4.12 - - fix bug https://github.com/AriaLyy/Aria/issues/286 - - 优化线程池任务 - + v_3.4.11 - - fix bug https://github.com/AriaLyy/Aria/issues/288 - - fix bug https://github.com/AriaLyy/Aria/issues/282 - + v_3.4.10 - - fix bug https://github.com/AriaLyy/Aria/issues/280 - + v_3.4.9 - - fix bug https://github.com/AriaLyy/Aria/issues/276 - + v_3.4.8 - - 组合任务新增`updateUrls(List)`用于修改组合子任务的url,[see](https://aria.laoyuyu.me/aria_doc/api/update_url.html) - - 出于安全考虑,FTP数据库去掉密码的保存 - - 增加FTPS支持 [see](https://aria.laoyuyu.me/aria_doc/download/ftps.html) - - 增加速度限制支持[see](https://aria.laoyuyu.me/aria_doc/api/speed_handle.html) - - 增加内存空间不足验证 - + v_3.4.7 - - 修复分块任务异常操作导致的问题 - + v_3.4.6 - - 修复android 4.4.4 版本多dex下无法进行回调的问题 - - 新增`updateUrl(newUrl)`用于修改任务的url,[see](https://aria.laoyuyu.me/aria_doc/api/task_handle.html#%E6%9B%B4%E6%96%B0%E4%BB%BB%E5%8A%A1url) - - 优化分块下载 - - 修复了字符串中有特殊字符导致的路径冲突问题;修复ftp分块下载失败问题 - - 修复连接中有`+`导致的地址呗使用问题。 - - 修复表重复创建导致的崩溃问题 https://github.com/AriaLyy/Aria/issues/264 - + v_3.4.4 - - 实现[多线程分块下载](https://aria.laoyuyu.me/aria_doc/start/config.html) - - 修复`stopAll()`和`resumeAll()`导致的进度为0问题 - - 修复任务组添加header无效的问题 - + v_3.4.3 - - 修复在activity 的onStop中取消注册导致的内存泄露问题 - - fix bug https://github.com/AriaLyy/Aria/issues/258 - - fix bug https://github.com/AriaLyy/Aria/issues/259 - + v_3.4.2 - - fix bug https://github.com/AriaLyy/Aria/issues/248 - - fix bug https://github.com/AriaLyy/Aria/issues/247 - - fix bug https://github.com/AriaLyy/Aria/issues/250 - - 添加任务判断是否存在的api - - 添加代理api - - 修复删除所有没有进出等待的问题 - - 进度有时出错的问题 - - FTP添加超时处理 - + v_3.4.1 - - 移除记录配置文件,改用数据库记录任务记录 - - 上传配置添加io超时时间、缓存大小配置 - - 添加没有网络也会重试的开关 - - 修复多次删除记录的bug - - 文件长度现在可动态增加,详情见 https://aria.laoyuyu.me/aria_doc/start/config.html - - 修复多module同时引用Aria导致打正式包出错的问题 https://github.com/AriaLyy/Aria/issues/240 - + v_3.4 - - 优化大量代码 - - 重构Aria的ORM模型,提高了数据读取的可靠性和读写速度 - - 现在可在任意类中使用Aria了,[使用方法](http://aria.laoyuyu.me/aria_doc/start/any_java.html) - - 添加`window.location.replace("http://xxxx")`类型的网页重定向支持 - - 支持gzip、deflate 压缩类型的输入流 - - 添加`useServerFileName`,可使用服务端响应header的`Content-Disposition`携带的文件名 - + v_3.3.16 - - 修复一个activity启动多次,无法进行回掉的bug https://github.com/AriaLyy/Aria/issues/200 - - 优化target代码结构,移除路径被占用的提示 - - 添加支持chunked模式的下载 - - 去掉上一个版本"//"的限制 - + v_3.3.14 - - 修复ftp上传和下载的兼容性问题 - - 如果url中的path有"//"将替换为"/" - - 修复http上传成功后,如果服务器没有设置返回码导致上传失败的问题 - - 上传实体UploadEntity增加responseStr字段,http上传完成后,在被`@Upload.onComplete`注解的方法中,可通过`task.getEntity().getResponseStr())`获取服务器返回的数据 - - 如果服务器存在已上传了一部分的文件,用户执行删除该FTP上传任务,再次重新上传,会出现550,权限错误;本版本已修复该问题 - + v_3.3.13 - - 添加`@Download.onWait、@Upload.onWait、@DownloadGroup.onWait`三个新注解,队列已经满了,继续创建新任务,任务处于等待中,将会执行被这三个注解标志的方法 - - app被kill,但是还存在等待中的任务A;第二次重新启动,先创建一个新的任务B,Aria会自动把B放进等待队列中,这时再次创建任务A,会导致重复下载,进度错乱的问题;本版本已修复这个问题 - + v_3.3.11 - - 添加进度更新间隔api,在`aria_config.xml`配置``或在代码中调用 - `AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setUpdateInterval(3000)`便可以改变进度刷新间隔 - - 修复下载过程中kill进程可能出现的文件错误的问题 https://github.com/AriaLyy/Aria/issues/192 - - 修复http上传的空指针问题 https://github.com/AriaLyy/Aria/issues/193 - - 修复下载地址中含有`'`导致的崩溃问题 https://github.com/AriaLyy/Aria/issues/194 - + v_3.3.10 - - 修复地址切换导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/181 - - 添加重置状态的api,当下载信息不改变,只是替换了服务器的对应的文件,可用`Aria.download(this).load(url).resetState()`重置下载状态 https://github.com/AriaLyy/Aria/issues/182 - + v_3.3.9 - - 添加POST支持 - - 任务执行的过程中,如果调用removeRecord()方法,将会取消任务 https://github.com/AriaLyy/Aria/issues/174 - - 修复一个数据库初始化的问题 https://github.com/AriaLyy/Aria/issues/173 - - 修复head头部信息过长时出现的崩溃问题 https://github.com/AriaLyy/Aria/issues/177 - + v_3.3.7 - - 修复一个线程重启的问题 https://github.com/AriaLyy/Aria/issues/160 - - 修复配置文件异常问题、格式化速度为0问题 https://github.com/AriaLyy/Aria/issues/161 - + v_3.3.6 - - 增加日志输出级别控制 - - 修复公网FTP地址不能下载的问题 https://github.com/AriaLyy/Aria/issues/146 - - 修复http下载地址有空格的时候下载失败的问题 https://github.com/AriaLyy/Aria/issues/131 - - 修复Activity在`onDestroy()`中调用`Aria.download(this).unRegister();`导致回调失效的问题 - - 修复Adapter下载FTP任务问题、任务调度问题 https://github.com/AriaLyy/Aria/issues/157 - - 优化代码,优化了IO性能 - + v_3.3.5 修复任务组、上传任务无法启动的bug - + v_3.3.4 优化任务代码结构,修复上一个版本暂停后无法自动执行任务的问题 - + v_3.3.3 修复进度条错乱的问题,修复同一时间多次调用start导致重复下载的问题 - + v_3.3.2 新加reTry(),修复上一个版本不会回调失败事件的问题;增加running状态下5秒钟保存一次数据库的功能;修复FTP断点上传文件不完整的问题 - + v_3.3.1 增加网络事件,网络未连接,将不会重试下载,修复删除未开始任务,状态回调错误 - + v_3.3.0 增加任务组子任务暂停和开始控制功能、修复5.0系统以上数据库多生成两个字段的bug、去掉addSchedulerListener事件 - + v_3.2.26 修复任务组有时注解不起作用的问题 - + v_3.2.25 修复删除任务组文件,记录无法删除的问题 - + v_3.2.17 修复一个版本兼容性问题,线程中使用Aria出错问题 - + v_3.2.15 修复大型文件分段下载失败的问题,修复中文URL乱码问题 - + v_3.2.14 修复恢复所有任务的api接口,不能恢复下载组任务的问题 - + v_3.2.13 修复某些服务器头文件返回描述文件格式错误的问题、修复有时删除任务,需要两次删除的问题 - + v_3.2.12 实现FTP多线程断点续传下载,FTP断点续传上传功能 - + v_3.2.9 修复任务组下载完成两次回掉的问题,修复又是获取不到下载状态的问题 - + v_3.2.8 修复下载超过2G大小的文件失败的问题 - + v_3.2.7 移除设置文件名的api接口,修复断开网络时出现的进度条错误的问题 - + v_3.2.6 移除广播事件,增加任务组下载功能 - + v_3.1.9 修复stopAll队列没有任务时崩溃的问题,增加针对单个任务监听的功能 - + v_3.1.7 修复某些文件下载不了的bug,增加apt注解方法,事件获取更加简单了 - + v_3.1.6 取消任务时onTaskCancel回调两次的bug - + v_3.1.5 优化代码结构,增加优先下载任务功能。 - + v_3.1.4 修复快速切换,暂停、恢复功能时,概率性出现的重新下载问题,添加onPre()回调,onPre()用于请求地址之前执行界面UI更新操作。 - + v_3.1.0 添加Aria配置文件,优化代码 - + v_3.0.3 修复暂停后删除任务,闪退问题,添加删除记录的api - + v_3.0.2 支持30x重定向链接下载 - + v_3.0.0 添加上传任务支持,修复一些已发现的bug - + v_2.4.4 修复不支持断点的下载链接拿不到文件大小的问题 - + v_2.4.3 修复404链接卡顿的问题 - + v_2.4.2 修复失败重试无效的bug - + v_2.4.1 修复下载慢的问题,修复application、service 不能使用的问题 - + v_2.4.0 支持https链接下载 - + v_2.3.8 修复数据错乱的bug、添加fragment支持 - + v_2.3.6 添加dialog、popupWindow支持 - + v_2.3.3 添加断点支持、修改下载逻辑,让使用更加简单、修复一个内存泄露的bug - + v_2.3.1 重命名为Aria,下载流程简化 +## 开发日志 + + v_3.6.4 + - 优化任务接收器的代码结构 + - 修复`DbEntity.saveAll()`失败的问题 + - 修复分块任务重命名失败的问题 + - fix bug https://github.com/AriaLyy/Aria/issues/379 + - 移除`getDownloadTask(String url)`、`getGroupTask(List urls)`、`getFtpDirTask(String path)` + 等获取任务的api,如果你希望获取对应状态,请使用实体的状态判断,如:`getDownloadEntity()`、`getDownloadGroupEntity()` + `getFtpDirEntity()` + - fix bug https://github.com/AriaLyy/Aria/issues/388 + - 修复使用`Content-Disposition`的文件名时,第一次下载无法重命名文件的问题 + - 修复使用`Content-Disposition`的文件名时,多次重命名文件的问题 + + v_3.6.3 (2019/4/2) + - fix bug https://github.com/AriaLyy/Aria/issues/377 + + v_3.6.2 (2019/4/1) + - fix bug https://github.com/AriaLyy/Aria/issues/368 + - 增加gradle 5.0支持 + - fix bug https://github.com/AriaLyy/Aria/issues/374 + - 增加分页功能,详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html#%E4%BB%BB%E5%8A%A1%E5%88%97%E8%A1%A8%E5%88%86%E9%A1%B5%EF%BC%88362%E4%BB%A5%E4%B8%8A%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81%EF%BC%89 + + v_3.6.1 (2019/3/5) + - fix bug https://github.com/AriaLyy/Aria/issues/367 + + v_3.6 (2019/2/27) + - 优化数据库写入\修改的速度 + - 精减任务实体的存储 + - 增加下载组合任务的配置 + - useBroadcast\notNetRetry这两个配置,统一在AppConfig中配置 + - fix bug https://github.com/AriaLyy/Aria/issues/361 + - fix bug https://github.com/AriaLyy/Aria/issues/365 + + v_3.5.4 (2019/1/8) + - 修复不支持断点的下载地址,重复下载出现的数据库主键冲突问题 + + v_3.5.3 (2018/12/23) + - 修复ftps不能加载默认证书的bug https://github.com/AriaLyy/Aria/issues/334 + - 优化注解性能,移除不必要的判断代码 + - 增加广播支持,详情见:http://aria.laoyuyu.me/aria_doc/api/use_broadcast.html + - 增加get参数支持 + ```java + Aria.download(SingleTaskActivity.this) + .load(DOWNLOAD_URL) // url 必须是主体url,也就是?前面的内容 + .setFilePath(path, true) + .asGet() + .setParams(params) // 设置参数 + .start(); + ``` + - fix bug https://github.com/AriaLyy/Aria/issues/335 + - 新增进度百分比保存 https://github.com/AriaLyy/Aria/issues/336 + - fix bug https://github.com/AriaLyy/Aria/issues/335 + + v_3.5.2 + - 添加Serializable接口支持 https://github.com/AriaLyy/Aria/issues/320 + - 失败回调增加错误原因 https://github.com/AriaLyy/Aria/issues/310 + ``` + @Download.onTaskFail void taskFail(DownloadTask task, Exception e) { + e.getMessage(); + ... + } + ``` + - fix bug https://github.com/AriaLyy/Aria/issues/322 + - 新增201 重定向支持 https://github.com/AriaLyy/Aria/issues/318 + - 修复使用`useServerFileName(true)`中含有`"`导致的文件后缀名错误问题 + - 优化logcat日志提示 + - 修改下载线程的优先级为`Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);` + - fix bug https://github.com/AriaLyy/Aria/issues/319 + - 修复分卡下载失败的问题 https://github.com/AriaLyy/Aria/issues/326 + - 初始化Aria时会将所有数据库状态为下载中的任务改为已停止,防止应用被kill后,任务状态错误 + - 初始化时自动判断文件是否被删除,文件被删除的任务将自动重置默认值 + - 修复刷新url后,文件无法删除的 bug + - fix bug https://github.com/AriaLyy/Aria/issues/309 + - 优化配置文件的读取 + + v_3.5.1 + - 优化`taskExists`方法 + - 添加`post`参数请求支持 + ```java + Aria.download(SingleTaskActivity.this) + .load(DOWNLOAD_URL) + .setFilePath(path) + .asPost() // post请求 + .setParam("key", "value") //传递参数 + //.setParams(Map) // 传递多参数 + .start(); + ``` + - 增加强制设置文件路径的api, https://github.com/AriaLyy/Aria/issues/311 + ```java + Aria.download(SingleTaskActivity.this) + .load(DOWNLOAD_URL) + .setFilePath(path, true) // true表示忽略路径是否被占用 + .start(); + ``` + + v_3.5 + - fix bug https://github.com/AriaLyy/Aria/issues/302 + - fix bug https://github.com/AriaLyy/Aria/issues/283 + - fix bug https://github.com/AriaLyy/Aria/issues/305 + - fix bug https://github.com/AriaLyy/Aria/issues/306 + - fix bug https://github.com/AriaLyy/Aria/issues/272 (现在,停止所有任务,未开始的任务状态将变为停止) + - fix bug https://github.com/AriaLyy/Aria/issues/277 + - fix bug https://github.com/AriaLyy/Aria/issues/303 + - 优化停止任务的速度 + - 修复组合任务修改子任务文件名失败的问题 + + v_3.4.12 + - fix bug https://github.com/AriaLyy/Aria/issues/286 + - 优化线程池任务 + + v_3.4.11 + - fix bug https://github.com/AriaLyy/Aria/issues/288 + - fix bug https://github.com/AriaLyy/Aria/issues/282 + + v_3.4.10 + - fix bug https://github.com/AriaLyy/Aria/issues/280 + + v_3.4.9 + - fix bug https://github.com/AriaLyy/Aria/issues/276 + + v_3.4.8 + - 组合任务新增`updateUrls(List)`用于修改组合子任务的url,[see](https://aria.laoyuyu.me/aria_doc/api/update_url.html) + - 出于安全考虑,FTP数据库去掉密码的保存 + - 增加FTPS支持 [see](https://aria.laoyuyu.me/aria_doc/download/ftps.html) + - 增加速度限制支持[see](https://aria.laoyuyu.me/aria_doc/api/speed_handle.html) + - 增加内存空间不足验证 + + v_3.4.7 + - 修复分块任务异常操作导致的问题 + + v_3.4.6 + - 修复android 4.4.4 版本多dex下无法进行回调的问题 + - 新增`updateUrl(newUrl)`用于修改任务的url,[see](https://aria.laoyuyu.me/aria_doc/api/task_handle.html#%E6%9B%B4%E6%96%B0%E4%BB%BB%E5%8A%A1url) + - 优化分块下载 + - 修复了字符串中有特殊字符导致的路径冲突问题;修复ftp分块下载失败问题 + - 修复连接中有`+`导致的地址呗使用问题。 + - 修复表重复创建导致的崩溃问题 https://github.com/AriaLyy/Aria/issues/264 + + v_3.4.4 + - 实现[多线程分块下载](https://aria.laoyuyu.me/aria_doc/start/config.html) + - 修复`stopAll()`和`resumeAll()`导致的进度为0问题 + - 修复任务组添加header无效的问题 + + v_3.4.3 + - 修复在activity 的onStop中取消注册导致的内存泄露问题 + - fix bug https://github.com/AriaLyy/Aria/issues/258 + - fix bug https://github.com/AriaLyy/Aria/issues/259 + + v_3.4.2 + - fix bug https://github.com/AriaLyy/Aria/issues/248 + - fix bug https://github.com/AriaLyy/Aria/issues/247 + - fix bug https://github.com/AriaLyy/Aria/issues/250 + - 添加任务判断是否存在的api + - 添加代理api + - 修复删除所有没有进出等待的问题 + - 进度有时出错的问题 + - FTP添加超时处理 + + v_3.4.1 + - 移除记录配置文件,改用数据库记录任务记录 + - 上传配置添加io超时时间、缓存大小配置 + - 添加没有网络也会重试的开关 + - 修复多次删除记录的bug + - 文件长度现在可动态增加,详情见 https://aria.laoyuyu.me/aria_doc/start/config.html + - 修复多module同时引用Aria导致打正式包出错的问题 https://github.com/AriaLyy/Aria/issues/240 + + v_3.4 + - 优化大量代码 + - 重构Aria的ORM模型,提高了数据读取的可靠性和读写速度 + - 现在可在任意类中使用Aria了,[使用方法](http://aria.laoyuyu.me/aria_doc/start/any_java.html) + - 添加`window.location.replace("http://xxxx")`类型的网页重定向支持 + - 支持gzip、deflate 压缩类型的输入流 + - 添加`useServerFileName`,可使用服务端响应header的`Content-Disposition`携带的文件名 + + v_3.3.16 + - 修复一个activity启动多次,无法进行回掉的bug https://github.com/AriaLyy/Aria/issues/200 + - 优化target代码结构,移除路径被占用的提示 + - 添加支持chunked模式的下载 + - 去掉上一个版本"//"的限制 + + v_3.3.14 + - 修复ftp上传和下载的兼容性问题 + - 如果url中的path有"//"将替换为"/" + - 修复http上传成功后,如果服务器没有设置返回码导致上传失败的问题 + - 上传实体UploadEntity增加responseStr字段,http上传完成后,在被`@Upload.onComplete`注解的方法中,可通过`task.getEntity().getResponseStr())`获取服务器返回的数据 + - 如果服务器存在已上传了一部分的文件,用户执行删除该FTP上传任务,再次重新上传,会出现550,权限错误;本版本已修复该问题 + + v_3.3.13 + - 添加`@Download.onWait、@Upload.onWait、@DownloadGroup.onWait`三个新注解,队列已经满了,继续创建新任务,任务处于等待中,将会执行被这三个注解标志的方法 + - app被kill,但是还存在等待中的任务A;第二次重新启动,先创建一个新的任务B,Aria会自动把B放进等待队列中,这时再次创建任务A,会导致重复下载,进度错乱的问题;本版本已修复这个问题 + + v_3.3.11 + - 添加进度更新间隔api,在`aria_config.xml`配置``或在代码中调用 + `AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setUpdateInterval(3000)`便可以改变进度刷新间隔 + - 修复下载过程中kill进程可能出现的文件错误的问题 https://github.com/AriaLyy/Aria/issues/192 + - 修复http上传的空指针问题 https://github.com/AriaLyy/Aria/issues/193 + - 修复下载地址中含有`'`导致的崩溃问题 https://github.com/AriaLyy/Aria/issues/194 + + v_3.3.10 + - 修复地址切换导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/181 + - 添加重置状态的api,当下载信息不改变,只是替换了服务器的对应的文件,可用`Aria.download(this).load(url).resetState()`重置下载状态 https://github.com/AriaLyy/Aria/issues/182 + + v_3.3.9 + - 添加POST支持 + - 任务执行的过程中,如果调用removeRecord()方法,将会取消任务 https://github.com/AriaLyy/Aria/issues/174 + - 修复一个数据库初始化的问题 https://github.com/AriaLyy/Aria/issues/173 + - 修复head头部信息过长时出现的崩溃问题 https://github.com/AriaLyy/Aria/issues/177 + + v_3.3.7 + - 修复一个线程重启的问题 https://github.com/AriaLyy/Aria/issues/160 + - 修复配置文件异常问题、格式化速度为0问题 https://github.com/AriaLyy/Aria/issues/161 + + v_3.3.6 + - 增加日志输出级别控制 + - 修复公网FTP地址不能下载的问题 https://github.com/AriaLyy/Aria/issues/146 + - 修复http下载地址有空格的时候下载失败的问题 https://github.com/AriaLyy/Aria/issues/131 + - 修复Activity在`onDestroy()`中调用`Aria.download(this).unRegister();`导致回调失效的问题 + - 修复Adapter下载FTP任务问题、任务调度问题 https://github.com/AriaLyy/Aria/issues/157 + - 优化代码,优化了IO性能 + + v_3.3.5 修复任务组、上传任务无法启动的bug + + v_3.3.4 优化任务代码结构,修复上一个版本暂停后无法自动执行任务的问题 + + v_3.3.3 修复进度条错乱的问题,修复同一时间多次调用start导致重复下载的问题 + + v_3.3.2 新加reTry(),修复上一个版本不会回调失败事件的问题;增加running状态下5秒钟保存一次数据库的功能;修复FTP断点上传文件不完整的问题 + + v_3.3.1 增加网络事件,网络未连接,将不会重试下载,修复删除未开始任务,状态回调错误 + + v_3.3.0 增加任务组子任务暂停和开始控制功能、修复5.0系统以上数据库多生成两个字段的bug、去掉addSchedulerListener事件 + + v_3.2.26 修复任务组有时注解不起作用的问题 + + v_3.2.25 修复删除任务组文件,记录无法删除的问题 + + v_3.2.17 修复一个版本兼容性问题,线程中使用Aria出错问题 + + v_3.2.15 修复大型文件分段下载失败的问题,修复中文URL乱码问题 + + v_3.2.14 修复恢复所有任务的api接口,不能恢复下载组任务的问题 + + v_3.2.13 修复某些服务器头文件返回描述文件格式错误的问题、修复有时删除任务,需要两次删除的问题 + + v_3.2.12 实现FTP多线程断点续传下载,FTP断点续传上传功能 + + v_3.2.9 修复任务组下载完成两次回掉的问题,修复又是获取不到下载状态的问题 + + v_3.2.8 修复下载超过2G大小的文件失败的问题 + + v_3.2.7 移除设置文件名的api接口,修复断开网络时出现的进度条错误的问题 + + v_3.2.6 移除广播事件,增加任务组下载功能 + + v_3.1.9 修复stopAll队列没有任务时崩溃的问题,增加针对单个任务监听的功能 + + v_3.1.7 修复某些文件下载不了的bug,增加apt注解方法,事件获取更加简单了 + + v_3.1.6 取消任务时onTaskCancel回调两次的bug + + v_3.1.5 优化代码结构,增加优先下载任务功能。 + + v_3.1.4 修复快速切换,暂停、恢复功能时,概率性出现的重新下载问题,添加onPre()回调,onPre()用于请求地址之前执行界面UI更新操作。 + + v_3.1.0 添加Aria配置文件,优化代码 + + v_3.0.3 修复暂停后删除任务,闪退问题,添加删除记录的api + + v_3.0.2 支持30x重定向链接下载 + + v_3.0.0 添加上传任务支持,修复一些已发现的bug + + v_2.4.4 修复不支持断点的下载链接拿不到文件大小的问题 + + v_2.4.3 修复404链接卡顿的问题 + + v_2.4.2 修复失败重试无效的bug + + v_2.4.1 修复下载慢的问题,修复application、service 不能使用的问题 + + v_2.4.0 支持https链接下载 + + v_2.3.8 修复数据错乱的bug、添加fragment支持 + + v_2.3.6 添加dialog、popupWindow支持 + + v_2.3.3 添加断点支持、修改下载逻辑,让使用更加简单、修复一个内存泄露的bug + + v_2.3.1 重命名为Aria,下载流程简化 + v_2.1.1 增加,选择最大下载任务数接口 \ No newline at end of file diff --git a/README.md b/README.md index 0d833323..2d4a24d0 100644 --- a/README.md +++ b/README.md @@ -1,172 +1,172 @@ -# Aria -![图标](https://github.com/AriaLyy/DownloadUtil/blob/v_3.0/app/src/main/res/mipmap-hdpi/ic_launcher.png)
-## [ENGLISH DOC](https://github.com/AriaLyy/Aria/blob/master/ENGLISH_README.md)
-## [中文文档](https://aria.laoyuyu.me/aria_doc) -Aria项目源于工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标了。 - -Aria有以下特点: - + 简单、方便 - - 可以在Activity、Service、Fragment、Dialog、popupWindow、Notification等组件中使用 - - 支持HTTP\FTP断点续传下载、多任务自动调度 - - 支持多文件打包下载,多文件共享同一进度(如:视频 + 封面 + 字幕) - - 支持下载FTP文件夹 - - 支持HTTP表单上传 - - 支持文件FTP断点续传上传 - - 支持FTPS/SFTP断点续传,[see](https://aria.laoyuyu.me/aria_doc/download/ftps.html) - + 支持https地址下载 - - 在配置文件中很容易就可以设置CA证书的信息 - + 支持[多线程分块下载](https://aria.laoyuyu.me/aria_doc/start/config.html),能更有效的发挥机器IO性能 - + 支持300、301、302重定向下载链接下载 - + 下载支持文件长度动态增加,文件下载初始化时将不再占用过多的内存空间,见[动态长度配置](https://aria.laoyuyu.me/aria_doc/start/config.html#%E4%B8%8B%E8%BD%BD%E5%8A%A8%E6%80%81%E6%96%87%E4%BB%B6%E8%AF%B4%E6%98%8E) - -[怎样使用Aria?](#使用) - -如果你觉得Aria对你有帮助,您的star和issues将是对我最大支持.`^_^` - -## 示例 -* 多任务下载 - -![多任务下载](https://github.com/AriaLyy/DownloadUtil/blob/master/img/download_img.gif) - -* 速度限制 - -![网速下载限制](https://github.com/AriaLyy/DownloadUtil/blob/master/img/max_speed.gif) - -* 多文件打包下载 - - - - -## 引入库 -[![Core](https://api.bintray.com/packages/arialyy/maven/AriaApi/images/download.svg)](https://bintray.com/arialyy/maven/AriaApi/_latestVersion) -[![Compiler](https://api.bintray.com/packages/arialyy/maven/AriaCompiler/images/download.svg)](https://bintray.com/arialyy/maven/AriaCompiler/_latestVersion) - -```java -compile 'com.arialyy.aria:aria-core:3.6.3' -annotationProcessor 'com.arialyy.aria:aria-compiler:3.6.3' -``` -如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:aria-core:'`替换为 -``` -api('com.arialyy.aria:aria-core:'){ - exclude group: 'com.android.support' -} -``` -如果你使用的是kotlin,请使用kotlin官方提供的方法配置apt,[kotlin kapt官方配置传送门](https://www.kotlincn.net/docs/reference/kapt.html) - -__注意:3.5.4以下版本升级时,需要更新[配置文件]!!(https://aria.laoyuyu.me/aria_doc/start/config.html)__ - -*** -## 使用 -由于Aria涉及到文件和网络的操作,因此需要你在manifest文件中添加以下权限,如果你希望在6.0以上的系统中使用Aria,那么你需要动态向安卓系统申请文件系统读写权限,[如何使用安卓系统权限](https://developer.android.com/training/permissions/index.html?hl=zh-cn) -```xml - - - - -``` - -## 使用Aria -### 基本使用 -例子为单任务下载,只需要很简单的代码,便可以实现下载功能 - ```java - Aria.download(this) - .load(DOWNLOAD_URL) //读取下载地址 - .setDownloadPath(DOWNLOAD_PATH) //设置文件保存的完整路径 - .start(); //启动下载 - ``` - -### 任务状态的获取 -基于解耦合的考虑,Aria的下载功能是和状态获取相分离的,状态的获取并不会集成到链式代码中,但是Aria提供了另一种更简单更灵活的方案。 -通过注解,你可以很容易获取任务的所有状态。 - -1. 将对象注册到Aria -```java -protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Aria.download(this).register(); -} -``` - -2. 通过注解获取任务执行状态 - **注意:** - - 注解回掉采用Apt的方式实现,所以,你不需要担心这会影响你机器的性能 - - 被注解的方法**不能被private修饰** - - 被注解的方法**只能有一个参数,并且参数类型必须是`DownloadTask`或`UploadTask`或`DownloadGroupTask`** - - 方法名可以为任意字符串 - -```java -//在这里处理任务执行中的状态,如进度进度条的刷新 -@Download.onTaskRunning protected void running(DownloadTask task) { - if(task.getUrl().eques(url)){ - .... - 可以通过url判断是否是指定任务的回调 - } - int p = task.getPercent(); //任务进度百分比 - String speed = task.getConvertSpeed(); //转换单位后的下载速度,单位转换需要在配置文件中打开 - String speed1 = task.getSpeed(); //原始byte长度速度 -} - -@Download.onTaskComplete void taskComplete(DownloadTask task) { - //在这里处理任务完成的状态 -} -``` - - -### 版本日志 -* v3.6.3 (2019/4/2) - - fix bug https://github.com/AriaLyy/Aria/issues/368 - - 增加gradle 5.0支持 - - fix bug https://github.com/AriaLyy/Aria/issues/374 - - fix bug https://github.com/AriaLyy/Aria/issues/377 - - 增加分页获取任务列表api, 详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html#%E4%BB%BB%E5%8A%A1%E5%88%97%E8%A1%A8%E5%88%86%E9%A1%B5%EF%BC%88362%E4%BB%A5%E4%B8%8A%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81%EF%BC%89 - -[更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) - -## 混淆配置 -``` --dontwarn com.arialyy.aria.** --keep class com.arialyy.aria.**{*;} --keep class **$$DownloadListenerProxy{ *; } --keep class **$$UploadListenerProxy{ *; } --keep class **$$DownloadGroupListenerProxy{ *; } --keepclasseswithmembernames class * { - @Download.* ; - @Upload.* ; - @DownloadGroup.* ; -} - -``` - -## 其他 - 有任何问题,可以在[issues](https://github.com/AriaLyy/Aria/issues)给我留言反馈。
- 在提交问题前,希望你已经查看过[wiki](https://aria.laoyuyu.me/aria_doc/)或搜索过[issues](https://github.com/AriaLyy/Aria/issues)。
- 交流群:524329160 - -*** - -License -------- - - Copyright 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. - - - - - - - - - - +# Aria +![图标](https://github.com/AriaLyy/DownloadUtil/blob/v_3.0/app/src/main/res/mipmap-hdpi/ic_launcher.png)
+## [ENGLISH DOC](https://github.com/AriaLyy/Aria/blob/master/ENGLISH_README.md)
+## [中文文档](https://aria.laoyuyu.me/aria_doc) +Aria项目源于工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标了。 + +Aria有以下特点: + + 简单、方便 + - 可以在Activity、Service、Fragment、Dialog、popupWindow、Notification等组件中使用 + - 支持HTTP\FTP断点续传下载、多任务自动调度 + - 支持多文件打包下载,多文件共享同一进度(如:视频 + 封面 + 字幕) + - 支持下载FTP文件夹 + - 支持HTTP表单上传 + - 支持文件FTP断点续传上传 + - 支持FTPS/SFTP断点续传,[see](https://aria.laoyuyu.me/aria_doc/download/ftps.html) + + 支持https地址下载 + - 在配置文件中很容易就可以设置CA证书的信息 + + 支持[多线程分块下载](https://aria.laoyuyu.me/aria_doc/start/config.html),能更有效的发挥机器IO性能 + + 支持300、301、302重定向下载链接下载 + + 下载支持文件长度动态增加,文件下载初始化时将不再占用过多的内存空间,见[动态长度配置](https://aria.laoyuyu.me/aria_doc/start/config.html#%E4%B8%8B%E8%BD%BD%E5%8A%A8%E6%80%81%E6%96%87%E4%BB%B6%E8%AF%B4%E6%98%8E) + +[怎样使用Aria?](#使用) + +如果你觉得Aria对你有帮助,您的star和issues将是对我最大支持.`^_^` + +## 示例 +* 多任务下载 + +![多任务下载](https://github.com/AriaLyy/DownloadUtil/blob/master/img/download_img.gif) + +* 速度限制 + +![网速下载限制](https://github.com/AriaLyy/DownloadUtil/blob/master/img/max_speed.gif) + +* 多文件打包下载 + + + + +## 引入库 +[![Core](https://api.bintray.com/packages/arialyy/maven/AriaApi/images/download.svg)](https://bintray.com/arialyy/maven/AriaApi/_latestVersion) +[![Compiler](https://api.bintray.com/packages/arialyy/maven/AriaCompiler/images/download.svg)](https://bintray.com/arialyy/maven/AriaCompiler/_latestVersion) + +```java +compile 'com.arialyy.aria:aria-core:3.6.3' +annotationProcessor 'com.arialyy.aria:aria-compiler:3.6.3' +``` +如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:aria-core:'`替换为 +``` +api('com.arialyy.aria:aria-core:'){ + exclude group: 'com.android.support' +} +``` +如果你使用的是kotlin,请使用kotlin官方提供的方法配置apt,[kotlin kapt官方配置传送门](https://www.kotlincn.net/docs/reference/kapt.html) + +__注意:3.5.4以下版本升级时,需要更新[配置文件]!!(https://aria.laoyuyu.me/aria_doc/start/config.html)__ + +*** +## 使用 +由于Aria涉及到文件和网络的操作,因此需要你在manifest文件中添加以下权限,如果你希望在6.0以上的系统中使用Aria,那么你需要动态向安卓系统申请文件系统读写权限,[如何使用安卓系统权限](https://developer.android.com/training/permissions/index.html?hl=zh-cn) +```xml + + + + +``` + +## 使用Aria +### 基本使用 +例子为单任务下载,只需要很简单的代码,便可以实现下载功能 + ```java + Aria.download(this) + .load(DOWNLOAD_URL) //读取下载地址 + .setDownloadPath(DOWNLOAD_PATH) //设置文件保存的完整路径 + .start(); //启动下载 + ``` + +### 任务状态的获取 +基于解耦合的考虑,Aria的下载功能是和状态获取相分离的,状态的获取并不会集成到链式代码中,但是Aria提供了另一种更简单更灵活的方案。 +通过注解,你可以很容易获取任务的所有状态。 + +1. 将对象注册到Aria +```java +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Aria.download(this).register(); +} +``` + +2. 通过注解获取任务执行状态 + **注意:** + - 注解回掉采用Apt的方式实现,所以,你不需要担心这会影响你机器的性能 + - 被注解的方法**不能被private修饰** + - 被注解的方法**只能有一个参数,并且参数类型必须是`DownloadTask`或`UploadTask`或`DownloadGroupTask`** + - 方法名可以为任意字符串 + +```java +//在这里处理任务执行中的状态,如进度进度条的刷新 +@Download.onTaskRunning protected void running(DownloadTask task) { + if(task.getKey().eques(url)){ + .... + 可以通过url判断是否是指定任务的回调 + } + int p = task.getPercent(); //任务进度百分比 + String speed = task.getConvertSpeed(); //转换单位后的下载速度,单位转换需要在配置文件中打开 + String speed1 = task.getSpeed(); //原始byte长度速度 +} + +@Download.onTaskComplete void taskComplete(DownloadTask task) { + //在这里处理任务完成的状态 +} +``` + + +### 版本日志 +* v3.6.3 (2019/4/2) + - fix bug https://github.com/AriaLyy/Aria/issues/368 + - 增加gradle 5.0支持 + - fix bug https://github.com/AriaLyy/Aria/issues/374 + - fix bug https://github.com/AriaLyy/Aria/issues/377 + - 增加分页获取任务列表api, 详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html#%E4%BB%BB%E5%8A%A1%E5%88%97%E8%A1%A8%E5%88%86%E9%A1%B5%EF%BC%88362%E4%BB%A5%E4%B8%8A%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81%EF%BC%89 + +[更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) + +## 混淆配置 +``` +-dontwarn com.arialyy.aria.** +-keep class com.arialyy.aria.**{*;} +-keep class **$$DownloadListenerProxy{ *; } +-keep class **$$UploadListenerProxy{ *; } +-keep class **$$DownloadGroupListenerProxy{ *; } +-keepclasseswithmembernames class * { + @Download.* ; + @Upload.* ; + @DownloadGroup.* ; +} + +``` + +## 其他 + 有任何问题,可以在[issues](https://github.com/AriaLyy/Aria/issues)给我留言反馈。
+ 在提交问题前,希望你已经查看过[wiki](https://aria.laoyuyu.me/aria_doc/)或搜索过[issues](https://github.com/AriaLyy/Aria/issues)。
+ 交流群:524329160 + +*** + +License +------- + + Copyright 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. + + + + + + + + + + diff --git a/app/src/main/assets/aria_config.xml b/app/src/main/assets/aria_config.xml index 40e41cb1..a9d5ccfb 100644 --- a/app/src/main/assets/aria_config.xml +++ b/app/src/main/assets/aria_config.xml @@ -1,161 +1,161 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index d18c7291..3d280298 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -31,6 +31,7 @@ import android.widget.Toast; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTarget; import com.arialyy.aria.core.download.DownloadTask; @@ -57,14 +58,11 @@ public class SingleTaskActivity extends BaseActivity { //"http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apks"; //"http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip"; //"https://yizi-kejian.oss-cn-beijing.aliyuncs.com/qimeng/package1/qmtable11.zip"; - "http://rs.0.gaoshouyou.com/d/04/1e/400423a7551e1f3f0eb1812afa1f9b44.apk"; + //"http://rs.0.gaoshouyou.com/d/04/1e/400423a7551e1f3f0eb1812afa1f9b44.apk"; + //"http://chargepile2.techsum.net/car-manage/file/download?path=2019-04-26/c0242efd18be4ecbb23911b1c509dcad--掌通各系统汇总.xls"; // 无长度的chunked //"http://58.210.9.131/tpk/sipgt//TDLYZTGH.tpk"; //chunked 下载 - //"https://static.donguo.me/video/ip/course/pfys_1.mp4"; - //"https://www.baidu.com/link?url=_LFCuTPtnzFxVJByJ504QymRywIA1Z_T5xUxe9ZLuxcGM0C_RcdpWyB1eGjbJC-e5wv5wAKM4WmLMAS5KeF6EZJHB8Va3YqZUiaErqK_pxm&wd=&eqid=e8583fe70002d126000000065a99f864"; - //"https://d.pcs.baidu.com/file/a02c89a2d479d4fd2756f3313d42491d?fid=4232431903-250528-1114369760340736&dstime=1525491372&rt=sh&sign=FDtAERVY-DCb740ccc5511e5e8fedcff06b081203-3C13vkOkuk4TqXvVYW05zj1K0ao%3D&expires=8h&chkv=1&chkbd=0&chkpc=et&dp-logid=8651730921842106225&dp-callid=0&r=165533013"; - //"http://apk500.bce.baidu-mgame.com/game/67000/67734/20170622040827_oem_5502845.apk?r=1"; + "http://apk500.bce.baidu-mgame.com/game/67000/67734/20170622040827_oem_5502845.apk?r=1"; //"https://dl.genymotion.com/releases/genymotion-2.12.1/genymotion-2.12.1-vbox.exe"; - //"http://9.9.9.50:5000/download1"; //"http://9.9.9.50:5000/download/CentOS-7-x86_64-Minimal-1804.iso"; //"http://v2.qingdian1.com/m_20180730_991/2/2B9FB34A4BCD8CE61481D1C8418EFE36_1080P.m3u8"; //"https://firmwareapi.azurewebsites.net/firmware-overview?name=A19_Filament_W_IMG0038_00102411-encrypted.ota"; @@ -236,18 +234,17 @@ public class SingleTaskActivity extends BaseActivity { } /** - * @param task - * @param e + * */ @Download.onTaskFail void taskFail(DownloadTask task, Exception e) { if (task.getKey().equals(DOWNLOAD_URL)) { Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show(); setBtState(true); - Aria.download(this) - .load(DOWNLOAD_URL) - .updateUrl("http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip") - .start(); + //Aria.download(this) + // .load(DOWNLOAD_URL) + // .updateUrl("http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip") + // .start(); //DOWNLOAD_URL = "http://120.55.95.61:8811/ghcg/zg/武义总规纲要成果.zip"; //ALog.d(TAG, ALog.getExceptionString(e)); } @@ -262,9 +259,11 @@ public class SingleTaskActivity extends BaseActivity { //mCancel.setEnabled(false); setBtState(true); getBinding().setSpeed(""); - L.d(TAG, "path ==> " + task.getDownloadEntity().getDownloadPath()); - L.d(TAG, "md5Code ==> " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); - L.d(TAG, "data ==> " + Aria.download(this).getDownloadEntity(DOWNLOAD_URL)); + L.d(TAG, "path = " + task.getDownloadEntity().getDownloadPath()); + L.d(TAG, "md5Code = " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); + L.d(TAG, "data = " + Aria.download(this).getDownloadEntity(DOWNLOAD_URL)); + DownloadEntity temp = Aria.download(this).getDownloadEntity(DOWNLOAD_URL); + L.d(TAG, "status = " + temp.getState() + ", isComplete = " + temp.isComplete()); //Intent install = new Intent(Intent.ACTION_VIEW); //install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //File apkFile = new File(task.getDownloadPath()); @@ -286,8 +285,6 @@ public class SingleTaskActivity extends BaseActivity { return R.layout.activity_single; } - - public void onClick(View view) { switch (view.getId()) { case R.id.start: @@ -318,9 +315,9 @@ public class SingleTaskActivity extends BaseActivity { //params.put("filename", "CentOS-7-x86_64-Minimal-1804.iso"); Aria.download(SingleTaskActivity.this) .load(DOWNLOAD_URL) - //.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8") - //.addHeader("Accept-Encoding", "gzip, deflate") - //.addHeader("DNT", "1") + //.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3") + .addHeader("Accept-Encoding", "gzip, deflate") + .addHeader("DNT", "1") //.addHeader("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0") .useServerFileName(true) .setFilePath(path, true) 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 8c31b312..22680684 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 @@ -48,16 +48,15 @@ public class DownloadGroupActivity extends BaseActivity temp = new ArrayList<>(); - temp.add("https://d.pcs.baidu.com/file/130335545f3f4d9cc38afe709c19af5a?fid=1411168371-250528-1010657263806840&dstime=1531134714&rt=sh&sign=FDtAERVY-DCb740ccc5511e5e8fedcff06b081203-h8KgJ6gl4oY9UR6NqvwJsT4nVSM%3D&expires=8h&chkv=1&chkbd=0&chkpc=et&dp-logid=4401996296756616039&dp-callid=0&r=279987343"); + temp.add( + "https://d.pcs.baidu.com/file/130335545f3f4d9cc38afe709c19af5a?fid=1411168371-250528-1010657263806840&dstime=1531134714&rt=sh&sign=FDtAERVY-DCb740ccc5511e5e8fedcff06b081203-h8KgJ6gl4oY9UR6NqvwJsT4nVSM%3D&expires=8h&chkv=1&chkbd=0&chkpc=et&dp-logid=4401996296756616039&dp-callid=0&r=279987343"); Aria.download(this) .loadGroup(mUrls) .setDirPath( diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java index e8283795..b76af385 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java @@ -36,23 +36,22 @@ import com.arialyy.simple.widget.SubStateLinearLayout; public class FTPDirDownloadActivity extends BaseActivity { private static final String dir = "ftp://9.9.9.50:21/upload/测试"; - SubStateLinearLayout mChildList; + SubStateLinearLayout mChildList; @Override protected void init(Bundle savedInstanceState) { super.init(savedInstanceState); Aria.download(this).register(); setTitle("FTP文件夹下载"); mChildList = findViewById(R.id.child_list); - DGTaskWrapper entity = Aria.download(this).getFtpDirTask(dir); - if (entity != null && entity.getEntity() != null) { - DownloadGroupEntity groupEntity = entity.getEntity(); - mChildList.addData(groupEntity.getSubEntities()); - getBinding().setFileSize(groupEntity.getConvertFileSize()); - if (groupEntity.getFileSize() == 0) { + DownloadGroupEntity entity = Aria.download(this).getFtpDirEntity(dir); + if (entity != null) { + mChildList.addData(entity.getSubEntities()); + getBinding().setFileSize(entity.getConvertFileSize()); + if (entity.getFileSize() == 0) { getBinding().setProgress(0); } else { - getBinding().setProgress(groupEntity.isComplete() ? 100 - : (int) (groupEntity.getCurrentProgress() * 100 / groupEntity.getFileSize())); + getBinding().setProgress(entity.isComplete() ? 100 + : (int) (entity.getCurrentProgress() * 100 / entity.getFileSize())); } } } diff --git a/app/src/main/java/com/arialyy/simple/core/test/AnyRunActivity.java b/app/src/main/java/com/arialyy/simple/core/test/AnyRunActivity.java index 0f67d5ea..364fed2e 100644 --- a/app/src/main/java/com/arialyy/simple/core/test/AnyRunActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/test/AnyRunActivity.java @@ -43,8 +43,6 @@ public class AnyRunActivity extends BaseActivity { mBar.setVisibility(View.GONE); module = new AnyRunnModule(this); urls = getResources().getStringArray(R.array.group_urls); - DTaskWrapper entity = Aria.download(this).getDownloadTask(URL); - Log.d(TAG, entity + ""); } public void onClick(View view) { diff --git a/build.gradle b/build.gradle index 1512fb90..45691c54 100644 --- a/build.gradle +++ b/build.gradle @@ -1,58 +1,58 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - ext.kotlin_version = '1.3.11' - repositories { - jcenter() - mavenCentral() - google() - maven { url 'https://jitpack.io' } - } - dependencies { -// classpath 'com.android.tools.build:gradle:2.3.3' - classpath 'com.android.tools.build:gradle:3.3.2' - classpath 'com.novoda:bintray-release:0.9' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" - // classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' - // classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - jcenter() - mavenCentral() - google() - maven { url 'https://jitpack.io' } - } - - tasks.withType(Javadoc) { - options { - encoding "UTF-8" - charSet 'UTF-8' - links "http://docs.oracle.com/javase/7/docs/api" - } - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} - -ext { - userOrg = 'arialyy' - groupId = 'com.arialyy.aria' - publishVersion = '3.6.3' -// publishVersion = '1.0.4' //FTP插件 - repoName='maven' - desc = 'android 下载框架' - website = 'https://github.com/AriaLyy/Aria' - licences = ['Apache-2.0'] - - compileSdkVersion = 28 - supportLibVersion = "28.0.0" - buildToolsVersion = "28.0.3" - targetSdkVersion = 28 - minSdkVersion = 15 -} +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + ext.kotlin_version = '1.3.11' + repositories { + jcenter() + mavenCentral() + google() + maven { url 'https://jitpack.io' } + } + dependencies { +// classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.novoda:bintray-release:0.9' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" + // classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' + // classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + mavenCentral() + google() + maven { url 'https://jitpack.io' } + } + + tasks.withType(Javadoc) { + options { + encoding "UTF-8" + charSet 'UTF-8' + links "http://docs.oracle.com/javase/7/docs/api" + } + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} + +ext { + userOrg = 'arialyy' + groupId = 'com.arialyy.aria' + publishVersion = '3.6.4_dev_1' +// publishVersion = '1.0.4' //FTP插件 + repoName='maven' + desc = 'android 下载框架' + website = 'https://github.com/AriaLyy/Aria' + licences = ['Apache-2.0'] + + compileSdkVersion = 28 + supportLibVersion = "28.0.0" + buildToolsVersion = "28.0.3" + targetSdkVersion = 28 + minSdkVersion = 15 +}