diff --git a/.gitignore b/.gitignore index 6b0f8a6a..04f27198 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ /.idea .idea /cache -*.log \ No newline at end of file +*.log +uml \ No newline at end of file diff --git a/Aria/build.gradle b/Aria/build.gradle index f95e9d8f..18b2d152 100644 --- a/Aria/build.gradle +++ b/Aria/build.gradle @@ -16,6 +16,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + lintOptions { + abortOnError false + } } dependencies { @@ -23,8 +26,8 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.1' compile project(':AriaAnnotations') -// compile 'com.arialyy.aria:aria-ftp-plug:1.0.3' + compile 'com.arialyy.aria:aria-ftp-plug:1.0.3' - compile project(':AriaFtpPlug') + // compile project(':AriaFtpPlug') } apply from: 'bintray-release.gradle' diff --git a/Aria/src/main/java/com/arialyy/aria/core/Aria.java b/Aria/src/main/java/com/arialyy/aria/core/Aria.java index d6d681b7..10a40fa2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/Aria.java +++ b/Aria/src/main/java/com/arialyy/aria/core/Aria.java @@ -17,15 +17,8 @@ package com.arialyy.aria.core; import android.annotation.TargetApi; -import android.app.Activity; -import android.app.Application; -import android.app.Dialog; -import android.app.DialogFragment; -import android.app.Fragment; -import android.app.Service; import android.content.Context; import android.os.Build; -import android.widget.PopupWindow; import com.arialyy.aria.core.download.DownloadReceiver; import com.arialyy.aria.core.upload.UploadReceiver; @@ -51,6 +44,24 @@ import com.arialyy.aria.core.upload.UploadReceiver; * .start(); * * + * + * 如果你需要在【Activity、Service、Application、DialogFragment、Fragment、PopupWindow、Dialog】 + * 之外的java中使用Aria,那么你应该在Application或Activity初始化的时候调用{@link #init(Context)}对Aria进行初始化 + * 然后才能使用{@link #download(Object)}、{@link #upload(Object)} + * + *
+ *   
+ *       Aria.init(getContext());
+ *
+ *      Aria.download(this)
+ *       .load(URL)     //下载地址,必填
+ *       //文件保存路径,必填
+ *       .setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/test.apk")
+ *       .start();
+ *
+ *   
+ *
+ * 
*/ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) public class Aria { @@ -60,52 +71,53 @@ import com.arialyy.aria.core.upload.UploadReceiver; /** * 初始化下载 * - * @param obj 支持类型有【Activity、Service、Application、DialogFragment、Fragment、PopupWindow、Dialog】 + * @param context 支持类型有【Activity、Service、Application、DialogFragment、Fragment、PopupWindow、Dialog】 */ - public static DownloadReceiver download(Object obj) { - return get(obj).download(obj); + public static DownloadReceiver download(Context context) { + return get(context).download(context); } /** * 初始化上传 * - * @param obj 支持类型有【Activity、Service、Application、DialogFragment、Fragment、PopupWindow、Dialog】 + * @param context 支持类型有【Activity、Service、Application、DialogFragment、Fragment、PopupWindow、Dialog】 + */ + public static UploadReceiver upload(Context context) { + return get(context).upload(context); + } + + /** + * 在任意对象中初始化下载,前提是你需要在Application或Activity初始化的时候调用{@link #init(Context)}对Aria进行初始化 + * + * @param obj 任意对象 + */ + public static DownloadReceiver download(Object obj) { + return AriaManager.getInstance().download(obj); + } + + /** + * 在任意对象中初始化上传,前提是你需要在Application或Activity初始化的时候调用{@link #init(Context)}对Aria进行初始化 + * + * @param obj 任意对象 */ public static UploadReceiver upload(Object obj) { - return get(obj).upload(obj); + return AriaManager.getInstance().upload(obj); } /** * 处理通用事件 - * - * @param obj 支持类型有【Activity、Service、Application、DialogFragment、Fragment、PopupWindow、Dialog】 */ - public static AriaManager get(Object obj) { - if (obj instanceof Activity || obj instanceof Service || obj instanceof Application) { - return AriaManager.getInstance((Context) obj); - } else if (obj instanceof DialogFragment) { - DialogFragment dialog = (DialogFragment) obj; - return AriaManager.getInstance( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? dialog.getContext() - : dialog.getActivity()); - } else if (obj instanceof android.support.v4.app.Fragment) { - android.support.v4.app.Fragment fragment = (android.support.v4.app.Fragment) obj; - return AriaManager.getInstance( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? fragment.getContext() - : fragment.getActivity()); - } else if (obj instanceof Fragment) { - Fragment fragment = (Fragment) obj; - return AriaManager.getInstance( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? fragment.getContext() - : fragment.getActivity()); - } else if (obj instanceof PopupWindow) { - PopupWindow popupWindow = (PopupWindow) obj; - return AriaManager.getInstance(popupWindow.getContentView().getContext()); - } else if (obj instanceof Dialog) { - Dialog dialog = (Dialog) obj; - return AriaManager.getInstance(dialog.getContext()); - } else { - throw new IllegalArgumentException("不支持的类型"); - } + public static AriaManager get(Context context) { + return AriaManager.getInstance(context); + } + + /** + * 初始化Aria,如果你需要在【Activity、Service、Application、DialogFragment、Fragment、PopupWindow、Dialog】 + * 之外的java中使用Aria,那么你应该在Application或Activity初始化的时候调用本方法对Aria进行初始化 + * 只需要初始化一次就可以 + * {@link #download(Object)}、{@link #upload(Object)} + */ + public static AriaManager init(Context context) { + return AriaManager.getInstance(context); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index 9f7316c5..2fe50163 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -20,13 +20,11 @@ import android.annotation.TargetApi; import android.app.Activity; import android.app.Application; import android.app.Dialog; -import android.app.Service; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; -import android.text.TextUtils; import android.widget.PopupWindow; import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.common.QueueMod; @@ -41,8 +39,9 @@ import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadReceiver; import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.orm.DbUtil; +import com.arialyy.aria.orm.DelegateWrapper; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.AriaCrashHandler; import com.arialyy.aria.util.CommonUtil; import java.io.File; import java.io.IOException; @@ -75,12 +74,14 @@ import org.xml.sax.SAXException; private List mCommands = new ArrayList<>(); private Configuration.DownloadConfig mDConfig; private Configuration.UploadConfig mUConfig; + private Configuration.AppConfig mAConfig; private AriaManager(Context context) { - DbUtil.init(context.getApplicationContext()); + DelegateWrapper.init(context.getApplicationContext()); APP = context.getApplicationContext(); regAppLifeCallback(context); initConfig(); + initAria(); } public static AriaManager getInstance(Context context) { @@ -92,34 +93,53 @@ import org.xml.sax.SAXException; return INSTANCE; } - public Map getReceiver() { - return mReceivers; + static AriaManager getInstance() { + if (INSTANCE == null) { + throw new NullPointerException("请在Application或Activity初始化时调用一次Aria.init(context)方法进行初始化操作"); + } + return INSTANCE; } - /** - * 设置Aria 日志级别 - * - * @param level {@link ALog#LOG_LEVEL_VERBOSE} - */ - public void setLogLevel(int level) { - ALog.LOG_LEVEL = level; + private void initAria() { + if (mAConfig.getUseAriaCrashHandler()) { + Thread.setDefaultUncaughtExceptionHandler(new AriaCrashHandler()); + } + mAConfig.setLogLevel(mAConfig.getLogLevel()); + } + + public Map getReceiver() { + return mReceivers; } /** - * 设置上传任务的执行队列类型 + * 设置上传任务的执行队列类型,后续版本会删除该api,请使用: + *
+   *   
+   *     Aria.get(this).getUploadConfig().setQueueMod(mod.tag)
+   *   
+   * 
* * @param mod {@link QueueMod} + * @deprecated 后续版本会删除该api */ + @Deprecated public AriaManager setUploadQueueMod(QueueMod mod) { mUConfig.setQueueMod(mod.tag); return this; } /** - * 设置下载任务的执行队列类型 + * 设置下载任务的执行队列类型,后续版本会删除该api,请使用: + *
+   *   
+   *     Aria.get(this).getDownloadConfig().setQueueMod(mod.tag)
+   *   
+   * 
* * @param mod {@link QueueMod} + * @deprecated 后续版本会删除该api */ + @Deprecated public AriaManager setDownloadQueueMod(QueueMod mod) { mDConfig.setQueueMod(mod.tag); return this; @@ -151,6 +171,13 @@ import org.xml.sax.SAXException; return mUConfig; } + /** + * 获取APP配置 + */ + public Configuration.AppConfig getAppConfig() { + return mAConfig; + } + /** * 设置命令 */ @@ -201,22 +228,6 @@ import org.xml.sax.SAXException; return (receiver instanceof UploadReceiver) ? (UploadReceiver) receiver : null; } - /** - * 获取Aria下载错误日志 - * - * @return 如果错误日志不存在则返回空,否则返回错误日志列表 - */ - public List getErrorLog() { - return DbEntity.findAllData(ErrorEntity.class); - } - - /** - * 清楚错误日志 - */ - public void cleanLog() { - DbEntity.clean(ErrorEntity.class); - } - /** * 删除任务记录 * @@ -244,6 +255,7 @@ import org.xml.sax.SAXException; final String key = getKey(isDownload, obj); IReceiver receiver = mReceivers.get(key); boolean needRmReceiver = false; + // 监控Dialog、fragment、popupWindow的生命周期 final WidgetLiftManager widgetLiftManager = new WidgetLiftManager(); if (obj instanceof Dialog) { needRmReceiver = widgetLiftManager.handleDialogLift((Dialog) obj); @@ -270,7 +282,8 @@ import org.xml.sax.SAXException; /** * 不允许在"onDestroy"、"finish"、"onStop"这三个方法中添加注册器 */ - private AbsReceiver checkTarget(String key, AbsReceiver receiver, Object obj, boolean needRmReceiver) { + private AbsReceiver checkTarget(String key, AbsReceiver receiver, Object obj, + boolean needRmReceiver) { StackTraceElement[] stack = Thread.currentThread().getStackTrace(); int i = 0; for (StackTraceElement e : stack) { @@ -288,8 +301,8 @@ import org.xml.sax.SAXException; "onStop"); if (isDestroyed) { - ALog.w(TAG, - "请不要在Activity或Fragment的onDestroy、finish、onStop等方法中调用Aria,Aria的unRegister会在Activity页面销毁时自动执行"); + ALog.e(TAG, + "请不要在Activity或Fragment的onDestroy、finish、onStop等方法中注册Aria,Aria的unRegister会在Activity页面销毁时自动执行"); } if (obj instanceof Activity && isDestroyed) { @@ -309,45 +322,33 @@ import org.xml.sax.SAXException; */ private String getKey(boolean isDownload, Object obj) { String clsName = obj.getClass().getName(); - String key = ""; - if (!(obj instanceof Activity)) { - if (obj instanceof DialogFragment) { - key = clsName + "_" + ((DialogFragment) obj).getActivity().getClass().getName(); - } else if (obj instanceof android.app.DialogFragment) { - key = clsName + "_" + ((android.app.DialogFragment) obj).getActivity().getClass().getName(); - } else if (obj instanceof android.support.v4.app.Fragment) { - key = clsName + "_" + ((Fragment) obj).getActivity().getClass().getName(); - } else if (obj instanceof android.app.Fragment) { - key = clsName + "_" + ((android.app.Fragment) obj).getActivity().getClass().getName(); - } else if (obj instanceof Dialog) { - Activity activity = ((Dialog) obj).getOwnerActivity(); - if (activity != null) { - key = clsName + "_" + activity.getClass().getName(); - } else { - key = clsName; - } - } else if (obj instanceof PopupWindow) { - Context context = ((PopupWindow) obj).getContentView().getContext(); - if (context instanceof Activity) { - key = clsName + "_" + context.getClass().getName(); - } else { - key = clsName; - } - } else if (obj instanceof Service) { + String key; + if (obj instanceof DialogFragment) { + key = clsName + "_" + ((DialogFragment) obj).getActivity().getClass().getName(); + } else if (obj instanceof android.app.DialogFragment) { + key = clsName + "_" + ((android.app.DialogFragment) obj).getActivity().getClass().getName(); + } else if (obj instanceof android.support.v4.app.Fragment) { + key = clsName + "_" + ((Fragment) obj).getActivity().getClass().getName(); + } else if (obj instanceof android.app.Fragment) { + key = clsName + "_" + ((android.app.Fragment) obj).getActivity().getClass().getName(); + } else if (obj instanceof Dialog) { + Activity activity = ((Dialog) obj).getOwnerActivity(); + if (activity != null) { + key = clsName + "_" + activity.getClass().getName(); + } else { key = clsName; - } else if (obj instanceof Application) { + } + } else if (obj instanceof PopupWindow) { + Context context = ((PopupWindow) obj).getContentView().getContext(); + if (context instanceof Activity) { + key = clsName + "_" + context.getClass().getName(); + } else { key = clsName; } - } - if (obj instanceof Activity || obj instanceof Service) { - key = clsName; - } else if (obj instanceof Application) { + } else { key = clsName; } - if (TextUtils.isEmpty(key)) { - throw new IllegalArgumentException("未知类型"); - } - key += isDownload ? DOWNLOAD : UPLOAD; + key += (isDownload ? DOWNLOAD : UPLOAD) + obj.hashCode(); return key; } @@ -377,6 +378,7 @@ import org.xml.sax.SAXException; } mDConfig = Configuration.DownloadConfig.getInstance(); mUConfig = Configuration.UploadConfig.getInstance(); + mAConfig = Configuration.AppConfig.getInstance(); if (tempDir.exists()) { File newDir = new File(APP.getFilesDir().getPath() + DOWNLOAD_TEMP_DIR); newDir.mkdirs(); @@ -406,8 +408,8 @@ import org.xml.sax.SAXException; private void regAppLifeCallback(Context context) { Context app = context.getApplicationContext(); if (app instanceof Application) { - LifeCallback mLifeCallback = new LifeCallback(); - ((Application) app).registerActivityLifecycleCallbacks(mLifeCallback); + LifeCallback lifeCallback = new LifeCallback(); + ((Application) app).registerActivityLifecycleCallbacks(lifeCallback); } } @@ -478,6 +480,7 @@ import org.xml.sax.SAXException; @Override public void onActivityDestroyed(Activity activity) { destroySchedulerListener(activity); + // TODO: 2018/4/11 维护一个activity堆栈,应用被kill,activity会回调onDestroy方法,需要考虑server后台情况 } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/ConfigHelper.java b/Aria/src/main/java/com/arialyy/aria/core/ConfigHelper.java index 1eb12427..71211cd2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/ConfigHelper.java +++ b/Aria/src/main/java/com/arialyy/aria/core/ConfigHelper.java @@ -28,9 +28,10 @@ import org.xml.sax.helpers.DefaultHandler; class ConfigHelper extends DefaultHandler { private final String TAG = "ConfigHelper"; - private boolean isDownloadConfig = false, isUploadConfig; + private boolean isDownloadConfig = false, isUploadConfig = false, isAppConfig = false; private Configuration.DownloadConfig mDownloadConfig = Configuration.DownloadConfig.getInstance(); private Configuration.UploadConfig mUploadConfig = Configuration.UploadConfig.getInstance(); + private Configuration.AppConfig mAppConfig = Configuration.AppConfig.getInstance(); @Override public void startDocument() throws SAXException { super.startDocument(); @@ -43,10 +44,17 @@ class ConfigHelper extends DefaultHandler { if (qName.equals("download")) { isDownloadConfig = true; isUploadConfig = false; + isAppConfig = false; } else if (qName.equals("upload")) { isUploadConfig = true; isDownloadConfig = false; + isAppConfig = false; + } else if (qName.equals("app")) { + isUploadConfig = false; + isDownloadConfig = false; + isAppConfig = true; } + if (isDownloadConfig || isUploadConfig) { String value = attributes.getValue("value"); @@ -90,17 +98,46 @@ class ConfigHelper extends DefaultHandler { loadUpdateInterval(value); break; } + } else if (isAppConfig) { + String value = attributes.getValue("value"); + switch (qName) { + case "useAriaCrashHandler": + loadUseAriaCrashHandler(value); + break; + case "logLevel": + loadLogLevel(value); + break; + } } } - private void loadUpdateInterval(String value) { - long temp = 1000; - if (!TextUtils.isEmpty(value)) { - temp = Long.parseLong(value); - if (temp <= 0) { - temp = 1000; - } + private void loadLogLevel(String value) { + int level; + try { + level = Integer.parseInt(value); + } catch (NumberFormatException e) { + e.printStackTrace(); + level = ALog.LOG_LEVEL_VERBOSE; + } + if (level < ALog.LOG_LEVEL_VERBOSE || level > ALog.LOG_CLOSE) { + ALog.w(TAG, "level【" + level + "】错误"); + mAppConfig.logLevel = ALog.LOG_LEVEL_VERBOSE; + } else { + mAppConfig.logLevel = level; + } + } + + private void loadUseAriaCrashHandler(String value) { + if (checkBoolean(value)) { + mAppConfig.useAriaCrashHandler = Boolean.parseBoolean(value); + } else { + ALog.w(TAG, "useAriaCrashHandler【" + value + "】错误"); + mAppConfig.useAriaCrashHandler = true; } + } + + private void loadUpdateInterval(String value) { + long temp = checkLong(value) ? Long.parseLong(value) : 1000; if (isDownloadConfig) { mDownloadConfig.updateInterval = temp; } @@ -124,17 +161,18 @@ class ConfigHelper extends DefaultHandler { } private void loadMaxSpeed(String value) { - double maxSpeed = 0.0; - if (!TextUtils.isEmpty(value)) { - maxSpeed = Double.parseDouble(value); - } + int maxSpeed = checkInt(value) ? Integer.parseInt(value) : 0; if (isDownloadConfig) { - mDownloadConfig.msxSpeed = maxSpeed; + mDownloadConfig.maxSpeed = maxSpeed; } } private void loadConvertSpeed(String value) { - boolean open = Boolean.parseBoolean(value); + boolean open = true; + if (checkBoolean(value)) { + open = Boolean.parseBoolean(value); + } + if (isDownloadConfig) { mDownloadConfig.isConvertSpeed = open; } @@ -144,10 +182,7 @@ class ConfigHelper extends DefaultHandler { } private void loadReTryInterval(String value) { - int time = 2 * 1000; - if (!TextUtils.isEmpty(value)) { - time = Integer.parseInt(value); - } + int time = checkInt(value) ? Integer.parseInt(value) : 2 * 1000; if (time < 2 * 1000) { time = 2 * 1000; @@ -166,10 +201,7 @@ class ConfigHelper extends DefaultHandler { } private void loadBuffSize(String value) { - int buffSize = 8192; - if (!TextUtils.isEmpty(value)) { - buffSize = Integer.parseInt(value); - } + int buffSize = checkInt(value) ? Integer.parseInt(value) : 8192; if (buffSize < 2048) { buffSize = 2048; @@ -181,10 +213,7 @@ class ConfigHelper extends DefaultHandler { } private void loadIOTimeout(String value) { - int time = 10 * 1000; - if (!TextUtils.isEmpty(value)) { - time = Integer.parseInt(value); - } + int time = checkInt(value) ? Integer.parseInt(value) : 10 * 1000; if (time < 10 * 1000) { time = 10 * 1000; @@ -196,10 +225,7 @@ class ConfigHelper extends DefaultHandler { } private void loadConnectTime(String value) { - int time = 5 * 1000; - if (!TextUtils.isEmpty(value)) { - time = Integer.parseInt(value); - } + int time = checkInt(value) ? Integer.parseInt(value) : 5 * 1000; if (isDownloadConfig) { mDownloadConfig.connectTimeOut = time; @@ -210,10 +236,7 @@ class ConfigHelper extends DefaultHandler { } private void loadReTry(String value) { - int num = 0; - if (!TextUtils.isEmpty(value)) { - num = Integer.parseInt(value); - } + int num = checkInt(value) ? Integer.parseInt(value) : 0; if (isDownloadConfig) { mDownloadConfig.reTryNum = num; @@ -224,10 +247,7 @@ class ConfigHelper extends DefaultHandler { } private void loadMaxQueue(String value) { - int num = 2; - if (!TextUtils.isEmpty(value)) { - num = Integer.parseInt(value); - } + int num = checkInt(value) ? Integer.parseInt(value) : 2; if (num < 1) { ALog.w(TAG, "任务队列数不能小于 1"); num = 2; @@ -241,10 +261,7 @@ class ConfigHelper extends DefaultHandler { } private void loadThreadNum(String value) { - int num = 3; - if (!TextUtils.isEmpty(value)) { - num = Integer.parseInt(value); - } + int num = checkInt(value) ? Integer.parseInt(value) : 3; if (num < 1) { ALog.e(TAG, "下载线程数不能小于 1"); num = 1; @@ -254,6 +271,52 @@ class ConfigHelper extends DefaultHandler { } } + /** + * 检查是否int值是否合法 + * + * @return {@code true} 合法 + */ + private boolean checkInt(String value) { + if (TextUtils.isEmpty(value)) { + return false; + } + try { + Integer l = Integer.parseInt(value); + return true; + } catch (NumberFormatException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 检查是否long值是否合法 + * + * @return {@code true} 合法 + */ + private boolean checkLong(String value) { + if (TextUtils.isEmpty(value)) { + return false; + } + try { + Long l = Long.parseLong(value); + return true; + } catch (NumberFormatException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 检查boolean值是否合法 + * + * @return {@code true} 合法 + */ + private boolean checkBoolean(String value) { + return !TextUtils.isEmpty(value) && (value.equalsIgnoreCase("true") || value.equalsIgnoreCase( + "false")); + } + @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); } @@ -266,5 +329,6 @@ class ConfigHelper extends DefaultHandler { super.endDocument(); mDownloadConfig.saveAll(); mUploadConfig.saveAll(); + mAppConfig.saveAll(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/ErrorEntity.java b/Aria/src/main/java/com/arialyy/aria/core/ErrorEntity.java deleted file mode 100644 index 0ba2bbe6..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/ErrorEntity.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core; - -import com.arialyy.aria.orm.DbEntity; - -/** - * Created by Aria.Lao on 2017/8/29. - * 错误实体 - */ -public class ErrorEntity extends DbEntity { - - /** - * 插入时间 - */ - public long insertTime; - - /** - * 错误信息 - */ - public String err; - - /** - * 任务名 - */ - public String taskName; - - /** - *任务类型 - */ - public String taskType; - - /** - * 提示 - */ - public String msg; - - /** - * 任务key - */ - public String key; - - @Override public String toString() { - return "ErrorEntity{" - + "insertTime=" - + insertTime - + ", err='" - + err - + '\'' - + ", taskName='" - + taskName - + '\'' - + ", taskType='" - + taskType - + '\'' - + ", msg='" - + msg - + '\'' - + ", key='" - + key - + '\'' - + '}'; - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java b/Aria/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java index a684e810..da821979 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java @@ -24,7 +24,10 @@ import java.net.InetAddress; * ftp url 信息链接实体 */ public class FtpUrlEntity implements Cloneable { - + /** + * 如:ftp://127.0.0.1:21/download/AriaPrj.zip + * remotePath便是:download/AriaPrj.zip + */ public String remotePath; public String account; @@ -35,12 +38,12 @@ public class FtpUrlEntity implements Cloneable { public String url; /** - * ftp协议 + * ftp协议:ftp */ public String protocol; /** - * 用户 + * 登录的用户名 */ public String user; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java index 3f3efe11..cd0197fb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelAllCmd.java @@ -16,13 +16,7 @@ package com.arialyy.aria.core.command.normal; -import com.arialyy.aria.core.download.DownloadGroupTaskEntity; -import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.core.upload.UploadTaskEntity; -import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.util.CommonUtil; -import java.util.List; /** * Created by AriaL on 2017/6/27. @@ -44,46 +38,5 @@ public class CancelAllCmd extends AbsNormalCmd { @Override public void executeCmd() { removeAll(); - if (mTaskEntity instanceof DownloadTaskEntity - || mTaskEntity instanceof DownloadGroupTaskEntity) { - handleDownloadRemove(); - handleDownloadGroupRemove(); - } else if (mTaskEntity instanceof UploadTaskEntity) { - handleUploadRemove(); - handleUploadRemove(); - } - } - - /** - * 处理下载任务组的删除操作 - */ - private void handleDownloadGroupRemove() { - List allEntity = DbEntity.findAllData(DownloadGroupTaskEntity.class); - if (allEntity == null || allEntity.size() == 0) return; - for (DownloadGroupTaskEntity entity : allEntity) { - CommonUtil.delDownloadGroupTaskConfig(removeFile, entity); - } - } - - /** - * 处理上传的删除 - */ - private void handleUploadRemove() { - List allEntity = DbEntity.findAllData(UploadTaskEntity.class); - if (allEntity == null || allEntity.size() == 0) return; - for (UploadTaskEntity entity : allEntity) { - CommonUtil.delUploadTaskConfig(removeFile, entity); - } - } - - /** - * 处理下载的删除 - */ - private void handleDownloadRemove() { - List allEntity = DbEntity.findAllData(DownloadTaskEntity.class); - if (allEntity == null || allEntity.size() == 0) return; - for (DownloadTaskEntity entity : allEntity) { - CommonUtil.delDownloadTaskConfig(removeFile, entity); - } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelCmd.java index 31db4cdf..57ab73d1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/CancelCmd.java @@ -42,7 +42,7 @@ public class CancelCmd extends AbsNormalCmd { task = createTask(); } if (task != null) { - mTaskEntity.removeFile = removeFile; + mTaskEntity.setRemoveFile(removeFile); if (!TextUtils.isEmpty(mTargetName)) { task.setTargetName(mTargetName); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/normal/ResumeAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/ResumeAllCmd.java index b952b205..ecdc37df 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/normal/ResumeAllCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/ResumeAllCmd.java @@ -3,12 +3,15 @@ package com.arialyy.aria.core.command.normal; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.download.wrapper.DGTEWrapper; +import com.arialyy.aria.core.download.wrapper.DTEWrapper; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; import com.arialyy.aria.core.queue.DownloadTaskQueue; import com.arialyy.aria.core.queue.UploadTaskQueue; import com.arialyy.aria.core.upload.UploadTaskEntity; +import com.arialyy.aria.core.upload.wrapper.UTEWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -53,29 +56,32 @@ final class ResumeAllCmd extends AbsNormalCmd { * @param type {@code 1}单任务下载任务;{@code 2}任务组下载任务;{@code 3} 单任务上传任务 */ private List findTaskData(int type) { + // TODO: 2018/4/20 需要测试 List tempList = new ArrayList<>(); - switch (type) { - case 1: - List dTaskEntity = - DbEntity.findDatas(DownloadTaskEntity.class, "isGroupTask=?", "false"); - if (dTaskEntity != null && !dTaskEntity.isEmpty()) { - tempList.addAll(dTaskEntity); + if (type == 1) { + List wrappers = DbEntity.findRelationData(DTEWrapper.class, + "DownloadTaskEntity.isGroupTask=? and DownloadTaskEntity.state!=?", "false", "1"); + if (wrappers != null && !wrappers.isEmpty()) { + for (DTEWrapper w : wrappers) { + tempList.add(w.taskEntity); } - break; - case 2: - List groupTask = - DbEntity.findAllData(DownloadGroupTaskEntity.class); - if (groupTask != null && !groupTask.isEmpty()) { - tempList.addAll(groupTask); + } + } else if (type == 2) { + List wrappers = + DbEntity.findRelationData(DGTEWrapper.class, "DownloadGroupTaskEntity.state!=?", "1"); + if (wrappers != null && !wrappers.isEmpty()) { + for (DGTEWrapper w : wrappers) { + tempList.add(w.taskEntity); } - break; - case 3: - List uTaskEntity = - DbEntity.findDatas(UploadTaskEntity.class, "isGroupTask=?", "false"); - if (uTaskEntity != null && !uTaskEntity.isEmpty()) { - tempList.addAll(uTaskEntity); + } + } else if (type == 3) { + List wrappers = + DbEntity.findRelationData(UTEWrapper.class, "UploadTaskEntity.state!=?", "1"); + if (wrappers != null && !wrappers.isEmpty()) { + for (UTEWrapper w : wrappers) { + tempList.add(w.taskEntity); } - break; + } } return tempList; } @@ -123,8 +129,8 @@ final class ResumeAllCmd extends AbsNormalCmd { */ private void resumeEntity(AbsTaskEntity te) { if (te instanceof DownloadTaskEntity) { - if (te.requestType == AbsTaskEntity.D_FTP || te.requestType == AbsTaskEntity.U_FTP) { - te.urlEntity = CommonUtil.getFtpUrlInfo(te.getEntity().getKey()); + if (te.getRequestType() == AbsTaskEntity.D_FTP || te.getRequestType() == AbsTaskEntity.U_FTP) { + te.setUrlEntity(CommonUtil.getFtpUrlInfo(te.getEntity().getKey())); } mQueue = DownloadTaskQueue.getInstance(); } else if (te instanceof UploadTaskEntity) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java index 82120c32..680245a0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java @@ -21,6 +21,8 @@ import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.common.QueueMod; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.download.wrapper.DGTEWrapper; +import com.arialyy.aria.core.download.wrapper.DTEWrapper; import com.arialyy.aria.core.inf.AbsTask; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.IEntity; @@ -28,6 +30,7 @@ import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; import com.arialyy.aria.core.queue.DownloadTaskQueue; import com.arialyy.aria.core.queue.UploadTaskQueue; import com.arialyy.aria.core.upload.UploadTaskEntity; +import com.arialyy.aria.core.upload.wrapper.UTEWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -53,6 +56,7 @@ class StartCmd extends AbsNormalCmd { return; } String mod; + // TODO: 2018/4/12 配置文件不存在,是否会出现wait获取不到 ? int maxTaskNum = mQueue.getMaxTaskNum(); AriaManager manager = AriaManager.getInstance(AriaManager.APP); if (isDownloadCmd) { @@ -77,7 +81,6 @@ class StartCmd extends AbsNormalCmd { || task.getState() == IEntity.STATE_OTHER || task.getState() == IEntity.STATE_POST_PRE || task.getState() == IEntity.STATE_COMPLETE) { - //startTask(); resumeTask(); } else { sendWaitState(); @@ -85,7 +88,6 @@ class StartCmd extends AbsNormalCmd { } } else { if (!task.isRunning()) { - //startTask(); resumeTask(); } } @@ -113,29 +115,32 @@ class StartCmd extends AbsNormalCmd { } private List findWaitData(int type) { + // TODO: 2018/4/20 需要测试 List waitList = new ArrayList<>(); - switch (type) { - case 1: - List dEntity = - DbEntity.findDatas(DownloadTaskEntity.class, "groupName=? and state=?", "", "3"); - if (dEntity != null && !dEntity.isEmpty()) { - waitList.addAll(dEntity); + if (type == 1) { + List wrappers = DbEntity.findRelationData(DTEWrapper.class, + "DownloadTaskEntity.isGroupTask=? and DownloadTaskEntity.state=?", "false", "3"); + if (wrappers != null && !wrappers.isEmpty()) { + for (DTEWrapper w : wrappers) { + waitList.add(w.taskEntity); } - break; - case 2: - List dgEntity = - DbEntity.findDatas(DownloadGroupTaskEntity.class, "state=?", "3"); - if (dgEntity != null && !dgEntity.isEmpty()) { - waitList.addAll(dgEntity); + } + } else if (type == 2) { + List wrappers = + DbEntity.findRelationData(DGTEWrapper.class, "DownloadGroupTaskEntity.state=?", "3"); + if (wrappers != null && !wrappers.isEmpty()) { + for (DGTEWrapper w : wrappers) { + waitList.add(w.taskEntity); } - break; - case 3: - List uEntity = - DbEntity.findDatas(UploadTaskEntity.class, "state=?", "3"); - if (uEntity != null && !uEntity.isEmpty()) { - waitList.addAll(uEntity); + } + } else if (type == 3) { + List wrappers = DbEntity.findRelationData(UTEWrapper.class, + "UploadTaskEntity.state=?", "3"); + if (wrappers != null && !wrappers.isEmpty()) { + for (UTEWrapper w : wrappers) { + waitList.add(w.taskEntity); } - break; + } } return waitList; } @@ -146,8 +151,8 @@ class StartCmd extends AbsNormalCmd { AbsTask task = getTask(te.getEntity()); if (task != null) continue; if (te instanceof DownloadTaskEntity) { - if (te.requestType == AbsTaskEntity.D_FTP || te.requestType == AbsTaskEntity.U_FTP) { - te.urlEntity = CommonUtil.getFtpUrlInfo(te.getEntity().getKey()); + if (te.getRequestType() == AbsTaskEntity.D_FTP || te.getRequestType() == AbsTaskEntity.U_FTP) { + te.setUrlEntity(CommonUtil.getFtpUrlInfo(te.getEntity().getKey())); } mQueue = DownloadTaskQueue.getInstance(); } else if (te instanceof UploadTaskEntity) { 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 c7fb1145..521284f5 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 @@ -18,11 +18,13 @@ package com.arialyy.aria.core.common; import android.content.Context; import android.util.SparseArray; import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IEventListener; +import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -36,12 +38,13 @@ import java.util.concurrent.Executors; /** * Created by AriaL on 2017/7/1. - * 文件下载器 + * 任务处理器 */ public abstract class AbsFileer> implements Runnable, IUtil { public static final String STATE = "_state_"; public static final String RECORD = "_record_"; + protected static final long SUB_LEN = 1024 * 1024; private final String TAG = "AbsFileer"; protected IEventListener mListener; @@ -49,7 +52,7 @@ public abstract class AbsFileer 0) { client = new FTPClient(); InetAddress ip = InetAddress.getByName(urlEntity.hostName); + client.setConnectTimeout(10000); // 连接10s超时 client.connect(ip, Integer.parseInt(urlEntity.port)); - mTaskEntity.urlEntity.validAddr = ip; + mTaskEntity.getUrlEntity().validAddr = ip; } else { InetAddress[] ips = InetAddress.getAllByName(urlEntity.hostName); client = connect(new FTPClient(), ips, 0, Integer.parseInt(urlEntity.port)); @@ -211,10 +212,10 @@ public abstract class AbsFtpInfoThread> + implements IFtpTarget { + private static final String TAG = "FtpDelegate"; + private ENTITY mEntity; + private TASK_ENTITY mTaskEntity; + private TARGET mTarget; + + public FtpDelegate(TARGET target, TASK_ENTITY taskEntity) { + mTarget = target; + mTaskEntity = taskEntity; + mEntity = mTaskEntity.getEntity(); + } + + @Override public TARGET charSet(String charSet) { + if (TextUtils.isEmpty(charSet)) return mTarget; + mTaskEntity.setCharSet(charSet); + return mTarget; + } + + @Override public TARGET login(String userName, String password) { + return login(userName, password, null); + } + + @Override public TARGET login(String userName, String password, String account) { + if (TextUtils.isEmpty(userName)) { + ALog.e(TAG, "用户名不能为null"); + return mTarget; + } else if (TextUtils.isEmpty(password)) { + ALog.e(TAG, "密码不能为null"); + return mTarget; + } + mTaskEntity.getUrlEntity().needLogin = true; + mTaskEntity.getUrlEntity().user = userName; + mTaskEntity.getUrlEntity().password = password; + mTaskEntity.getUrlEntity().account = account; + return mTarget; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/delegate/HttpHeaderDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/delegate/HttpHeaderDelegate.java new file mode 100644 index 00000000..998d3858 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/delegate/HttpHeaderDelegate.java @@ -0,0 +1,139 @@ +/* + * 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.delegate; + +import android.support.annotation.NonNull; +import android.text.TextUtils; +import com.arialyy.aria.core.common.RequestEnum; +import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.inf.AbsTaskEntity; +import com.arialyy.aria.core.inf.IHttpHeaderTarget; +import com.arialyy.aria.core.inf.ITarget; +import com.arialyy.aria.util.ALog; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * Created by laoyuyu on 2018/3/9. + * HTTP header参数设置委托类 + */ +public class HttpHeaderDelegate> + implements IHttpHeaderTarget { + private static final String TAG = "HttpHeaderDelegate"; + private ENTITY mEntity; + private TASK_ENTITY mTaskEntity; + private TARGET mTarget; + + public HttpHeaderDelegate(TARGET target, TASK_ENTITY taskEntity) { + mTarget = target; + mTaskEntity = taskEntity; + + mEntity = mTaskEntity.getEntity(); + } + + /** + * 给url请求添加Header数据 + * 如果新的header数据和数据保存的不一致,则更新数据库中对应的header数据 + * + * @param key header对应的key + * @param value header对应的value + */ + @Override + public TARGET addHeader(@NonNull String key, @NonNull String value) { + if (TextUtils.isEmpty(key)) { + ALog.w(TAG, "设置header失败,header对应的key不能为null"); + return mTarget; + } else if (TextUtils.isEmpty(value)) { + ALog.w(TAG, "设置header失败,header对应的value不能为null"); + return mTarget; + } + if (mTaskEntity.getHeaders().get(key) == null) { + mTaskEntity.getHeaders().put(key, value); + } else if (!mTaskEntity.getHeaders().get(key).equals(value)) { + mTaskEntity.getHeaders().put(key, value); + } + return mTarget; + } + + /** + * 给url请求添加一组header数据 + * 如果新的header数据和数据保存的不一致,则更新数据库中对应的header数据 + * + * @param headers 一组http header数据 + */ + @Override + public TARGET addHeaders(@NonNull Map headers) { + if (headers.size() == 0) { + ALog.w(TAG, "设置header失败,map没有header数据"); + return mTarget; + } + /* + 两个map比较逻辑 + 1、比对key是否相同 + 2、如果key相同,比对value是否相同 + 3、只有当上面两个步骤中key 和 value都相同时才能任务两个map数据一致 + */ + boolean mapEquals = false; + if (mTaskEntity.getHeaders().size() == headers.size()) { + int i = 0; + Set keys = mTaskEntity.getHeaders().keySet(); + for (String key : keys) { + if (headers.containsKey(key)) { + i++; + } else { + break; + } + } + if (i == mTaskEntity.getHeaders().size()) { + int j = 0; + Collection values = mTaskEntity.getHeaders().values(); + for (String value : values) { + if (headers.containsValue(value)) { + j++; + } else { + break; + } + } + if (j == mTaskEntity.getHeaders().size()) { + mapEquals = true; + } + } + } + + if (!mapEquals) { + mTaskEntity.getHeaders().clear(); + Set keys = headers.keySet(); + for (String key : keys) { + mTaskEntity.getHeaders().put(key, headers.get(key)); + } + } + + return mTarget; + } + + /** + * 设置请求类型,POST或GET,默认为在GET + * 只试用于HTTP请求 + * + * @param requestEnum {@link RequestEnum} + */ + @Override + public TARGET setRequestMode(RequestEnum requestEnum) { + mTaskEntity.setRequestEnum(requestEnum); + return mTarget; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/AbsDownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/AbsDownloadTarget.java new file mode 100644 index 00000000..c48943e3 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/AbsDownloadTarget.java @@ -0,0 +1,97 @@ +/* + * 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 com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.command.normal.NormalCmdFactory; +import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.inf.AbsTarget; +import com.arialyy.aria.core.inf.AbsTaskEntity; +import com.arialyy.aria.util.CommonUtil; + +/** + * Created by lyy on 2017/2/28. + */ +abstract class AbsDownloadTarget + extends AbsTarget { + + static final int HTTP = 1; + static final int FTP = 2; + //HTTP任务组 + static final int GROUP_HTTP = 3; + //FTP文件夹 + static final int GROUP_FTP_DIR = 4; + + /** + * 设置的文件保存路径的临时变量 + */ + String mTempFilePath; + + /** + * 将任务设置为最高优先级任务,最高优先级任务有以下特点: + * 1、在下载队列中,有且只有一个最高优先级任务 + * 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 + * 3、任务调度器不会暂停最高优先级任务 + * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 + * 5、如果下载队列中已经满了,则会停止队尾的任务,当高优先级任务完成后,该队尾任务将自动执行 + * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 + */ + protected void setHighestPriority() { + if (checkEntity()) { + AriaManager.getInstance(AriaManager.APP) + .setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, + NormalCmdFactory.TASK_HIGHEST_PRIORITY, checkTaskType())) + .exe(); + } + } + + /** + * 添加任务 + */ + public void add() { + if (checkEntity()) { + AriaManager.getInstance(AriaManager.APP) + .setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_CREATE, + checkTaskType())) + .exe(); + } + } + + /** + * 获取任务文件大小 + * + * @return 文件大小 + */ + public long getFileSize() { + return getSize(); + } + + /** + * 获取单位转换后的文件大小 + * + * @return 文件大小{@code xxx mb} + */ + public String getConvertFileSize() { + return getConvertSize(); + } + + /** + * 设置target类型 + * + * @return {@link #HTTP}、{@link #FTP}、{@link #GROUP_HTTP}、{@link #GROUP_FTP_DIR} + */ + protected abstract int getTargetType(); +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/BaseDListener.java b/Aria/src/main/java/com/arialyy/aria/core/download/BaseDListener.java index 04b6d44f..30d6388f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/BaseDListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/BaseDListener.java @@ -150,11 +150,11 @@ class BaseDListener - extends AbsDownloadTarget { +abstract class BaseGroupTarget + extends AbsDownloadTarget { - List mUrls = new ArrayList<>(); + /** + * 组任务名 + */ String mGroupName; /** - * 子任务文件名 + * 文件夹临时路径 */ - private List mSubTaskFileName = new ArrayList<>(); + String mDirPathTemp; /** - * 是否已经设置了文件路径 + * 是否需要修改路径 */ - private boolean isSetDirPathed = false; + boolean needModifyPath = false; private SubTaskManager mSubTaskManager; @@ -63,10 +62,24 @@ abstract class BaseGroupTarget * - * @param groupDirPath 任务组保存文件夹路径 + * @param dirPath 任务组保存文件夹路径 */ - public TARGET setDownloadDirPath(String groupDirPath) { - if (TextUtils.isEmpty(groupDirPath)) { - throw new NullPointerException("任务组文件夹保存路径不能为null"); - } - - isSetDirPathed = true; - if (mEntity.getDirPath().equals(groupDirPath)) return (TARGET) this; - - File file = new File(groupDirPath); - if (file.exists() && file.isFile()) { - throw new IllegalArgumentException("路径不能为文件"); - } - if (!file.exists()) { - file.mkdirs(); - } - - mEntity.setDirPath(groupDirPath); - if (!TextUtils.isEmpty(mEntity.getDirPath())) { - reChangeDirPath(groupDirPath); - } else { - mEntity.setSubTasks(createSubTask()); - } - mEntity.update(); + public TARGET setDirPath(String dirPath) { + mDirPathTemp = dirPath; return (TARGET) this; } + @Override public boolean isRunning() { + DownloadGroupTask task = DownloadGroupTaskQueue.getInstance().getTask(mEntity.getKey()); + return task != null && task.isRunning(); + } + /** * 改变任务组文件夹路径,修改文件夹路径会将子任务所有路径更换 * * @param newDirPath 新的文件夹路径 */ - private void reChangeDirPath(String newDirPath) { - List subTask = mEntity.getSubTask(); - if (subTask != null && !subTask.isEmpty()) { - for (DownloadEntity entity : subTask) { - String oldPath = entity.getDownloadPath(); - String newPath = newDirPath + "/" + entity.getFileName(); + void reChangeDirPath(String newDirPath) { + List subTasks = mTaskEntity.getSubTaskEntities(); + if (subTasks != null && !subTasks.isEmpty()) { + for (DownloadTaskEntity dte : subTasks) { + DownloadEntity de = dte.getEntity(); + String oldPath = de.getDownloadPath(); + String newPath = newDirPath + "/" + de.getFileName(); File file = new File(oldPath); - file.renameTo(new File(newPath)); - DbEntity.exeSql("UPDATE DownloadEntity SET downloadPath='" - + newPath - + "' WHERE downloadPath='" - + oldPath - + "'"); - DbEntity.exeSql( - "UPDATE DownloadTaskEntity SET key='" + newPath + "' WHERE key='" + oldPath + "'"); + if (file.exists()) { + file.renameTo(new File(newPath)); + } + de.setDownloadPath(newPath); + dte.setKey(newPath); + de.save(); + dte.save(); } - } else { - mEntity.setSubTasks(createSubTask()); } } /** - * 设置子任务文件名,该方法必须在{@link #setDownloadDirPath(String)}之后调用,否则不生效 + * 检查并设置文件夹路径 * - * @see #setSubFileName(List) - */ - @Deprecated public TARGET setSubTaskFileName(List subTaskFileName) { - - return setSubFileName(subTaskFileName); - } - - /** - * 设置子任务文件名,该方法必须在{@link #setDownloadDirPath(String)}之后调用,否则不生效 + * @return {@code true} 合法 */ - public TARGET setSubFileName(List subTaskFileName) { - if (subTaskFileName == null || subTaskFileName.isEmpty()) return (TARGET) this; - mSubTaskFileName.addAll(subTaskFileName); - if (mUrls.size() != subTaskFileName.size()) { - throw new IllegalArgumentException("下载链接数必须要和保存路径的数量一致"); + boolean checkDirPath() { + if (TextUtils.isEmpty(mDirPathTemp)) { + ALog.e(TAG, "文件夹路径不能为null"); + return false; + } else if (!mDirPathTemp.startsWith("/")) { + ALog.e(TAG, "文件夹路径【" + mDirPathTemp + "】错误"); + return false; } - if (isSetDirPathed) { - List entities = mEntity.getSubTask(); - int i = 0; - for (DownloadEntity entity : entities) { - if (i < mSubTaskFileName.size()) { - String newName = mSubTaskFileName.get(i); - updateSubFileName(entity, newName); - } - i++; - } + File file = new File(mDirPathTemp); + if (file.isFile()) { + ALog.e(TAG, "路径【" + mDirPathTemp + "】是文件,请设置文件夹路径"); + return false; } - return (TARGET) this; - } - /** - * 更新子任务文件名 - */ - private void updateSubFileName(DownloadEntity entity, String newName) { - if (!newName.equals(entity.getFileName())) { - String oldPath = mEntity.getDirPath() + "/" + entity.getFileName(); - String newPath = mEntity.getDirPath() + "/" + newName; - File oldFile = new File(oldPath); - if (oldFile.exists()) { - oldFile.renameTo(new File(newPath)); + if (TextUtils.isEmpty(mEntity.getDirPath()) || !mEntity.getDirPath().equals(mDirPathTemp)) { + if (!file.exists()) { + file.mkdirs(); } - CommonUtil.renameDownloadConfig(oldFile.getName(), newName); - DbEntity.exeSql( - "UPDATE DownloadTaskEntity SET key='" + newPath + "' WHERE key='" + oldPath + "'"); - entity.setDownloadPath(newPath); - entity.setFileName(newName); - entity.update(); + needModifyPath = true; + mEntity.setDirPath(mDirPathTemp); } - } - /** - * 创建子任务 - */ - private List createSubTask() { - List list = new ArrayList<>(); - for (int i = 0, len = mUrls.size(); i < len; i++) { - DownloadEntity entity = new DownloadEntity(); - entity.setUrl(mUrls.get(i)); - String fileName = - mSubTaskFileName.isEmpty() ? createFileName(entity.getUrl()) : mSubTaskFileName.get(i); - entity.setDownloadPath(mEntity.getDirPath() + "/" + fileName); - entity.setGroupName(mGroupName); - entity.setGroupChild(true); - entity.setFileName(fileName); - entity.insert(); - list.add(entity); - } - return list; + return true; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/BaseNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/BaseNormalTarget.java new file mode 100644 index 00000000..c0b39569 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/BaseNormalTarget.java @@ -0,0 +1,183 @@ +/* + * 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.text.TextUtils; +import com.arialyy.aria.core.manager.TEManager; +import com.arialyy.aria.core.queue.DownloadTaskQueue; +import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; +import java.io.File; + +/** + * Created by Aria.Lao on 2017/7/26. + */ +abstract class BaseNormalTarget + extends AbsDownloadTarget { + + /** + * 资源地址 + */ + protected String url; + + /** + * 通过地址初始化target + */ + void initTarget(String url, String targetName, boolean refreshInfo) { + this.url = url; + mTargetName = targetName; + mTaskEntity = TEManager.getInstance().getTEntity(DownloadTaskEntity.class, url); + mEntity = mTaskEntity.getEntity(); + mTaskEntity.setRefreshInfo(refreshInfo); + if (mEntity != null) { + mTempFilePath = mEntity.getDownloadPath(); + } + } + + /** + * 将任务设置为最高优先级任务,最高优先级任务有以下特点: + * 1、在下载队列中,有且只有一个最高优先级任务 + * 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 + * 3、任务调度器不会暂停最高优先级任务 + * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 + * 5、如果下载队列中已经满了,则会停止队尾的任务,当高优先级任务完成后,该队尾任务将自动执行 + * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 + */ + @Override public void setHighestPriority() { + super.setHighestPriority(); + } + + /** + * 下载任务是否存在 + * + * @return {@code true}任务存在 + */ + @Override public boolean taskExists() { + return DownloadTaskQueue.getInstance().getTask(mEntity.getUrl()) != null; + } + + /** + * 获取下载实体 + */ + public DownloadEntity getDownloadEntity() { + return mEntity; + } + + /** + * 是否在下载,该api后续版本会删除 + * + * @deprecated {@link #isRunning()} + */ + @Deprecated public boolean isDownloading() { + return isRunning(); + } + + /** + * 是否在下载 + * + * @return {@code true}任务正在下载 + */ + @Override public boolean isRunning() { + DownloadTask task = DownloadTaskQueue.getInstance().getTask(mEntity.getKey()); + return task != null && task.isRunning(); + } + + /** + * 检查下载实体,判断实体是否合法 + * 合法标准为: + * 1、下载路径不为null,并且下载路径是正常的http或ftp路径 + * 2、保存路径不为null,并且保存路径是android文件系统路径 + * 3、保存路径不能重复 + * + * @return {@code true}合法 + */ + @Override protected boolean checkEntity() { + boolean b = getTargetType() < GROUP_HTTP && checkUrl() && checkFilePath(); + if (b) { + mEntity.save(); + mTaskEntity.save(); + } + return b; + } + + /** + * 检查并设置普通任务的文件保存路径 + * + * @return {@code true}保存路径合法 + */ + private boolean checkFilePath() { + String filePath = mTempFilePath; + if (TextUtils.isEmpty(filePath)) { + ALog.e(TAG, "下载失败,文件保存路径为null"); + return false; + } else if (!filePath.startsWith("/")) { + ALog.e(TAG, "下载失败,文件保存路径【" + filePath + "】错误"); + return false; + } + File file = new File(filePath); + if (file.isDirectory()) { + if (getTargetType() == HTTP) { + ALog.e(TAG, "下载失败,保存路径【" + filePath + "】不能为文件夹,路径需要是完整的文件路径,如:/mnt/sdcard/game.zip"); + return false; + } else if (getTargetType() == FTP) { + filePath += mEntity.getFileName(); + } + } + mEntity.setFileName(file.getName()); + + //设置文件保存路径,如果新文件路径和就文件路径不同,则修改路径 + if (!filePath.equals(mEntity.getDownloadPath())) { + if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) { + ALog.e(TAG, "下载失败,保存路径【" + filePath + "】已经被其它任务占用,请设置其它保存路径"); + return false; + } + File oldFile = new File(mEntity.getDownloadPath()); + File newFile = new File(filePath); + mEntity.setDownloadPath(filePath); + mEntity.setFileName(newFile.getName()); + mTaskEntity.setKey(filePath); + //mTaskEntity.update(); + if (oldFile.exists()) { + oldFile.renameTo(newFile); + CommonUtil.renameDownloadConfig(oldFile.getName(), newFile.getName()); + } + } + return true; + } + + /** + * 检查普通任务的下载地址 + * + * @return {@code true}地址合法 + */ + private boolean checkUrl() { + final String url = mEntity.getUrl(); + 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 false; + } + return true; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java index 91f11d1c..b22ae995 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java @@ -21,8 +21,9 @@ import android.os.Parcelable; import android.text.TextUtils; import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.orm.Foreign; -import com.arialyy.aria.orm.Primary; +import com.arialyy.aria.orm.ActionPolicy; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Primary; import com.arialyy.aria.util.CommonUtil; /** @@ -30,33 +31,30 @@ import com.arialyy.aria.util.CommonUtil; * 下载实体 */ public class DownloadEntity extends AbsNormalEntity implements Parcelable { - @Primary private String downloadPath = ""; //保存路径 + @Primary private String downloadPath; //保存路径 /** * 所属任务组 */ - @Foreign(table = DownloadGroupEntity.class, column = "groupName") private String groupName = ""; + @Foreign(parent = DownloadGroupEntity.class, column = "groupName", + onUpdate = ActionPolicy.CASCADE, onDelete = ActionPolicy.CASCADE) + private String groupName; /** - * 下载任务实体的key - */ - @Foreign(table = DownloadTaskEntity.class, column = "key") private String taskKey = ""; - - /** - * 通过{@link AbsTaskEntity#md5Key}从服务器的返回信息中获取的文件md5信息,如果服务器没有返回,则不会设置该信息 + * 从服务器的返回信息中获取的文件md5信息,如果服务器没有返回,则不会设置该信息 * 如果你已经设置了该任务的MD5信息,Aria也不会从服务器返回的信息中获取该信息 */ - private String md5Code = ""; + private String md5Code; /** - * 通过{@link AbsTaskEntity#dispositionKey}从服务器的返回信息中获取的文件描述信息 + * 从服务器的返回信息中获取的文件描述信息 */ - private String disposition = ""; + private String disposition; /** * 从disposition获取到的文件名,如果可以获取到,则会赋值到这个字段 */ - private String serverFileName = ""; + private String serverFileName; @Override public String getKey() { return getUrl(); @@ -101,13 +99,6 @@ public class DownloadEntity extends AbsNormalEntity implements Parcelable { this.groupName = groupName; } - /** - * {@link #getUrl()} - */ - @Deprecated public String getDownloadUrl() { - return getUrl(); - } - public String getDownloadPath() { return downloadPath; } @@ -117,8 +108,6 @@ public class DownloadEntity extends AbsNormalEntity implements Parcelable { return this; } - - @Override public DownloadEntity clone() throws CloneNotSupportedException { return (DownloadEntity) super.clone(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java index cb714c38..800a5974 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java @@ -18,8 +18,7 @@ package com.arialyy.aria.core.download; import android.os.Parcel; import com.arialyy.aria.core.inf.AbsGroupEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.orm.OneToMany; -import java.util.ArrayList; +import com.arialyy.aria.orm.annotation.Ignore; import java.util.List; /** @@ -28,22 +27,24 @@ import java.util.List; */ public class DownloadGroupEntity extends AbsGroupEntity { - @OneToMany(table = DownloadEntity.class, key = "groupName") private List subtask = - new ArrayList<>(); + @Ignore private List subEntities; /** * 任务组下载文件的文件夹地址 * - * @see DownloadGroupTarget#setDownloadDirPath(String) + * @see DownloadGroupTarget#setDirPath(String) */ - private String dirPath = ""; + private String dirPath; - public List getSubTask() { - return subtask; + /** + * 子任务实体列表 + */ + public List getSubEntities() { + return subEntities; } - public void setSubTasks(List subTasks) { - this.subtask = subTasks; + public void setSubEntities(List subTasks) { + this.subEntities = subTasks; } public String getDirPath() { @@ -71,13 +72,13 @@ public class DownloadGroupEntity extends AbsGroupEntity { @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); - dest.writeTypedList(this.subtask); + dest.writeTypedList(this.subEntities); dest.writeString(this.dirPath); } protected DownloadGroupEntity(Parcel in) { super(in); - this.subtask = in.createTypedArrayList(DownloadEntity.CREATOR); + this.subEntities = in.createTypedArrayList(DownloadEntity.CREATOR); this.dirPath = in.readString(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupListener.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupListener.java index 321a8311..806d341f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupListener.java @@ -86,7 +86,7 @@ class DownloadGroupListener private void saveCurrentLocation() { long location = 0; - for (DownloadEntity e : mEntity.getSubTask()) { + for (DownloadEntity e : mEntity.getSubEntities()) { location += e.getCurrentProgress(); } mEntity.setCurrentProgress(location); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java index 9bd63dd7..7de286cb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTarget.java @@ -15,45 +15,53 @@ */ package com.arialyy.aria.core.download; -import com.arialyy.aria.core.inf.IEntity; +import android.text.TextUtils; import com.arialyy.aria.core.manager.TEManager; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Created by AriaL on 2017/6/29. * 下载任务组 */ -public class DownloadGroupTarget - extends BaseGroupTarget { - private final String TAG = "DownloadGroupTarget"; +public class DownloadGroupTarget extends BaseGroupTarget { + /** + * 子任务下载地址, + */ + private List mUrls = new ArrayList<>(); + + /** + * 子任务文件名 + */ + private List mSubNameTemp = new ArrayList<>(); DownloadGroupTarget(DownloadGroupEntity groupEntity, String targetName) { this.mTargetName = targetName; if (groupEntity.getUrls() != null && !groupEntity.getUrls().isEmpty()) { this.mUrls.addAll(groupEntity.getUrls()); } - mGroupName = CommonUtil.getMd5Code(groupEntity.getUrls()); - mTaskEntity = TEManager.getInstance().getTEntity(DownloadGroupTaskEntity.class, mGroupName); - if (mTaskEntity == null) { - mTaskEntity = - TEManager.getInstance().createTEntity(DownloadGroupTaskEntity.class, groupEntity); - } - mEntity = mTaskEntity.entity; + init(); } DownloadGroupTarget(List urls, String targetName) { this.mTargetName = targetName; this.mUrls = urls; - mGroupName = CommonUtil.getMd5Code(urls); - mTaskEntity = TEManager.getInstance().getTEntity(DownloadGroupTaskEntity.class, mGroupName); - if (mTaskEntity == null) { - mTaskEntity = TEManager.getInstance().createGTEntity(DownloadGroupTaskEntity.class, mUrls); + init(); + } + + private void init() { + mGroupName = CommonUtil.getMd5Code(mUrls); + mTaskEntity = TEManager.getInstance().getGTEntity(DownloadGroupTaskEntity.class, mUrls); + mEntity = mTaskEntity.getEntity(); + + if (mEntity != null) { + mDirPathTemp = mEntity.getDirPath(); } - mEntity = mTaskEntity.entity; } /** @@ -71,7 +79,6 @@ public class DownloadGroupTarget } if (mEntity.getFileSize() <= 1 || mEntity.getFileSize() != fileSize) { mEntity.setFileSize(fileSize); - mEntity.update(); } return this; } @@ -80,16 +87,165 @@ public class DownloadGroupTarget * 如果你是使用{@link DownloadReceiver#load(DownloadGroupEntity)}进行下载操作,那么你需要设置任务组的下载地址 */ public DownloadGroupTarget setGroupUrl(List urls) { - CheckUtil.checkDownloadUrls(urls); mUrls.clear(); mUrls.addAll(urls); - mEntity.setGroupName(CommonUtil.getMd5Code(urls)); - mEntity.update(); return this; } - @Override public boolean isRunning() { - DownloadGroupTask task = DownloadGroupTaskQueue.getInstance().getTask(mEntity.getKey()); - return task != null && task.isRunning(); + /** + * 设置子任务文件名,该方法必须在{@link #setDirPath(String)}之后调用,否则不生效 + * + * @deprecated {@link #setSubFileName(List)} 请使用该api + */ + @Deprecated public DownloadGroupTarget setSubTaskFileName(List subTaskFileName) { + return setSubFileName(subTaskFileName); + } + + /** + * 设置子任务文件名,该方法必须在{@link #setDirPath(String)}之后调用,否则不生效 + */ + public DownloadGroupTarget setSubFileName(List subTaskFileName) { + if (subTaskFileName == null || subTaskFileName.isEmpty()) { + ALog.e(TAG, "修改子任务的文件名失败:列表为null"); + return this; + } + if (subTaskFileName.size() != mTaskEntity.getSubTaskEntities().size()) { + ALog.e(TAG, "修改子任务的文件名失败:子任务文件名列表数量和子任务的数量不匹配"); + return this; + } + mSubNameTemp.clear(); + mSubNameTemp.addAll(subTaskFileName); + return this; + } + + @Override protected int getTargetType() { + return GROUP_HTTP; + } + + @Override protected boolean checkEntity() { + if (getTargetType() == GROUP_HTTP) { + if (!checkDirPath()) { + return false; + } + + if (!checkSubName()) { + return false; + } + + if (!checkUrls()) { + return false; + } + + mEntity.save(); + mTaskEntity.save(); + + if (needModifyPath) { + reChangeDirPath(mDirPathTemp); + } + + if (!mSubNameTemp.isEmpty()) { + updateSingleSubFileName(); + } + return true; + } + return false; + } + + /** + * 更新所有改动的子任务文件名 + */ + private void updateSingleSubFileName() { + List entities = mTaskEntity.getSubTaskEntities(); + int i = 0; + for (DownloadTaskEntity entity : entities) { + if (i < mSubNameTemp.size()) { + String newName = mSubNameTemp.get(i); + updateSingleSubFileName(entity, newName); + } + i++; + } + } + + /** + * 检查urls是否合法,并删除不合法的子任务 + * + * @return {@code true} 合法 + */ + private boolean checkUrls() { + if (mUrls.isEmpty()) { + ALog.e(TAG, "下载失败,子任务下载列表为null"); + return false; + } + Set delItem = new HashSet<>(); + + int i = 0; + for (String url : mUrls) { + if (TextUtils.isEmpty(url)) { + ALog.e(TAG, "子任务url为null,即将删除该子任务。"); + delItem.add(i); + continue; + } else if (!url.startsWith("http")) { + //} else if (!url.startsWith("http") && !url.startsWith("ftp")) { + ALog.e(TAG, "子任务url【" + url + "】错误,即将删除该子任务。"); + delItem.add(i); + continue; + } + int index = url.indexOf("://"); + if (index == -1) { + ALog.e(TAG, "子任务url【" + url + "】不合法,即将删除该子任务。"); + delItem.add(i); + continue; + } + + i++; + } + + for (int index : delItem) { + mUrls.remove(index); + if (mSubNameTemp != null && !mSubNameTemp.isEmpty()) { + mSubNameTemp.remove(index); + } + } + + mEntity.setGroupName(CommonUtil.getMd5Code(mUrls)); + + return true; + } + + /** + * 更新单个子任务文件名 + */ + private void updateSingleSubFileName(DownloadTaskEntity taskEntity, String newName) { + DownloadEntity entity = taskEntity.getEntity(); + if (!newName.equals(entity.getFileName())) { + String oldPath = mEntity.getDirPath() + "/" + entity.getFileName(); + String newPath = mEntity.getDirPath() + "/" + newName; + File oldFile = new File(oldPath); + if (oldFile.exists()) { + oldFile.renameTo(new File(newPath)); + } + CommonUtil.renameDownloadConfig(oldFile.getName(), newName); + entity.setDownloadPath(newPath); + taskEntity.setKey(newPath); + entity.setFileName(newName); + entity.update(); + } + } + + /** + * 如果用户设置了子任务文件名,检查子任务文件名 + * + * @return {@code true} 合法 + */ + private boolean checkSubName() { + if (mSubNameTemp == null || mSubNameTemp.isEmpty()) { + return true; + } + if (mUrls.size() != mSubNameTemp.size()) { + ALog.e(TAG, "子任务文件名必须和子任务数量一致"); + return false; + } + + return true; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java index ee2b44b4..6977202a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java @@ -41,7 +41,7 @@ public class DownloadGroupTask extends AbsGroupTask { mOutHandler = outHandler; mContext = AriaManager.APP; mListener = new DownloadGroupListener(this, mOutHandler); - switch (taskEntity.requestType) { + switch (taskEntity.getRequestType()) { case AbsTaskEntity.D_HTTP: mUtil = new DownloadGroupUtil(mListener, mTaskEntity); break; @@ -71,15 +71,17 @@ public class DownloadGroupTask extends AbsGroupTask { @Override public void stop() { if (!mUtil.isRunning()) { mListener.onStop(getCurrentProgress()); + } else { + mUtil.stop(); } - mUtil.stop(); } @Override public void cancel() { if (!mUtil.isRunning()) { mListener.onCancel(); + } else { + mUtil.cancel(); } - mUtil.cancel(); } @Override public String getTaskName() { @@ -116,7 +118,6 @@ public class DownloadGroupTask extends AbsGroupTask { public DownloadGroupTask build() { DownloadGroupTask task = new DownloadGroupTask(taskEntity, outHandler); task.setTargetName(targetName); - taskEntity.save(); return task; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java index 72ecdc31..0fff9682 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTaskEntity.java @@ -16,24 +16,48 @@ package com.arialyy.aria.core.download; import com.arialyy.aria.core.inf.AbsGroupTaskEntity; -import com.arialyy.aria.orm.OneToOne; +import com.arialyy.aria.orm.ActionPolicy; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Ignore; +import com.arialyy.aria.orm.annotation.Primary; +import java.util.List; /** * Created by AriaL on 2017/7/1. + * 任务组的任务实体 */ public class DownloadGroupTaskEntity extends AbsGroupTaskEntity { - @OneToOne(table = DownloadGroupEntity.class, key = "groupName") public DownloadGroupEntity entity; + @Ignore private DownloadGroupEntity entity; + + @Ignore private List subTaskEntities; + + @Primary + @Foreign(parent = DownloadGroupEntity.class, column = "groupName", + onUpdate = ActionPolicy.CASCADE, onDelete = ActionPolicy.CASCADE) + private String key; @Override public DownloadGroupEntity getEntity() { return entity; } + public void setEntity(DownloadGroupEntity entity) { + this.entity = entity; + } + + public List getSubTaskEntities() { + return subTaskEntities; + } + + public void setSubTaskEntities(List subTaskEntities) { + this.subTaskEntities = subTaskEntities; + } + + @Override public String getKey() { + return key; + } - public void save(DownloadGroupEntity groupEntity){ - key = groupEntity.getKey(); - entity = groupEntity; - groupEntity.save(); - save(); + public void setKey(String key) { + this.key = key; } } 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 21ca515b..9fccd6d5 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 @@ -22,13 +22,15 @@ 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.download.wrapper.DGEWrapper; import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.AbsTarget; +import com.arialyy.aria.core.manager.TEManager; import com.arialyy.aria.core.scheduler.DownloadGroupSchedulers; import com.arialyy.aria.core.scheduler.DownloadSchedulers; -import com.arialyy.aria.core.scheduler.ISchedulerListener; 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; @@ -41,7 +43,6 @@ import java.util.Set; */ public class DownloadReceiver extends AbsReceiver { private final String TAG = "DownloadReceiver"; - public ISchedulerListener listener; /** * 设置最大下载速度,单位:kb @@ -49,8 +50,8 @@ public class DownloadReceiver extends AbsReceiver { * * @param maxSpeed 为0表示不限速 */ - @Deprecated public void setMaxSpeed(double maxSpeed) { - AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setMsxSpeed(maxSpeed); + @Deprecated public void setMaxSpeed(int maxSpeed) { + AriaManager.getInstance(AriaManager.APP).getDownloadConfig().setMaxSpeed(maxSpeed); } /** @@ -108,14 +109,25 @@ public class DownloadReceiver extends AbsReceiver { * */ @Deprecated public DownloadTarget load(@NonNull String url, boolean refreshInfo) { - url = CheckUtil.checkUrl(url); + CheckUtil.checkUrlInvalidThrow(url); return new DownloadTarget(url, targetName, refreshInfo); } /** * 加载下载地址,如果任务组的中的下载地址改变了,则任务从新的一个任务组 + * + * @param urls 任务组子任务下载地址列表 + * @deprecated {@link #loadGroup(DownloadGroupEntity)} */ + @Deprecated public DownloadGroupTarget load(List urls) { + return loadGroup(urls); + } + + /** + * 加载下载地址,如果任务组的中的下载地址改变了,则任务从新的一个任务组 + */ + public DownloadGroupTarget loadGroup(List urls) { CheckUtil.checkDownloadUrls(urls); return new DownloadGroupTarget(urls, targetName); } @@ -165,17 +177,29 @@ public class DownloadReceiver extends AbsReceiver { * @param refreshInfo 是否刷新下载信息 */ public FtpDownloadTarget loadFtp(@NonNull String url, boolean refreshInfo) { - url = CheckUtil.checkUrl(url); + CheckUtil.checkUrlInvalidThrow(url); return new FtpDownloadTarget(url, targetName, refreshInfo); } /** - * 使用任务组实体执行任务组的实体执行任务组的下载操作 + * 使用任务组实体执行任务组的实体执行任务组的下载操作,后续版本会删除该api * * @param groupEntity 如果加载的任务实体没有子项的下载地址, * 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址 + * @deprecated 请使用 {@link #loadGroup(DownloadGroupEntity)} */ + @Deprecated public DownloadGroupTarget load(DownloadGroupEntity groupEntity) { + return loadGroup(groupEntity); + } + + /** + * 使用任务组实体执行任务组的实体执行任务组的下载操作 + * + * @param groupEntity 如果加载的任务实体没有子项的下载地址, + * 那么你需要使用{@link DownloadGroupTarget#setGroupUrl(List)}设置子项的下载地址 + */ + public DownloadGroupTarget loadGroup(DownloadGroupEntity groupEntity) { return new DownloadGroupTarget(groupEntity, targetName); } @@ -183,7 +207,7 @@ public class DownloadReceiver extends AbsReceiver { * 加载ftp文件夹下载地址 */ public FtpDirDownloadTarget loadFtpDir(@NonNull String dirUrl) { - dirUrl = CheckUtil.checkUrl(dirUrl); + CheckUtil.checkUrlInvalidThrow(dirUrl); return new FtpDirDownloadTarget(dirUrl, targetName); } @@ -207,6 +231,7 @@ public class DownloadReceiver extends AbsReceiver { /** * 取消注册,如果是Activity或fragment,Aria会界面销毁时自动调用该方法。 + * 如果在activity中一定要调用该方法,那么请在onDestroy()中调用 * 如果是Dialog或popupwindow,需要你在撤销界面时调用该方法 */ @Override public void unRegister() { @@ -232,44 +257,59 @@ public class DownloadReceiver extends AbsReceiver { @Override public void destroy() { targetName = null; - listener = null; } /** * 通过下载链接获取下载实体 + * + * @return 如果url错误或查找不到数据,则返回null */ public DownloadEntity getDownloadEntity(String downloadUrl) { - downloadUrl = CheckUtil.checkUrl(downloadUrl); + if (CheckUtil.checkUrl(downloadUrl)) { + return null; + } return DbEntity.findFirst(DownloadEntity.class, "url=? and isGroupChild='false'", downloadUrl); } /** - * 通过下载链接获取保存在数据库的下载任务实体 + * 通过下载地址和文件保存路径获取下载任务实体 + * + * @param downloadUrl 下载地址 + * @return 如果url错误或查找不到数据,则返回null */ public DownloadTaskEntity getDownloadTask(String downloadUrl) { - downloadUrl = CheckUtil.checkUrl(downloadUrl); - DownloadEntity entity = getDownloadEntity(downloadUrl); - if (entity == null || TextUtils.isEmpty(entity.getDownloadPath())) return null; - return DbEntity.findFirst(DownloadTaskEntity.class, "key=? and isGroupTask='false'", - entity.getDownloadPath()); + if (CheckUtil.checkUrl(downloadUrl)) { + return null; + } + return TEManager.getInstance().getTEntity(DownloadTaskEntity.class, downloadUrl); } /** * 通过下载链接获取保存在数据库的下载任务组实体 + * + * @param urls 任务组子任务下载地址列表 + * @return 返回对应的任务组实体;如果查找不到对应的数据或子任务列表为null,返回null */ - public DownloadGroupTaskEntity getDownloadGroupTask(List urls) { - CheckUtil.checkDownloadUrls(urls); - String hashCode = CommonUtil.getMd5Code(urls); - return DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", hashCode); + public DownloadGroupTaskEntity getGroupTask(List urls) { + if (urls == null || urls.isEmpty()) { + ALog.e(TAG, "获取任务组实体失败:任务组子任务下载地址列表为null"); + return null; + } + return TEManager.getInstance().getGTEntity(DownloadGroupTaskEntity.class, urls); } /** - * 通过任务组key,获取任务组实体 - * 如果是http,key为所有子任务下载地址拼接后取md5 - * 如果是ftp,key为ftp服务器的文件夹路径 + * 获取FTP文件夹下载任务实体 + * + * @param dirUrl FTP文件夹本地下载路径 + * @return 返回对应的任务组实体;如果查找不到对应的数据或路径为null,返回null */ - public DownloadGroupTaskEntity getDownloadGroupTask(String key) { - return DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", key); + public DownloadGroupTaskEntity getFtpDirTask(String dirUrl) { + if (TextUtils.isEmpty(dirUrl)) { + ALog.e(TAG, "获取FTP文件夹实体失败:下载路径为null"); + return null; + } + return TEManager.getInstance().getFDTEntity(DownloadGroupTaskEntity.class, dirUrl); } /** @@ -307,15 +347,25 @@ public class DownloadReceiver extends AbsReceiver { /** * 获取任务组列表 + * + * @return 如果没有任务组列表,则返回null */ public List getGroupTaskList() { - return DownloadEntity.findAllData(DownloadGroupEntity.class); + List wrappers = DbEntity.findRelationData(DGEWrapper.class); + if (wrappers == null || wrappers.isEmpty()) { + return null; + } + List entities = new ArrayList<>(); + for (DGEWrapper wrapper : wrappers) { + entities.add(wrapper.groupEntity); + } + return entities; } /** * 获取普通任务和任务组的任务列表 */ - public List getTotleTaskList() { + public List getTotalTaskList() { List list = new ArrayList<>(); List simpleTask = getTaskList(); List groupTask = getGroupTaskList(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java index 5a8b20ed..cba839aa 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java @@ -16,35 +16,25 @@ package com.arialyy.aria.core.download; import android.support.annotation.NonNull; -import android.text.TextUtils; -import com.arialyy.aria.core.inf.AbsDownloadTarget; -import com.arialyy.aria.core.manager.TEManager; -import com.arialyy.aria.core.queue.DownloadTaskQueue; -import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.util.CommonUtil; -import java.io.File; +import com.arialyy.aria.core.common.RequestEnum; +import com.arialyy.aria.core.delegate.HttpHeaderDelegate; +import com.arialyy.aria.core.inf.IHttpHeaderTarget; +import java.util.Map; /** * Created by lyy on 2016/12/5. * https://github.com/AriaLyy/Aria */ -public class DownloadTarget - extends AbsDownloadTarget { - protected String url; +public class DownloadTarget extends BaseNormalTarget + implements IHttpHeaderTarget { + private HttpHeaderDelegate mDelegate; DownloadTarget(DownloadEntity entity, String targetName) { this(entity, targetName, false); } DownloadTarget(DownloadEntity entity, String targetName, boolean refreshInfo) { - this.url = entity.getUrl(); - mTargetName = targetName; - mTaskEntity = TEManager.getInstance().getTEntity(DownloadTaskEntity.class, url); - if (mTaskEntity == null) { - mTaskEntity = TEManager.getInstance().createTEntity(DownloadTaskEntity.class, entity); - } - mEntity = mTaskEntity.entity; - mTaskEntity.refreshInfo = refreshInfo; + this(entity.getUrl(), targetName, refreshInfo); } DownloadTarget(String url, String targetName) { @@ -52,14 +42,8 @@ public class DownloadTarget } DownloadTarget(String url, String targetName, boolean refreshInfo) { - this.url = url; - mTargetName = targetName; - mTaskEntity = TEManager.getInstance().getTEntity(DownloadTaskEntity.class, url); - if (mTaskEntity == null) { - mTaskEntity = TEManager.getInstance().createTEntity(DownloadTaskEntity.class, url); - } - mEntity = mTaskEntity.entity; - mTaskEntity.refreshInfo = refreshInfo; + initTarget(url, targetName, refreshInfo); + mDelegate = new HttpHeaderDelegate<>(this, mTaskEntity); } /** @@ -69,31 +53,20 @@ public class DownloadTarget * * @param use {@code true} 使用 */ - @Deprecated public DownloadTarget useServerFileName(boolean use) { - mTaskEntity.useServerFileName = use; + public DownloadTarget useServerFileName(boolean use) { + mTaskEntity.setUseServerFileName(use); return this; } /** - * 将任务设置为最高优先级任务,最高优先级任务有以下特点: - * 1、在下载队列中,有且只有一个最高优先级任务 - * 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 - * 3、任务调度器不会暂停最高优先级任务 - * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 - * 5、如果下载队列中已经满了,则会停止队尾的任务,当高优先级任务完成后,该队尾任务将自动执行 - * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 - */ - @Override public void setHighestPriority() { - super.setHighestPriority(); - } - - /** - * 下载任务是否存在 + * 设置文件存储路径 + * 该api后续版本会删除 * - * @return {@code true}任务存在 + * @param downloadPath 文件保存路径 + * @deprecated {@link #setFilePath(String)} 请使用这个api */ - @Override public boolean taskExists() { - return DownloadTaskQueue.getInstance().getTask(mEntity.getUrl()) != null; + @Deprecated public DownloadTarget setDownloadPath(@NonNull String downloadPath) { + return setFilePath(downloadPath); } /** @@ -101,38 +74,13 @@ public class DownloadTarget * 如:原文件路径 /mnt/sdcard/test.zip * 如果需要将test.zip改为game.zip,只需要重新设置文件路径为:/mnt/sdcard/game.zip * - * @param downloadPath 路径必须为文件路径,不能为文件夹路径 + * @param filePath 路径必须为文件路径,不能为文件夹路径 */ - public DownloadTarget setDownloadPath(@NonNull String downloadPath) { - if (TextUtils.isEmpty(downloadPath)) { - throw new IllegalArgumentException("文件保持路径不能为null"); - } - File file = new File(downloadPath); - if (file.isDirectory()) { - throw new IllegalArgumentException("保存路径不能为文件夹,路径需要是完整的文件路径,如:/mnt/sdcard/game.zip"); - } - if (!downloadPath.equals(mEntity.getDownloadPath())) { - if (!mTaskEntity.refreshInfo && DbEntity.checkDataExist(DownloadEntity.class, - "downloadPath=?", downloadPath)) { - throw new IllegalArgumentException("保存路径【" + downloadPath + "】已经被其它任务占用,请设置其它保存路径"); - } - File oldFile = new File(mEntity.getDownloadPath()); - File newFile = new File(downloadPath); - if (TextUtils.isEmpty(mEntity.getDownloadPath()) || oldFile.renameTo(newFile)) { - mEntity.setDownloadPath(downloadPath); - mEntity.setFileName(newFile.getName()); - mTaskEntity.key = downloadPath; - mTaskEntity.update(); - CommonUtil.renameDownloadConfig(oldFile.getName(), newFile.getName()); - } - } + public DownloadTarget setFilePath(@NonNull String filePath) { + mTempFilePath = filePath; return this; } - public DownloadEntity getDownloadEntity() { - return mEntity; - } - /** * 从header中获取文件描述信息 */ @@ -140,17 +88,19 @@ public class DownloadTarget return mEntity.getDisposition(); } - /** - * 是否在下载 - * - * @deprecated {@link #isRunning()} - */ - public boolean isDownloading() { - return isRunning(); + @Override protected int getTargetType() { + return HTTP; + } + + @Override public DownloadTarget addHeader(@NonNull String key, @NonNull String value) { + return mDelegate.addHeader(key, value); + } + + @Override public DownloadTarget addHeaders(Map headers) { + return mDelegate.addHeaders(headers); } - @Override public boolean isRunning() { - DownloadTask task = DownloadTaskQueue.getInstance().getTask(mEntity.getKey()); - return task != null && task.isRunning(); + @Override public DownloadTarget setRequestMode(RequestEnum requestEnum) { + return mDelegate.setRequestMode(requestEnum); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java index b45a9478..ea770433 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java @@ -18,7 +18,6 @@ package com.arialyy.aria.core.download; import android.os.Handler; import android.os.Looper; -import android.util.Log; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.common.IUtil; import com.arialyy.aria.core.download.downloader.SimpleDownloadUtil; @@ -78,16 +77,12 @@ public class DownloadTask extends AbsNormalTask { } /** - * 任务下载状态 + * 是否真正下载 * - * @see DownloadTask#isRunning() + * @return {@code true} 真正下载 */ - @Deprecated public boolean isDownloading() { - return mUtil.isRunning(); - } - @Override public boolean isRunning() { - return isDownloading(); + return mUtil.isRunning(); } public DownloadEntity getDownloadEntity() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java index f7d84ad5..9803d604 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java @@ -16,31 +16,51 @@ package com.arialyy.aria.core.download; import com.arialyy.aria.core.inf.AbsNormalTaskEntity; -import com.arialyy.aria.orm.NoNull; -import com.arialyy.aria.orm.OneToOne; +import com.arialyy.aria.orm.ActionPolicy; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Ignore; +import com.arialyy.aria.orm.annotation.NoNull; +import com.arialyy.aria.orm.annotation.Primary; /** * Created by lyy on 2017/1/23. - * 下载任务实体 + * 下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除 */ public class DownloadTaskEntity extends AbsNormalTaskEntity { - @OneToOne(table = DownloadEntity.class, key = "downloadPath") public DownloadEntity entity; + @Ignore private DownloadEntity entity; /** * 任务的url */ - @NoNull public String url = ""; + @NoNull private String url; /** * 所属的任务组组名,如果不属于任务组,则为null */ - public String groupName = ""; + @Foreign(parent = DownloadGroupTaskEntity.class, column = "key", + onUpdate = ActionPolicy.CASCADE, onDelete = ActionPolicy.CASCADE) + private String groupName; + + /** + * 是否是chunk模式 + */ + private boolean isChunked = false; /** * 该任务是否属于任务组 */ - public boolean isGroupTask = false; + private boolean isGroupTask = false; + + /** + * Task实体对应的key + */ + @Primary + @Foreign(parent = DownloadEntity.class, column = "downloadPath", + onUpdate = ActionPolicy.CASCADE, onDelete = ActionPolicy.CASCADE) + private String key; + + public DownloadTaskEntity() { } @@ -49,13 +69,47 @@ public class DownloadTaskEntity extends AbsNormalTaskEntity { return entity; } - public void save(DownloadEntity entity) { + @Override public String getKey() { + return key; + } + + public String getUrl() { + return url; + } + + public String getGroupName() { + return groupName; + } + + public boolean isChunked() { + return isChunked; + } + + public boolean isGroupTask() { + return isGroupTask; + } + + public void setEntity(DownloadEntity entity) { this.entity = entity; - if (entity != null) { - url = entity.getUrl(); - key = entity.getDownloadPath(); - entity.save(); - } - save(); + } + + public void setUrl(String url) { + this.url = url; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public void setChunked(boolean chunked) { + isChunked = chunked; + } + + public void setGroupTask(boolean groupTask) { + isGroupTask = groupTask; + } + + public void setKey(String key) { + this.key = key; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDownloadTarget.java index f7c51df7..60f99319 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDownloadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDirDownloadTarget.java @@ -16,18 +16,19 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; +import com.arialyy.aria.core.delegate.FtpDelegate; import com.arialyy.aria.core.inf.AbsTaskEntity; +import com.arialyy.aria.core.inf.IFtpTarget; import com.arialyy.aria.core.manager.TEManager; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; import com.arialyy.aria.util.ALog; /** * Created by Aria.Lao on 2017/7/26. * ftp文件夹下载 */ -public class FtpDirDownloadTarget - extends BaseGroupTarget { - private final String TAG = "FtpDirDownloadTarget"; +public class FtpDirDownloadTarget extends BaseGroupTarget + implements IFtpTarget { + private FtpDelegate mDelegate; FtpDirDownloadTarget(String url, String targetName) { mTargetName = targetName; @@ -36,57 +37,68 @@ public class FtpDirDownloadTarget private void init(String key) { mGroupName = key; - mTaskEntity = TEManager.getInstance().getTEntity(DownloadGroupTaskEntity.class, key); - if (mTaskEntity == null) { - mTaskEntity = TEManager.getInstance().createTEntity(DownloadGroupTaskEntity.class, key); + mTaskEntity = TEManager.getInstance().getFDTEntity(DownloadGroupTaskEntity.class, key); + mTaskEntity.setRequestType(AbsTaskEntity.D_FTP_DIR); + mEntity = mTaskEntity.getEntity(); + if (mEntity != null) { + mDirPathTemp = mEntity.getDirPath(); } - mTaskEntity.requestType = AbsTaskEntity.D_FTP_DIR; - mEntity = mTaskEntity.entity; + mDelegate = new FtpDelegate<>(this, mTaskEntity); } - /** - * 设置字符编码 - */ - public FtpDirDownloadTarget charSet(String charSet) { - if (TextUtils.isEmpty(charSet)) return this; - mTaskEntity.charSet = charSet; - return this; + @Override protected int getTargetType() { + return GROUP_FTP_DIR; } - /** - * ftp 用户登录信息 - * - * @param userName ftp用户名 - * @param password ftp用户密码 - */ - public FtpDirDownloadTarget login(String userName, String password) { - return login(userName, password, null); + @Override protected boolean checkEntity() { + boolean b = getTargetType() == GROUP_FTP_DIR && checkDirPath() && checkUrl(); + if (b) { + mEntity.save(); + mTaskEntity.save(); + if (mTaskEntity.getSubTaskEntities() != null) { + //初始化子项的登录信息 + for (DownloadTaskEntity entity : mTaskEntity.getSubTaskEntities()) { + entity.getUrlEntity().needLogin = mTaskEntity.getUrlEntity().needLogin; + entity.getUrlEntity().account = mTaskEntity.getUrlEntity().account; + entity.getUrlEntity().user = mTaskEntity.getUrlEntity().user; + entity.getUrlEntity().password = mTaskEntity.getUrlEntity().password; + } + } + } + return b; } /** - * ftp 用户登录信息 + * 检查普通任务的下载地址 * - * @param userName ftp用户名 - * @param password ftp用户密码 - * @param account ftp账号 + * @return {@code true}地址合法 */ - public FtpDirDownloadTarget login(String userName, String password, String account) { - if (TextUtils.isEmpty(userName)) { - ALog.e(TAG, "用户名不能为null"); - return this; - } else if (TextUtils.isEmpty(password)) { - ALog.e(TAG, "密码不能为null"); - return this; + private boolean checkUrl() { + final String url = mGroupName; + if (TextUtils.isEmpty(url)) { + ALog.e(TAG, "下载失败,url为null"); + return false; + } else if (!url.startsWith("ftp")) { + ALog.e(TAG, "下载失败,url【" + url + "】错误"); + return false; } - mTaskEntity.urlEntity.needLogin = true; - mTaskEntity.urlEntity.user = userName; - mTaskEntity.urlEntity.password = password; - mTaskEntity.urlEntity.account = account; - return this; + int index = url.indexOf("://"); + if (index == -1) { + ALog.e(TAG, "下载失败,url【" + url + "】不合法"); + return false; + } + return true; + } + + @Override public FtpDirDownloadTarget charSet(String charSet) { + return mDelegate.charSet(charSet); + } + + @Override public FtpDirDownloadTarget login(String userName, String password) { + return mDelegate.login(userName, password); } - @Override public boolean isRunning() { - DownloadGroupTask task = DownloadGroupTaskQueue.getInstance().getTask(mEntity.getKey()); - return task != null && task.isRunning(); + @Override public FtpDirDownloadTarget login(String userName, String password, String account) { + return mDelegate.login(userName, password, account); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java index ccc18455..81ae6413 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java @@ -16,22 +16,21 @@ package com.arialyy.aria.core.download; import android.support.annotation.NonNull; -import android.text.TextUtils; +import com.arialyy.aria.core.delegate.FtpDelegate; import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.util.ALog; +import com.arialyy.aria.core.inf.IFtpTarget; import com.arialyy.aria.util.CommonUtil; -import java.io.File; /** * Created by lyy on 2016/12/5. * https://github.com/AriaLyy/Aria */ -public class FtpDownloadTarget extends DownloadTarget { - private final String TAG = "FtpDownloadTarget"; +public class FtpDownloadTarget extends BaseNormalTarget + implements IFtpTarget { + private FtpDelegate mDelegate; FtpDownloadTarget(DownloadEntity entity, String targetName, boolean refreshInfo) { - super(entity, targetName, refreshInfo); - init(refreshInfo); + this(entity.getUrl(), targetName, refreshInfo); } FtpDownloadTarget(String url, String targetName) { @@ -39,16 +38,29 @@ public class FtpDownloadTarget extends DownloadTarget { } FtpDownloadTarget(String url, String targetName, boolean refreshInfo) { - super(url, targetName); + initTarget(url, targetName, refreshInfo); init(refreshInfo); } private void init(boolean refreshInfo) { int lastIndex = url.lastIndexOf("/"); mEntity.setFileName(url.substring(lastIndex + 1, url.length())); - mTaskEntity.urlEntity = CommonUtil.getFtpUrlInfo(url); - mTaskEntity.refreshInfo = refreshInfo; - mTaskEntity.requestType = AbsTaskEntity.D_FTP; + mTaskEntity.setUrlEntity(CommonUtil.getFtpUrlInfo(url)); + mTaskEntity.setRefreshInfo(refreshInfo); + mTaskEntity.setRequestType(AbsTaskEntity.D_FTP); + + mDelegate = new FtpDelegate<>(this, mTaskEntity); + } + + /** + * 设置文件保存文件夹路径 + * + * @param filePath 文件保存路径 + * @deprecated {@link #setFilePath(String)} 请使用这个api + */ + @Deprecated + public FtpDownloadTarget setDownloadPath(@NonNull String filePath) { + return setFilePath(filePath); } /** @@ -57,69 +69,26 @@ public class FtpDownloadTarget extends DownloadTarget { * 1、如果保存路径是该文件的保存路径,如:/mnt/sdcard/file.zip,则使用路径中的文件名file.zip * 2、如果保存路径是文件夹路径,如:/mnt/sdcard/,则使用FTP服务器该文件的文件名 * - * @param downloadPath 路径必须为文件路径,不能为文件夹路径 + * @param filePath 路径必须为文件路径,不能为文件夹路径 */ - @Override public FtpDownloadTarget setDownloadPath(@NonNull String downloadPath) { - if (TextUtils.isEmpty(downloadPath)) { - throw new IllegalArgumentException("文件保持路径不能为null"); - } - File file = new File(downloadPath); - if (file.isDirectory()) { - downloadPath += mEntity.getFileName(); - } - if (!downloadPath.equals(mEntity.getDownloadPath())) { - File oldFile = new File(mEntity.getDownloadPath()); - File newFile = new File(downloadPath); - if (TextUtils.isEmpty(mEntity.getDownloadPath()) || oldFile.renameTo(newFile)) { - mEntity.setDownloadPath(downloadPath); - mEntity.setFileName(newFile.getName()); - mTaskEntity.key = downloadPath; - mEntity.update(); - mTaskEntity.update(); - CommonUtil.renameDownloadConfig(oldFile.getName(), newFile.getName()); - } - } + public FtpDownloadTarget setFilePath(@NonNull String filePath) { + mTempFilePath = filePath; return this; } - /** - * 设置字符编码 - */ - public FtpDownloadTarget charSet(String charSet) { - if (TextUtils.isEmpty(charSet)) return this; - mTaskEntity.charSet = charSet; - return this; + @Override protected int getTargetType() { + return FTP; } - /** - * ftp 用户登录信息 - * - * @param userName ftp用户名 - * @param password ftp用户密码 - */ - public FtpDownloadTarget login(String userName, String password) { - return login(userName, password, null); + @Override public FtpDownloadTarget charSet(String charSet) { + return mDelegate.charSet(charSet); } - /** - * ftp 用户登录信息 - * - * @param userName ftp用户名 - * @param password ftp用户密码 - * @param account ftp账号 - */ - public FtpDownloadTarget login(String userName, String password, String account) { - if (TextUtils.isEmpty(userName)) { - ALog.e(TAG, "用户名不能为null"); - return this; - } else if (TextUtils.isEmpty(password)) { - ALog.e(TAG, "密码不能为null"); - return this; - } - mTaskEntity.urlEntity.needLogin = true; - mTaskEntity.urlEntity.user = userName; - mTaskEntity.urlEntity.password = password; - mTaskEntity.urlEntity.account = account; - return this; + @Override public FtpDownloadTarget login(String userName, String password) { + return mDelegate.login(userName, password); + } + + @Override public FtpDownloadTarget login(String userName, String password, String account) { + return mDelegate.login(userName, password, account); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java index 7bd318c5..8880a09d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/AbsGroupUtil.java @@ -16,20 +16,17 @@ package com.arialyy.aria.core.download.downloader; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.common.IUtil; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.NetUtils; import java.io.File; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; @@ -73,65 +70,31 @@ public abstract class AbsGroupUtil implements IUtil { Map mFailMap = new HashMap<>(); /** - * 下载器映射表,key为下载地址 + * 该任务组对应的所有任务 */ - private Map mDownloaderMap = new HashMap<>(); + private Map mTasksMap = new HashMap<>(); /** - * 该任务组对应的所有任务 + * 下载器映射表,key为下载地址 */ - private Map mTasksMap = new HashMap<>(); + private Map mDownloaderMap = new HashMap<>(); + /** * 是否需要读取文件长度,{@code true}需要 */ boolean isNeedLoadFileSize = true; //已经完成的任务数 - private int mCompleteNum = 0; - //失败的任务数 - private int mFailNum = 0; + int mCompleteNum = 0; //停止的任务数 private int mStopNum = 0; - //实际的下载任务数 - int mActualTaskNum = 0; - //初始化完成的任务数 - int mInitNum = 0; - // 初始化失败的任务数 - int mInitFailNum = 0; //任务组大小 int mGroupSize = 0; - long mUpdateInterval = 1000; + private long mUpdateInterval = 1000; AbsGroupUtil(IDownloadGroupListener listener, DownloadGroupTaskEntity groupEntity) { mListener = listener; mGTEntity = groupEntity; mExePool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); - List tasks = - DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mGTEntity.key); - if (tasks != null && !tasks.isEmpty()) { - for (DownloadTaskEntity te : tasks) { - te.removeFile = mGTEntity.removeFile; - if (te.getEntity() == null) continue; - mTasksMap.put(te.getEntity().getUrl(), te); - } - } - mGroupSize = mGTEntity.entity.getSubTask().size(); - mTotalLen = groupEntity.getEntity().getFileSize(); - isNeedLoadFileSize = mTotalLen <= 1; - for (DownloadEntity entity : mGTEntity.entity.getSubTask()) { - File file = new File(entity.getDownloadPath()); - if (entity.getState() == IEntity.STATE_COMPLETE && file.exists()) { - mCompleteNum++; - mCurrentLocation += entity.getFileSize(); - } else { - mExeMap.put(entity.getUrl(), createChildDownloadTask(entity)); - mCurrentLocation += file.exists() ? entity.getCurrentProgress() : 0; - mActualTaskNum++; - } - if (isNeedLoadFileSize) { - mTotalLen += entity.getFileSize(); - } - } - updateFileSize(); mUpdateInterval = AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getUpdateInterval(); } @@ -190,11 +153,9 @@ public abstract class AbsGroupUtil implements IUtil { * @param url 子任务下载地址 */ public void cancelSubTask(String url) { - List urls = mGTEntity.entity.getUrls(); - if (urls != null && !urls.isEmpty() && urls.contains(url)) { - urls.remove(url); - DownloadTaskEntity det = - DbEntity.findFirst(DownloadTaskEntity.class, "url=? and isGroupTask='true'", url); + Set urls = mTasksMap.keySet(); + if (!urls.isEmpty() && urls.contains(url)) { + DownloadTaskEntity det = mTasksMap.get(url); if (det != null) { mTotalLen -= det.getEntity().getFileSize(); mGroupSize--; @@ -260,7 +221,6 @@ public abstract class AbsGroupUtil implements IUtil { @Override public void cancel() { closeTimer(false); - mListener.onCancel(); onCancel(); if (!mExePool.isShutdown()) { mExePool.shutdown(); @@ -273,38 +233,15 @@ public abstract class AbsGroupUtil implements IUtil { dt.cancel(); } } - delDownloadInfo(); - mGTEntity.deleteData(); + clearState(); + CommonUtil.delDownloadGroupTaskConfig(mGTEntity.isRemoveFile(), mGTEntity.getEntity()); + mListener.onCancel(); } public void onCancel() { } - /** - * 删除所有子任务的下载信息 - */ - private void delDownloadInfo() { - List tasks = - DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", mGTEntity.key); - if (tasks != null && !tasks.isEmpty()) { - for (DownloadTaskEntity taskEntity : tasks) { - CommonUtil.delDownloadTaskConfig(mGTEntity.removeFile, taskEntity); - } - } - - File dir = new File(mGTEntity.getEntity().getDirPath()); - if (mGTEntity.removeFile) { - if (dir.exists()) { - dir.delete(); - } - } else { - if (!mGTEntity.getEntity().isComplete()) { - dir.delete(); - } - } - } - @Override public void stop() { closeTimer(false); onStop(); @@ -325,10 +262,35 @@ public abstract class AbsGroupUtil implements IUtil { } + /** + * 预处理操作,由于属性的不同,http任务组在构造函数中就可以完成了 + * 而FTP文件夹的,需要获取完成所有子任务信息才算预处理完成 + */ + protected void onPre() { + mListener.onPre(); + mGroupSize = mGTEntity.getSubTaskEntities().size(); + mTotalLen = mGTEntity.getEntity().getFileSize(); + isNeedLoadFileSize = mTotalLen <= 1; + for (DownloadTaskEntity te : mGTEntity.getSubTaskEntities()) { + File file = new File(te.getKey()); + if (te.getState() == IEntity.STATE_COMPLETE && file.exists()) { + mCompleteNum++; + mCurrentLocation += te.getEntity().getFileSize(); + } else { + mExeMap.put(te.getUrl(), te); + mCurrentLocation += file.exists() ? te.getEntity().getCurrentProgress() : 0; + } + if (isNeedLoadFileSize) { + mTotalLen += te.getEntity().getFileSize(); + } + mTasksMap.put(te.getUrl(), te); + } + updateFileSize(); + } + @Override public void start() { isRunning = true; - mFailNum = 0; - mListener.onPre(); + clearState(); onStart(); } @@ -345,6 +307,11 @@ public abstract class AbsGroupUtil implements IUtil { } + private void clearState(){ + mDownloaderMap.clear(); + mFailMap.clear(); + } + private void closeTimer(boolean isRunning) { this.isRunning = isRunning; if (mTimer != null) { @@ -389,7 +356,7 @@ public abstract class AbsGroupUtil implements IUtil { * * @param start 是否启动下载 */ - Downloader createChildDownload(DownloadTaskEntity taskEntity, boolean start) { + private Downloader createChildDownload(DownloadTaskEntity taskEntity, boolean start) { ChildDownloadListener listener = new ChildDownloadListener(taskEntity); Downloader dt = new Downloader(listener, taskEntity); mDownloaderMap.put(taskEntity.getEntity().getUrl(), dt); @@ -400,44 +367,6 @@ public abstract class AbsGroupUtil implements IUtil { return dt; } - /** - * 创建子任务下载信息 - */ - DownloadTaskEntity createChildDownloadTask(DownloadEntity entity) { - DownloadTaskEntity taskEntity = mTasksMap.get(entity.getUrl()); - if (taskEntity != null) { - taskEntity.entity = entity; - if (getTaskType() == FTP_DIR) { - taskEntity.urlEntity = createFtpUrlEntity(entity); - } - mTasksMap.put(entity.getUrl(), taskEntity); - return taskEntity; - } - taskEntity = new DownloadTaskEntity(); - taskEntity.entity = entity; - taskEntity.headers = mGTEntity.headers; - taskEntity.requestEnum = mGTEntity.requestEnum; - taskEntity.redirectUrlKey = mGTEntity.redirectUrlKey; - taskEntity.removeFile = mGTEntity.removeFile; - taskEntity.groupName = mGTEntity.key; - taskEntity.isGroupTask = true; - taskEntity.requestType = mGTEntity.requestType; - taskEntity.key = entity.getDownloadPath(); - if (getTaskType() == FTP_DIR) { - taskEntity.urlEntity = createFtpUrlEntity(entity); - } - taskEntity.save(); - mTasksMap.put(entity.getUrl(), taskEntity); - return taskEntity; - } - - private FtpUrlEntity createFtpUrlEntity(DownloadEntity entity) { - FtpUrlEntity urlEntity = mGTEntity.urlEntity.clone(); - urlEntity.url = entity.getUrl(); - urlEntity.remotePath = CommonUtil.getRemotePath(entity.getUrl()); - return urlEntity; - } - /** * 子任务事件监听 */ @@ -498,7 +427,7 @@ public abstract class AbsGroupUtil implements IUtil { mListener.onSubStop(subEntity); synchronized (AbsGroupUtil.class) { mStopNum++; - if (mStopNum + mCompleteNum + mInitFailNum + mFailNum >= mGroupSize) { + if (mStopNum + mCompleteNum + mFailMap.size() == mGroupSize) { closeTimer(false); mListener.onStop(mCurrentLocation); } @@ -515,13 +444,13 @@ public abstract class AbsGroupUtil implements IUtil { saveData(IEntity.STATE_COMPLETE, subEntity.getFileSize()); handleSpeed(0); mListener.onSubComplete(subEntity); - synchronized (AbsGroupUtil.class) { + synchronized (ChildDownloadListener.class) { mCompleteNum++; //如果子任务完成的数量和总任务数一致,表示任务组任务已经完成 if (mCompleteNum >= mGroupSize) { closeTimer(false); mListener.onComplete(); - } else if (mStopNum + mCompleteNum + mInitFailNum + mFailNum >= mGroupSize) { + } else if (mFailMap.size() > 0 && mStopNum + mCompleteNum + mFailMap.size() >= mGroupSize) { //如果子任务完成数量加上失败的数量和总任务数一致,则任务组停止下载 closeTimer(false); mListener.onStop(mCurrentLocation); @@ -537,20 +466,23 @@ public abstract class AbsGroupUtil implements IUtil { } /** - * 失败后重试下载,如果失败次数超过5次,不再重试 + * 重试下载 */ private void reTry(boolean needRetry) { - synchronized (AriaManager.LOCK) { - if (subEntity.getFailNum() < 5 && isRunning && needRetry && NetUtils.isConnected( - AriaManager.APP)) { + synchronized (ChildDownloadListener.class) { + if (subEntity.getFailNum() < 5 && needRetry && NetUtils.isConnected(AriaManager.APP)) { reStartTask(); } else { - mFailNum++; + mFailMap.put(subTaskEntity.getUrl(), subTaskEntity); mListener.onSubFail(subEntity); //如果失败的任务数大于实际的下载任务数,任务组停止下载 - if (mFailNum >= mActualTaskNum) { + if (mFailMap.size() >= mExeMap.size()) { closeTimer(false); - mListener.onStop(mCurrentLocation); + if (mFailMap.size() == mGroupSize) { //所有任务都失败了,则认为该任务组已经失败 + mListener.onFail(true); + } else { + mListener.onStop(mCurrentLocation); + } } } } @@ -574,11 +506,11 @@ public abstract class AbsGroupUtil implements IUtil { } private void saveData(int state, long location) { - subTaskEntity.state = state; + subTaskEntity.setState(state); subEntity.setState(state); subEntity.setComplete(state == IEntity.STATE_COMPLETE); if (state == IEntity.STATE_CANCEL) { - subTaskEntity.deleteData(); + subEntity.deleteData(); return; } else if (subEntity.isComplete()) { subEntity.setCompleteTime(System.currentTimeMillis()); 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 a4a2dfd4..0803249a 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 @@ -15,18 +15,21 @@ */ package com.arialyy.aria.core.download.downloader; +import android.text.TextUtils; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.util.SSLContextUtil; import java.io.IOException; +import java.io.InputStream; import java.net.HttpURLConnection; import java.net.ProtocolException; import java.net.URL; import java.net.URLConnection; import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; -import org.apache.commons.net.ftp.FTPClient; /** * Created by lyy on 2017/1/18. @@ -34,6 +37,26 @@ import org.apache.commons.net.ftp.FTPClient; */ class ConnectionHelp { + /** + * 转换HttpUrlConnect的inputStream流 + * + * @return {@link GZIPInputStream}、{@link InflaterInputStream} + * @throws IOException + */ + static InputStream convertInputStream(HttpURLConnection connection) throws IOException { + String encoding = connection.getContentEncoding(); + if (TextUtils.isEmpty(encoding)) { + return connection.getInputStream(); + } + if (encoding.contains("gzip")) { + return new GZIPInputStream(connection.getInputStream()); + } else if (encoding.contains("deflate")) { + return new InflaterInputStream(connection.getInputStream()); + } else { + return connection.getInputStream(); + } + } + /** * 处理链接 * @@ -65,12 +88,12 @@ class ConnectionHelp { */ static HttpURLConnection setConnectParam(DownloadTaskEntity entity, HttpURLConnection conn) throws ProtocolException { - conn.setRequestMethod(entity.requestEnum.name); + conn.setRequestMethod(entity.getRequestEnum().name); Set keys = null; - if (entity.headers != null && entity.headers.size() > 0) { - keys = entity.headers.keySet(); + if (entity.getHeaders() != null && entity.getHeaders().size() > 0) { + keys = entity.getHeaders().keySet(); for (String key : keys) { - conn.setRequestProperty(key, entity.headers.get(key)); + conn.setRequestProperty(key, entity.getHeaders().get(key)); } } if (keys == null || !keys.contains("Charset")) { @@ -86,6 +109,7 @@ class ConnectionHelp { .append("image/jpeg, ") .append("image/pjpeg, ") .append("image/webp, ") + .append("image/apng, ") .append("application/xml, ") .append("application/xaml+xml, ") .append("application/xhtml+xml, ") diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java index 937f535f..32949d9f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/DownloadGroupUtil.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download.downloader; import android.util.SparseArray; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.IUtil; import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; @@ -33,6 +34,7 @@ import java.util.concurrent.Executors; public class DownloadGroupUtil extends AbsGroupUtil implements IUtil { private final String TAG = "DownloadGroupUtil"; private ExecutorService mInfoPool; + private int mInitCompleteNum, mInitFailNum; /** * 文件信息回调组 @@ -42,6 +44,7 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil { public DownloadGroupUtil(IDownloadGroupListener listener, DownloadGroupTaskEntity taskEntity) { super(listener, taskEntity); mInfoPool = Executors.newCachedThreadPool(); + onPre(); } @Override int getTaskType() { @@ -64,26 +67,28 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil { @Override protected void onStart() { super.onStart(); - if (mExeMap.size() == 0){ + if (mCompleteNum == mGroupSize) { + mListener.onComplete(); + return; + } + + if (mExeMap.size() == 0) { ALog.e(TAG, "任务组无可执行任务"); mListener.onFail(false); return; } Set keys = mExeMap.keySet(); - int i = 0; for (String key : keys) { DownloadTaskEntity taskEntity = mExeMap.get(key); if (taskEntity != null) { if (taskEntity.getState() != IEntity.STATE_FAIL && taskEntity.getState() != IEntity.STATE_WAIT) { createChildDownload(taskEntity); - i++; } else { mInfoPool.execute(createFileInfoThread(taskEntity)); } } } - if (i != 0 && i == mExeMap.size()) startRunningFlow(); if (mCurrentLocation == mTotalLen) { mListener.onComplete(); } @@ -99,7 +104,7 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil { callback = new OnFileInfoCallback() { int failNum = 0; - @Override public void onComplete(String url, int code) { + @Override public void onComplete(String url, CompleteInfo info) { DownloadTaskEntity te = mExeMap.get(url); if (te != null) { if (isNeedLoadFileSize) { @@ -107,32 +112,32 @@ public class DownloadGroupUtil extends AbsGroupUtil implements IUtil { } createChildDownload(te); } - mInitNum++; - if (mInitNum + mInitFailNum >= mGTEntity.getEntity().getSubTask().size() - || !isNeedLoadFileSize) { + mInitCompleteNum ++; + + if (mInitCompleteNum + mInitFailNum >= mGroupSize || !isNeedLoadFileSize) { startRunningFlow(); updateFileSize(); } } @Override public void onFail(String url, String errorMsg, boolean needRetry) { + ALog.e(TAG, "任务【" + url + "】初始化失败。"); DownloadTaskEntity te = mExeMap.get(url); if (te != null) { mFailMap.put(url, te); mFileInfoCallbacks.put(te.hashCode(), this); + mExeMap.remove(url); } //404链接不重试下载 - if (failNum < 10 && !errorMsg.contains("错误码:404") && !errorMsg.contains( - "UnknownHostException")) { - mInfoPool.execute(createFileInfoThread(te)); - } else { - mInitFailNum++; - mActualTaskNum--; - if (mActualTaskNum < 0) mActualTaskNum = 0; - } - failNum++; - if (mInitNum + mInitFailNum >= mGTEntity.getEntity().getSubTask().size() - || !isNeedLoadFileSize) { + //if (failNum < 3 && !errorMsg.contains("错误码:404") && !errorMsg.contains( + // "UnknownHostException")) { + // mInfoPool.execute(createFileInfoThread(te)); + //} else { + // mInitFailNum++; + //} + //failNum++; + mInitFailNum ++; + if (mInitCompleteNum + mInitFailNum >= mGroupSize || !isNeedLoadFileSize) { startRunningFlow(); updateFileSize(); } 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 e72c97bd..de1780e1 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 @@ -44,14 +44,20 @@ class Downloader extends AbsFileer { AriaManager.getInstance(AriaManager.APP).getDownloadConfig().getUpdateInterval()); } + @Override protected int setNewTaskThreadNum() { + return mEntity.getFileSize() <= SUB_LEN || mTaskEntity.getRequestType() == AbsTaskEntity.D_FTP_DIR + ? 1 + : AriaManager.getInstance(mContext).getDownloadConfig().getThreadNum(); + } + @Override protected void checkTask() { mConfigFile = new File(CommonUtil.getFileConfigPath(true, mEntity.getFileName())); mTempFile = new File(mEntity.getDownloadPath()); - if (!mTaskEntity.isSupportBP) { + if (!mTaskEntity.isSupportBP()) { isNewTask = true; return; } - if (mTaskEntity.isNewTask) { + if (mTaskEntity.isNewTask()) { isNewTask = true; return; } @@ -67,20 +73,21 @@ class Downloader extends AbsFileer { } } - @Override protected void handleNewTask() { + @Override protected boolean handleNewTask() { CommonUtil.createFile(mTempFile.getPath()); BufferedRandomAccessFile file = null; try { file = new BufferedRandomAccessFile(new File(mTempFile.getPath()), "rwd", 8192); //设置文件长度 file.setLength(mEntity.getFileSize()); + return true; } catch (IOException e) { failDownload("下载失败【downloadUrl:" + mEntity.getUrl() + "】\n【filePath:" + mEntity.getDownloadPath() + "】\n" - + CommonUtil.getPrintException(e)); + + ALog.getExceptionString(e)); } finally { if (file != null) { try { @@ -90,10 +97,11 @@ class Downloader extends AbsFileer { } } } + return false; } @Override protected AbsThreadTask selectThreadTask(SubThreadConfig config) { - switch (mTaskEntity.requestType) { + switch (mTaskEntity.getRequestType()) { case AbsTaskEntity.D_FTP: case AbsTaskEntity.D_FTP_DIR: return new FtpThreadTask(mConstance, (IDownloadListener) mListener, config); @@ -108,6 +116,6 @@ class Downloader extends AbsFileer { ALog.e(TAG, errorMsg); mConstance.isRunning = false; mListener.onFail(false); - ErrorHelp.saveError("", mEntity, "", errorMsg); + ErrorHelp.saveError(TAG, "", errorMsg); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java index cba16bd5..00ec7df8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirDownloadUtil.java @@ -15,8 +15,8 @@ */ package com.arialyy.aria.core.download.downloader; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.OnFileInfoCallback; -import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.util.ErrorHelp; @@ -27,6 +27,8 @@ import java.util.Set; * ftp文件夹下载工具 */ public class FtpDirDownloadUtil extends AbsGroupUtil { + private String TAG = "FtpDirDownloadUtil"; + public FtpDirDownloadUtil(IDownloadGroupListener listener, DownloadGroupTaskEntity taskEntity) { super(listener, taskEntity); } @@ -38,30 +40,35 @@ public class FtpDirDownloadUtil extends AbsGroupUtil { @Override protected void onStart() { super.onStart(); if (mGTEntity.getEntity().getFileSize() > 1) { + onPre(); startDownload(); } else { new FtpDirInfoThread(mGTEntity, new OnFileInfoCallback() { - @Override public void onComplete(String url, int code) { - if (code >= 200 && code < 300) { - for (DownloadEntity entity : mGTEntity.entity.getSubTask()) { - mExeMap.put(entity.getUrl(), createChildDownloadTask(entity)); - } - mActualTaskNum = mGTEntity.entity.getSubTask().size(); - mGroupSize = mActualTaskNum; - mTotalLen = mGTEntity.entity.getFileSize(); + @Override public void onComplete(String url, CompleteInfo info) { + if (info.code >= 200 && info.code < 300) { + onPre(); startDownload(); } } @Override public void onFail(String url, String errorMsg, boolean needRetry) { + DownloadTaskEntity te = mExeMap.get(url); + if (te != null) { + mFailMap.put(url, te); + mExeMap.remove(url); + } mListener.onFail(needRetry); - ErrorHelp.saveError("D_FTP_DIR", mGTEntity.getEntity(), "", errorMsg); + ErrorHelp.saveError(TAG, "", errorMsg); } }).start(); } } private void startDownload() { + if (mCompleteNum == mGroupSize) { + mListener.onComplete(); + return; + } int i = 0; Set keys = mExeMap.keySet(); for (String key : keys) { @@ -71,6 +78,10 @@ public class FtpDirDownloadUtil extends AbsGroupUtil { i++; } } - if (i == mExeMap.size()) startRunningFlow(); + if (mExeMap.size() == 0) { + mListener.onComplete(); + } else if (i == mExeMap.size()) { + startRunningFlow(); + } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirInfoThread.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirInfoThread.java index caec83c6..7c6ab6eb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirInfoThread.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpDirInfoThread.java @@ -17,10 +17,13 @@ package com.arialyy.aria.core.download.downloader; import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.common.AbsFtpInfoThread; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.util.CommonUtil; import java.nio.charset.Charset; import java.util.ArrayList; @@ -37,7 +40,7 @@ class FtpDirInfoThread extends AbsFtpInfoThread()); } - if (mEntity.getSubTask() == null) { - mEntity.setSubTasks(new ArrayList()); - } - mEntity.getSubTask().add(entity); + mEntity.getSubEntities().add(entity); + mTaskEntity.getSubTaskEntities().add(taskEntity); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpFileInfoThread.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpFileInfoThread.java index 42dc1900..acb3371a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpFileInfoThread.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpFileInfoThread.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download.downloader; import com.arialyy.aria.core.common.AbsFtpInfoThread; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; @@ -31,15 +32,15 @@ class FtpFileInfoThread extends AbsFtpInfoThread { - private final String TAG = "FtpDownloadThreadTask"; + private final String TAG = "FtpThreadTask"; FtpThreadTask(StateConstance constance, IDownloadListener listener, SubThreadConfig downloadInfo) { @@ -71,7 +71,7 @@ class FtpThreadTask extends AbsFtpThreadTask return; } String remotePath = - new String(mTaskEntity.urlEntity.remotePath.getBytes(charSet), SERVER_CHARSET); + new String(mTaskEntity.getUrlEntity().remotePath.getBytes(charSet), SERVER_CHARSET); ALog.i(TAG, "remotePath【" + remotePath + "】"); is = client.retrieveFileStream(remotePath); reply = client.getReplyCode(); 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 c2cdafd0..63634d0f 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 @@ -17,13 +17,17 @@ package com.arialyy.aria.core.download.downloader; import android.text.TextUtils; import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; +import java.io.BufferedReader; +import java.io.File; import java.io.IOException; +import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLDecoder; @@ -54,7 +58,7 @@ class HttpFileInfoThread implements Runnable { conn = ConnectionHelp.setConnectParam(mTaskEntity, conn); conn.setRequestProperty("Range", "bytes=" + 0 + "-"); conn.setConnectTimeout(mConnectTimeOut); - conn.setRequestMethod(mTaskEntity.requestEnum.name); + //conn.setChunkedStreamingMode(0); conn.connect(); handleConnect(conn); } catch (IOException e) { @@ -63,7 +67,7 @@ class HttpFileInfoThread implements Runnable { + "】\n【filePath:" + mEntity.getDownloadPath() + "】\n" - + CommonUtil.getPrintException(e), true); + + ALog.getExceptionString(e), true); } finally { if (conn != null) { conn.disconnect(); @@ -74,62 +78,122 @@ class HttpFileInfoThread implements Runnable { private void handleConnect(HttpURLConnection conn) throws IOException { long len = conn.getContentLength(); if (len < 0) { - String temp = conn.getHeaderField(mTaskEntity.contentLength); + String temp = conn.getHeaderField("Content-Length"); len = TextUtils.isEmpty(temp) ? -1 : Long.parseLong(temp); + // 某些服务,如果设置了conn.setRequestProperty("Range", "bytes=" + 0 + "-"); + // 会返回 Content-Range: bytes 0-225427911/225427913 + if (len < 0) { + temp = conn.getHeaderField("Content-Range"); + if (TextUtils.isEmpty(temp)) { + len = -1; + } else { + int start = temp.indexOf("/"); + len = Long.parseLong(temp.substring(start + 1, temp.length())); + } + } } int code = conn.getResponseCode(); - boolean isComplete = false; + boolean end = false; if (TextUtils.isEmpty(mEntity.getMd5Code())) { - String md5Code = conn.getHeaderField(mTaskEntity.md5Key); + String md5Code = conn.getHeaderField("Content-MD5"); mEntity.setMd5Code(md5Code); } - String disposition = conn.getHeaderField(mTaskEntity.dispositionKey); + + boolean isChunked = false; + final String str = conn.getHeaderField("Transfer-Encoding"); + if (!TextUtils.isEmpty(str) && str.equals("chunked")) { + isChunked = true; + } //Map> headers = conn.getHeaderFields(); - if (!TextUtils.isEmpty(disposition)) { + String disposition = conn.getHeaderField("Content-Disposition"); + if (mTaskEntity.isUseServerFileName() && !TextUtils.isEmpty(disposition)) { mEntity.setDisposition(CommonUtil.encryptBASE64(disposition)); - if (disposition.contains(mTaskEntity.dispositionFileKey)) { - String[] infos = disposition.split("="); - mEntity.setServerFileName(URLDecoder.decode(infos[1], "utf-8")); + 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"); + mEntity.setServerFileName(newName); + fileRename(newName); + break; + } + } + } } } - mTaskEntity.code = code; + mTaskEntity.setCode(code); if (code == HttpURLConnection.HTTP_PARTIAL) { - if (!checkLen(len)) return; + if (!checkLen(len) && !isChunked) { + return; + } mEntity.setFileSize(len); - mTaskEntity.isSupportBP = true; - isComplete = true; + mTaskEntity.setSupportBP(true); + end = true; } else if (code == HttpURLConnection.HTTP_OK) { - if (!checkLen(len)) return; + if (conn.getHeaderField("Content-Type").equals("text/html")) { + BufferedReader reader = + new BufferedReader(new InputStreamReader(ConnectionHelp.convertInputStream(conn))); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + reader.close(); + handleUrlReTurn(conn, CommonUtil.getWindowReplaceUrl(sb.toString())); + return; + } else if (!checkLen(len) && !isChunked) { + return; + } mEntity.setFileSize(len); - mTaskEntity.isSupportBP = false; - isComplete = true; + mTaskEntity.setSupportBP(false); + end = true; } else if (code == HttpURLConnection.HTTP_NOT_FOUND) { failDownload("任务【" + mEntity.getUrl() + "】下载失败,错误码:404", true); } else if (code == HttpURLConnection.HTTP_MOVED_TEMP || code == HttpURLConnection.HTTP_MOVED_PERM || code == HttpURLConnection.HTTP_SEE_OTHER) { - mTaskEntity.redirectUrl = conn.getHeaderField(mTaskEntity.redirectUrlKey); - mEntity.setRedirect(true); - mEntity.setRedirectUrl(mTaskEntity.redirectUrl); - handle302Turn(conn); + handleUrlReTurn(conn, conn.getHeaderField("Location")); } else { failDownload("任务【" + mEntity.getUrl() + "】下载失败,错误码:" + code, true); } - if (isComplete) { + if (end) { + mTaskEntity.setChunked(isChunked); + mTaskEntity.update(); if (onFileInfoListener != null) { - onFileInfoListener.onComplete(mEntity.getUrl(), code); + CompleteInfo info = new CompleteInfo(code); + onFileInfoListener.onComplete(mEntity.getUrl(), info); } - mTaskEntity.update(); } } + /** + * 重命名文件 + */ + private void fileRename(String newName) { + if (TextUtils.isEmpty(newName)) { + ALog.w(TAG, "重命名失败【服务器返回的文件名为空】"); + return; + } + File oldFile = new File(mEntity.getDownloadPath()); + String oldName = oldFile.getName(); + String newPath = oldFile.getParent() + "/" + newName; + if (oldFile.exists()){ + oldFile.renameTo(new File(newPath)); + } + mEntity.setFileName(newName); + mEntity.setDownloadPath(newPath); + mTaskEntity.setKey(newPath); + CommonUtil.renameDownloadConfig(oldName, newName); + } + /** * 处理30x跳转 */ - private void handle302Turn(HttpURLConnection conn) throws IOException { - String newUrl = conn.getHeaderField(mTaskEntity.redirectUrlKey); - ALog.d(TAG, "30x跳转,location【 " + mTaskEntity.redirectUrlKey + "】" + "新url为【" + newUrl + "】"); + private void handleUrlReTurn(HttpURLConnection conn, String newUrl) throws IOException { + ALog.d(TAG, "30x跳转,新url为【" + newUrl + "】"); if (TextUtils.isEmpty(newUrl) || newUrl.equalsIgnoreCase("null") || !newUrl.startsWith( "http")) { if (onFileInfoListener != null) { @@ -137,7 +201,13 @@ class HttpFileInfoThread implements Runnable { } return; } - newUrl = CheckUtil.checkUrl(newUrl); + if (!CheckUtil.checkUrl(newUrl)) { + failDownload("下载失败,重定向url错误", false); + return; + } + mTaskEntity.setRedirectUrl(newUrl); + mEntity.setRedirect(true); + mEntity.setRedirectUrl(newUrl); String cookies = conn.getHeaderField("Set-Cookie"); conn = (HttpURLConnection) new URL(newUrl).openConnection(); conn = ConnectionHelp.setConnectParam(mTaskEntity, conn); @@ -157,7 +227,7 @@ class HttpFileInfoThread implements Runnable { */ private boolean checkLen(long len) { if (len != mEntity.getFileSize()) { - mTaskEntity.isNewTask = true; + mTaskEntity.setNewTask(true); } if (len < 0) { failDownload("任务【" + mEntity.getUrl() + "】下载失败,文件长度小于0", 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 8b6f09c5..333dfff7 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 @@ -27,6 +27,8 @@ import com.arialyy.aria.util.CommonUtil; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; @@ -71,49 +73,23 @@ final class HttpThreadTask extends AbsThreadTask 0) { - Thread.sleep(mSleepTime); - } - file.write(buffer, 0, len); - progress(len); + + if (mTaskEntity.isChunked()) { + readChunk(is, file); + } else { + readNormal(is, file); } - if (STATE.isCancel || STATE.isStop){ + + if (STATE.isCancel || STATE.isStop) { return; } - //支持断点的处理 - if (mConfig.SUPPORT_BP) { - ALog.i(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】线程__" + mConfig.THREAD_ID + "__下载完毕"); - writeConfig(true, 1); - STATE.COMPLETE_THREAD_NUM++; - if (STATE.isComplete()) { - File configFile = new File(mConfigFPath); - if (configFile.exists()) { - configFile.delete(); - } - STATE.isRunning = false; - mListener.onComplete(); - } - if (STATE.isFail()){ - STATE.isRunning = false; - mListener.onFail(false); - } - } else { - ALog.i(TAG, "任务下载完成"); - STATE.isRunning = false; - mListener.onComplete(); - } + handleComplete(); } catch (MalformedURLException e) { fail(mChildCurrentLocation, "下载链接异常", e); } catch (IOException e) { @@ -137,6 +113,69 @@ final class HttpThreadTask extends AbsThreadTask 0) { + Thread.sleep(mSleepTime); + } + file.write(buffer, 0, len); + progress(len); + } + } + + /** + * 处理完成配置文件的更新或事件回调 + * + * @throws IOException + */ + private void handleComplete() throws IOException { + //支持断点的处理 + if (mConfig.SUPPORT_BP) { + if (mChildCurrentLocation == mConfig.END_LOCATION) { + ALog.i(TAG, "任务【" + mConfig.TEMP_FILE.getName() + "】线程__" + mConfig.THREAD_ID + "__下载完毕"); + writeConfig(true, 1); + STATE.COMPLETE_THREAD_NUM++; + if (STATE.isComplete()) { + File configFile = new File(mConfigFPath); + if (configFile.exists()) { + configFile.delete(); + } + STATE.isRunning = false; + mListener.onComplete(); + } + } else { + STATE.FAIL_NUM++; + } + if (STATE.isFail()) { + STATE.isRunning = false; + mListener.onFail(false); + } + } else { + ALog.i(TAG, "任务下载完成"); + STATE.isRunning = false; + mListener.onComplete(); + } + } + @Override protected String getTaskType() { return "HTTP_DOWNLOAD"; } 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 ee99b3a5..9d43adac 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 @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download.downloader; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.IUtil; import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.download.DownloadTaskEntity; @@ -28,7 +29,7 @@ import com.arialyy.aria.util.ErrorHelp; * D_HTTP\FTP单任务下载工具 */ public class SimpleDownloadUtil implements IUtil, Runnable { - private static final String TAG = "SimpleDownloadUtil"; + private String TAG = "SimpleDownloadUtil"; private IDownloadListener mListener; private Downloader mDownloader; private DownloadTaskEntity mTaskEntity; @@ -85,14 +86,14 @@ public class SimpleDownloadUtil implements IUtil, Runnable { private void failDownload(String msg, boolean needRetry) { mListener.onFail(needRetry); - ErrorHelp.saveError("HTTP_DOWNLOAD", mTaskEntity.getEntity(), msg, ""); + ErrorHelp.saveError(TAG, msg, ""); } @Override public void run() { mListener.onPre(); if (mTaskEntity.getEntity().getFileSize() <= 1 - || mTaskEntity.refreshInfo - || mTaskEntity.requestType == AbsTaskEntity.D_FTP) { + || mTaskEntity.isRefreshInfo() + || mTaskEntity.getRequestType() == AbsTaskEntity.D_FTP) { new Thread(createInfoThread()).start(); } else { mDownloader.start(); @@ -103,10 +104,10 @@ public class SimpleDownloadUtil implements IUtil, Runnable { * 通过链接类型创建不同的获取文件信息的线程 */ private Runnable createInfoThread() { - switch (mTaskEntity.requestType) { + switch (mTaskEntity.getRequestType()) { case AbsTaskEntity.D_FTP: return new FtpFileInfoThread(mTaskEntity, new OnFileInfoCallback() { - @Override public void onComplete(String url, int code) { + @Override public void onComplete(String url, CompleteInfo info) { mDownloader.start(); } @@ -116,7 +117,7 @@ public class SimpleDownloadUtil implements IUtil, Runnable { }); case AbsTaskEntity.D_HTTP: return new HttpFileInfoThread(mTaskEntity, new OnFileInfoCallback() { - @Override public void onComplete(String url, int code) { + @Override public void onComplete(String url, CompleteInfo info) { mDownloader.start(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DGEWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DGEWrapper.java new file mode 100644 index 00000000..0c50181d --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DGEWrapper.java @@ -0,0 +1,44 @@ +/* + * 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.wrapper; + +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.orm.AbsWrapper; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import java.util.List; + +/** + * Created by laoyuyu on 2018/3/30. + * 任务组实体和子任务实体的关系 + */ +@Wrapper +public class DGEWrapper extends AbsWrapper { + + @One + public DownloadGroupEntity groupEntity; + + @Many(parentColumn = "groupName", entityColumn = "groupName") + public List subEntity; + + @Override protected void handleConvert() { + if (subEntity != null && !subEntity.isEmpty()) { + groupEntity.setSubEntities(subEntity); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DGSTEWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DGSTEWrapper.java new file mode 100644 index 00000000..447c33f0 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DGSTEWrapper.java @@ -0,0 +1,44 @@ +/* + * 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.wrapper; + +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.orm.AbsWrapper; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import java.util.List; + +/** + * Created by laoyuyu on 2018/4/11. + * 任务组任务实体和任务组任务实体的子任务实体对应关系 + */ +@Wrapper +public class DGSTEWrapper extends AbsWrapper { + + @One + public DownloadGroupTaskEntity dgTaskEntity; + + @Many(parentColumn = "key", entityColumn = "groupName") + public List subTaskEntity; + + @Override protected void handleConvert() { + if (subTaskEntity != null && !subTaskEntity.isEmpty()) { + dgTaskEntity.setSubTaskEntities(subTaskEntity); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DGTEWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DGTEWrapper.java new file mode 100644 index 00000000..680585ac --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DGTEWrapper.java @@ -0,0 +1,65 @@ +/* + * 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.wrapper; + +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.inf.AbsTaskEntity; +import com.arialyy.aria.orm.AbsWrapper; +import com.arialyy.aria.orm.DbEntity; +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.CommonUtil; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by laoyuyu on 2018/3/30. + * 任务组实体和任务组任务实体的关系 + */ +@Wrapper +public class DGTEWrapper extends AbsWrapper { + + @One + public DownloadGroupEntity entity; + + @Many(parentColumn = "groupName", entityColumn = "key") + private List taskEntitys; + + public DownloadGroupTaskEntity taskEntity; + + @Override protected void handleConvert() { + taskEntity = (taskEntitys == null || taskEntitys.isEmpty()) ? null : taskEntitys.get(0); + if (taskEntity != null) { + taskEntity.setEntity(entity); + List subWrappers = + DbEntity.findRelationData(DTEWrapper.class, "DownloadTaskEntity.groupName=?", + taskEntity.getKey()); + if (subWrappers != null && !subWrappers.isEmpty()) { + List temp = new ArrayList<>(); + for (DTEWrapper dw : subWrappers) { + if (dw.taskEntity.getRequestType() == AbsTaskEntity.D_FTP) { + dw.taskEntity.setUrlEntity(CommonUtil.getFtpUrlInfo(dw.taskEntity.getUrl())); + } + temp.add(dw.taskEntity); + } + taskEntity.setSubTaskEntities(temp); + } + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DTEWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DTEWrapper.java new file mode 100644 index 00000000..d2062f98 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/wrapper/DTEWrapper.java @@ -0,0 +1,46 @@ +/* + * 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.wrapper; + +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.orm.AbsWrapper; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import java.util.List; + +/** + * Created by laoyuyu on 2018/3/30. + */ +@Wrapper +public class DTEWrapper extends AbsWrapper { + + @One + public DownloadEntity entity; + + @Many(parentColumn = "downloadPath", entityColumn = "key") + private List taskEntitys = null; + + public DownloadTaskEntity taskEntity; + + @Override public void handleConvert() { + taskEntity = (taskEntitys == null || taskEntitys.isEmpty()) ? null : taskEntitys.get(0); + if (taskEntity != null) { + taskEntity.setEntity(entity); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsDownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsDownloadTarget.java deleted file mode 100644 index f5909a1f..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsDownloadTarget.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.inf; - -import android.text.TextUtils; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.command.normal.NormalCmdFactory; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; - -/** - * Created by lyy on 2017/2/28. - */ -public abstract class AbsDownloadTarget - extends AbsTarget { - - /** - * 如果你的下载链接的header中含有md5码信息,那么你可以通过设置key,来获取从header获取该md5码信息。 - * key默认值为:Content-MD5 - * 获取md5信息:{@link DownloadEntity#getMd5Code()} - */ - public TARGET setHeaderMd5Key(String md5Key) { - if (TextUtils.isEmpty(md5Key)) return (TARGET) this; - mTaskEntity.md5Key = md5Key; - if (TextUtils.isEmpty(mTaskEntity.md5Key) || !mTaskEntity.md5Key.equals(md5Key)) { - mTaskEntity.update(); - } - return (TARGET) this; - } - - /** - * 如果你的文件长度是放在header中,那么你需要配置key来让Aria知道正确的文件长度 - * key默认值为:Content-Length - */ - public TARGET setHeaderContentLengthKey(String contentLength) { - if (TextUtils.isEmpty(contentLength)) return (TARGET) this; - mTaskEntity.contentLength = contentLength; - if (TextUtils.isEmpty(mTaskEntity.contentLength) || !mTaskEntity.contentLength.equals( - contentLength)) { - mTaskEntity.update(); - } - return (TARGET) this; - } - - /** - * 如果你的下载链接的header中含有文件描述信息,那么你可以通过设置key,来获取从header获取该文件描述信息。 - * key默认值为:Content-Disposition - * 获取文件描述信息:{@link DownloadEntity#getDisposition()} - */ - public TARGET setHeaderDispositionKey(String dispositionKey) { - if (TextUtils.isEmpty(dispositionKey)) return (TARGET) this; - mTaskEntity.dispositionKey = dispositionKey; - if (TextUtils.isEmpty(mTaskEntity.dispositionKey) || !mTaskEntity.dispositionKey.equals( - dispositionKey)) { - mTaskEntity.save(); - } - return (TARGET) this; - } - - /** - * 从文件描述信息{@link #setHeaderDispositionKey(String)}中含有文件名信息,你可以通过设置key来获取header中的文件名 - * key默认值为:attachment;filename - * 获取文件名信息:{@link DownloadEntity#getServerFileName()} - */ - public TARGET setHeaderDispositionFileKey(String dispositionFileKey) { - if (TextUtils.isEmpty(dispositionFileKey)) return (TARGET) this; - mTaskEntity.dispositionFileKey = dispositionFileKey; - if (TextUtils.isEmpty(mTaskEntity.dispositionFileKey) || !mTaskEntity.dispositionFileKey.equals( - dispositionFileKey)) { - mTaskEntity.save(); - } - return (TARGET) this; - } - - /** - * 将任务设置为最高优先级任务,最高优先级任务有以下特点: - * 1、在下载队列中,有且只有一个最高优先级任务 - * 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 - * 3、任务调度器不会暂停最高优先级任务 - * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 - * 5、如果下载队列中已经满了,则会停止队尾的任务,当高优先级任务完成后,该队尾任务将自动执行 - * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 - */ - protected void setHighestPriority() { - AriaManager.getInstance(AriaManager.APP) - .setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, - NormalCmdFactory.TASK_HIGHEST_PRIORITY, checkTaskType())) - .exe(); - } - - /** - * 重定向后,新url的key,默认为location - */ - public void setRedirectUrlKey(String redirectUrlKey) { - if (TextUtils.isEmpty(redirectUrlKey)) { - ALog.e("AbsDownloadTarget", "重定向后,新url的key不能为null"); - return; - } - mTaskEntity.redirectUrlKey = redirectUrlKey; - } - - /** - * 获取任务文件大小 - * - * @return 文件大小 - */ - public long getFileSize() { - return getSize(); - } - - /** - * 获取单位转换后的文件大小 - * - * @return 文件大小{@code xxx mb} - */ - public String getConvertFileSize() { - return getConvertSize(); - } - - /** - * 添加任务 - */ - public void add() { - AriaManager.getInstance(AriaManager.APP) - .setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_CREATE, - checkTaskType())) - .exe(); - } - - /** - * 重新下载 - */ - public void reStart() { - cancel(); - start(); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java index cfb731bc..c4a10c5c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java @@ -18,7 +18,7 @@ package com.arialyy.aria.core.inf; import android.os.Parcel; import android.os.Parcelable; import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.orm.Ignore; +import com.arialyy.aria.orm.annotation.Ignore; /** * Created by AriaL on 2017/6/29. @@ -31,7 +31,7 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable /** * 单位转换后的速度 */ - @Ignore private String convertSpeed = ""; + @Ignore private String convertSpeed; /** * 下载失败计数,每次开始都重置为0 */ @@ -40,7 +40,7 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable /** * 扩展字段 */ - private String str = ""; + private String str; /** * 文件大小 */ @@ -48,7 +48,7 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable /** * 转换后的文件大小 */ - private String convertFileSize = ""; + private String convertFileSize; private int state = STATE_WAIT; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java index 132b78f7..ad7f6abc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java @@ -17,10 +17,7 @@ package com.arialyy.aria.core.inf; import android.os.Parcel; import android.os.Parcelable; -import com.arialyy.aria.core.download.DownloadGroupTaskEntity; -import com.arialyy.aria.orm.Foreign; -import com.arialyy.aria.orm.NormalList; -import com.arialyy.aria.orm.Primary; +import com.arialyy.aria.orm.annotation.Primary; import java.util.ArrayList; import java.util.List; @@ -29,20 +26,16 @@ import java.util.List; */ public abstract class AbsGroupEntity extends AbsEntity implements Parcelable { /** - * 组名,组名为任务地址相加的urlMd5 + * 组名,组名为任务地址相加的url的Md5 */ - @Primary @Foreign(table = DownloadGroupTaskEntity.class, column = "key") protected String - groupName = ""; + @Primary protected String groupName; /** * 任务组别名 */ - private String alias = ""; + private String alias; - /** - * 子任务链接组 - */ - @NormalList(clazz = String.class) private List urls = new ArrayList<>(); + private List urls = new ArrayList<>(); public List getUrls() { return urls; diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java index 5f03f29a..86e6ed12 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java @@ -17,8 +17,6 @@ package com.arialyy.aria.core.inf; import android.os.Parcel; import android.os.Parcelable; -import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.orm.Ignore; /** * Created by AriaL on 2017/6/3. @@ -28,12 +26,12 @@ public abstract class AbsNormalEntity extends AbsEntity implements Parcelable { /** * 服务器地址 */ - private String url = ""; + private String url; /** * 文件名 */ - private String fileName = ""; + private String fileName; /** * 是否是任务组里面的下载实体 @@ -41,7 +39,7 @@ public abstract class AbsNormalEntity extends AbsEntity implements Parcelable { private boolean isGroupChild = false; private boolean isRedirect = false; //是否重定向 - private String redirectUrl = ""; //重定向链接 + private String redirectUrl; //重定向链接 public String getUrl() { return url; diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java index 6e2d69c4..1a27a943 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java @@ -15,13 +15,11 @@ */ package com.arialyy.aria.core.inf; -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.CancelCmd; import com.arialyy.aria.core.command.normal.NormalCmdFactory; -import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.manager.TEManager; @@ -30,25 +28,29 @@ import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.Set; /** * Created by AriaL on 2017/7/3. */ public abstract class AbsTarget implements ITarget { + protected String TAG = ""; protected ENTITY mEntity; protected TASK_ENTITY mTaskEntity; protected String mTargetName; + protected AbsTarget() { + TAG = CommonUtil.getClassName(this); + } + /** - * 重置下载状态,将任务状态设置为未开始状态 + * 重置状态,将任务状态设置为未开始状态 + * 注意:如果在后续方法调用链中没有调用 {@link #start()}、{@link #stop()}、{@link #cancel()}、{@link #resume()} + * 等操作任务的方法,那么你需要调用{@link #save()}才能将修改保存到数据库 */ public TARGET resetState() { mTaskEntity.getEntity().setState(IEntity.STATE_WAIT); - mTaskEntity.refreshInfo = true; - mTaskEntity.update(); + mTaskEntity.setRefreshInfo(true); return (TARGET) this; } @@ -60,32 +62,17 @@ public abstract class AbsTarget headers) { - return addHeaders(headers, false); - } - - /** - * 给url请求添加头部 - * - * @param refreshHeader 更新数据库中保存的头部信息 - */ - public TARGET addHeaders(Map headers, boolean refreshHeader) { - if (headers != null && headers.size() > 0) { - Set keys = headers.keySet(); - for (String key : keys) { - mTaskEntity.headers.put(key, headers.get(key)); - } - } - if (refreshHeader) { - mTaskEntity.update(); + public void save() { + if (!checkEntity()) { + ALog.e(TAG, "保存修改失败"); } - return (TARGET) this; - } - - /** - * 设置请求类型,POST或GET,默认为在GET - * 只试用于HTTP请求 - * - * @param requestEnum {@link RequestEnum} - */ - public TARGET setRequestMode(RequestEnum requestEnum) { - mTaskEntity.requestEnum = requestEnum; - return (TARGET) this; } /** * 开始任务 */ @Override public void start() { - AriaManager.getInstance(AriaManager.APP) - .setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_START, - checkTaskType())) - .exe(); - } - - protected int checkTaskType() { - int taskType = 0; - if (mTaskEntity instanceof DownloadTaskEntity) { - taskType = ICmd.TASK_TYPE_DOWNLOAD; - } else if (mTaskEntity instanceof DownloadGroupTaskEntity) { - taskType = ICmd.TASK_TYPE_DOWNLOAD_GROUP; - } else if (mTaskEntity instanceof UploadTaskEntity) { - taskType = ICmd.TASK_TYPE_UPLOAD; + if (checkEntity()) { + AriaManager.getInstance(AriaManager.APP) + .setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_START, + checkTaskType())) + .exe(); } - return taskType; } /** @@ -244,47 +193,58 @@ public abstract class AbsTarget cmds = new ArrayList<>(); - int taskType = checkTaskType(); - cmds.add( - CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_STOP, taskType)); - cmds.add(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_START, - taskType)); - AriaManager.getInstance(AriaManager.APP).setCmds(cmds).exe(); + if (checkEntity()) { + List cmds = new ArrayList<>(); + int taskType = checkTaskType(); + cmds.add( + CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_STOP, + taskType)); + cmds.add(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_START, + taskType)); + AriaManager.getInstance(AriaManager.APP).setCmds(cmds).exe(); + } } /** @@ -294,35 +254,21 @@ public abstract class AbsTarget 0) { - tempUrl = url.substring(0, end); - int tempEnd = tempUrl.lastIndexOf("/"); - if (tempEnd > 0) { - fileName = tempUrl.substring(tempEnd + 1, tempUrl.length()); - } - } else { - int tempEnd = url.lastIndexOf("/"); - if (tempEnd > 0) { - fileName = url.substring(tempEnd + 1, url.length()); - } - } - if (TextUtils.isEmpty(fileName)) { - fileName = CommonUtil.keyToHashKey(url); + public void reStart() { + if (checkEntity()) { + cancel(); + start(); } - return fileName; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskEntity.java index b63fad34..9407a302 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskEntity.java @@ -18,8 +18,7 @@ package com.arialyy.aria.core.inf; import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.orm.Ignore; -import com.arialyy.aria.orm.Primary; +import com.arialyy.aria.orm.annotation.Ignore; import java.util.HashMap; import java.util.Map; @@ -55,103 +54,75 @@ public abstract class AbsTaskEntity extends DbEntity { */ public static final int U_FTP = 0xA2; - /** - * Task实体对应的key - */ - @Primary public String key = ""; - /** * 账号和密码 */ - @Ignore public FtpUrlEntity urlEntity; + @Ignore private FtpUrlEntity urlEntity; /** * 刷新信息 {@code true} 重新刷新下载信息 */ - @Ignore public boolean refreshInfo = false; + @Ignore private boolean refreshInfo = false; /** * 是否是新任务,{@code true} 新任务 */ - @Ignore public boolean isNewTask = false; + @Ignore private boolean isNewTask = false; /** * 任务状态,和Entity的state同步 */ - public int state = IEntity.STATE_WAIT; + private int state = IEntity.STATE_WAIT; /** * 请求类型 * {@link AbsTaskEntity#D_HTTP}、{@link AbsTaskEntity#D_FTP}、{@link AbsTaskEntity#D_FTP_DIR}。。。 */ - public int requestType = D_HTTP; + private int requestType = D_HTTP; /** * http 请求头 */ - public Map headers = new HashMap<>(); + private Map headers = new HashMap<>(); /** * 字符编码,默认为"utf-8" */ - public String charSet = "utf-8"; + private String charSet = "utf-8"; /** * 网络请求类型 */ - public RequestEnum requestEnum = RequestEnum.GET; + private RequestEnum requestEnum = RequestEnum.GET; /** - * 从header中含有的文件md5码信息所需要的key - */ - public String md5Key = "Content-MD5"; - - /** - * 是否使用服务器通过content-disposition传递的文件名,内容格式{@code attachment;filename=***} + * 是否使用服务器通过content-disposition传递的文件名,内容格式{@code attachment; filename="filename.jpg"} * {@code true} 使用 */ - public boolean useServerFileName = false; - - /** - * 从header中获取文件描述信息所需要的key - */ - public String dispositionKey = "Content-Disposition"; - - /** - * 重定向后,从header中获取新url所需要的key - */ - public String redirectUrlKey = "location"; - - /** - * 从Disposition获取的文件名说需要的key - */ - public String dispositionFileKey = "attachment;filename"; - - /** - * 从header中含有的文件长度信息所需要的key - */ - public String contentLength = "Content-Length"; + private boolean useServerFileName = false; /** * 重定向链接 */ - public String redirectUrl = ""; + private String redirectUrl = ""; /** + * 删除任务时,是否删除已下载完成的文件 + * 未完成的任务,不管true还是false,都会删除文件 * {@code true} 删除任务数据库记录,并且删除已经下载完成的文件 * {@code false} 如果任务已经完成,只删除任务数据库记录 */ - @Ignore public boolean removeFile = false; + @Ignore private boolean removeFile = false; /** * 是否支持断点, {@code true} 为支持断点 */ - public boolean isSupportBP = true; + private boolean isSupportBP = true; /** * 状态码 */ - public int code; + private int code; public abstract ENTITY getEntity(); @@ -164,12 +135,7 @@ public abstract class AbsTaskEntity extends DbEntity { return getEntity().getState(); } - @Override public void deleteData() { - if (getEntity() != null) { - getEntity().deleteData(); - } - super.deleteData(); - } + public abstract String getKey(); @Override public void update() { if (getEntity() != null) { @@ -177,4 +143,104 @@ public abstract class AbsTaskEntity extends DbEntity { } super.update(); } + + public FtpUrlEntity getUrlEntity() { + return urlEntity; + } + + public void setUrlEntity(FtpUrlEntity urlEntity) { + this.urlEntity = urlEntity; + } + + public boolean isRefreshInfo() { + return refreshInfo; + } + + public void setRefreshInfo(boolean refreshInfo) { + this.refreshInfo = refreshInfo; + } + + public boolean isNewTask() { + return isNewTask; + } + + public void setNewTask(boolean newTask) { + isNewTask = newTask; + } + + public void setState(int state) { + this.state = state; + } + + public int getRequestType() { + return requestType; + } + + public void setRequestType(int requestType) { + this.requestType = requestType; + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public String getCharSet() { + return charSet; + } + + public void setCharSet(String charSet) { + this.charSet = charSet; + } + + public RequestEnum getRequestEnum() { + return requestEnum; + } + + public void setRequestEnum(RequestEnum requestEnum) { + this.requestEnum = requestEnum; + } + + public boolean isUseServerFileName() { + return useServerFileName; + } + + public void setUseServerFileName(boolean useServerFileName) { + this.useServerFileName = useServerFileName; + } + + public String getRedirectUrl() { + return redirectUrl; + } + + public void setRedirectUrl(String redirectUrl) { + this.redirectUrl = redirectUrl; + } + + public boolean isRemoveFile() { + return removeFile; + } + + public void setRemoveFile(boolean removeFile) { + this.removeFile = removeFile; + } + + public boolean isSupportBP() { + return isSupportBP; + } + + public void setSupportBP(boolean supportBP) { + isSupportBP = supportBP; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsUploadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsUploadTarget.java deleted file mode 100644 index be6541f1..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsUploadTarget.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.inf; - -import android.support.annotation.NonNull; -import com.arialyy.aria.core.queue.UploadTaskQueue; -import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.upload.UploadTask; -import com.arialyy.aria.core.upload.UploadTaskEntity; -import com.arialyy.aria.util.CheckUtil; - -/** - * Created by AriaL on 2017/6/29. - * 任务组超类 - */ -public abstract class AbsUploadTarget - extends AbsTarget { - - /** - * 设置上传路径 - * - * @param uploadUrl 上传路径 - */ - public TARGET setUploadUrl(@NonNull String uploadUrl) { - uploadUrl = CheckUtil.checkUrl(uploadUrl); - if (mEntity.getUrl().equals(uploadUrl)) return (TARGET) this; - mEntity.setUrl(uploadUrl); - mEntity.update(); - return (TARGET) this; - } - - /** - * 下载任务是否存在 - */ - @Override public boolean taskExists() { - return UploadTaskQueue.getInstance().getTask(mEntity.getFilePath()) != null; - } - - /** - * 是否在下载 - * - * @deprecated {@link #isRunning()} - */ - public boolean isUploading() { - return isRunning(); - } - - @Override public boolean isRunning() { - UploadTask task = UploadTaskQueue.getInstance().getTask(mEntity.getKey()); - return task != null && task.isRunning(); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java index 156a01ce..5fdfe079 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.inf; -import com.arialyy.aria.orm.Ignore; +import com.arialyy.aria.orm.annotation.Ignore; /** * Created by lyy on 2017/2/23. @@ -42,7 +42,7 @@ public interface IEntity { */ @Ignore int STATE_WAIT = 3; /** - * 下载中 + * 正在执行 */ @Ignore int STATE_RUNNING = 4; /** @@ -54,7 +54,7 @@ public interface IEntity { */ @Ignore int STATE_POST_PRE = 6; /** - * 取消下载 + * 删除任务 */ @Ignore int STATE_CANCEL = 7; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IFtpTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IFtpTarget.java new file mode 100644 index 00000000..519b4393 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IFtpTarget.java @@ -0,0 +1,43 @@ +/* + * 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.inf; + +/** + * Created by laoyuyu on 2018/3/9. + */ +public interface IFtpTarget { + /** + * 设置字符编码 + */ + TARGET charSet(String charSet); + + /** + * ftp 用户登录信。 + * + * @param userName ftp用户名 + * @param password ftp用户密码 + */ + TARGET login(String userName, String password); + + /** + * ftp 用户登录信息 + * + * @param userName ftp用户名 + * @param password ftp用户密码 + * @param account ftp账号 + */ + TARGET login(String userName, String password, String account); +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IHttpHeaderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IHttpHeaderTarget.java new file mode 100644 index 00000000..72b5aa47 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IHttpHeaderTarget.java @@ -0,0 +1,52 @@ +/* + * 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.inf; + +import android.support.annotation.NonNull; +import com.arialyy.aria.core.common.RequestEnum; +import com.arialyy.aria.core.download.DownloadEntity; +import java.util.Map; + +/** + * Created by laoyuyu on 2018/3/9. + * HTTP Header功能接口 + */ +public interface IHttpHeaderTarget { + + /** + * 给url请求添加Header数据 + * 如果新的header数据和数据保存的不一致,则更新数据库中对应的header数据 + * + * @param key header对应的key + * @param value header对应的value + */ + TARGET addHeader(@NonNull String key, @NonNull String value); + + /** + * 给url请求添加一组header数据 + * 如果新的header数据和数据保存的不一致,则更新数据库中对应的header数据 + * + * @param headers 一组http header数据 + */ + TARGET addHeaders(Map headers); + + /** + * 设置HTTP请求类型 + * + * @param requestEnum {@link RequestEnum} + */ + TARGET setRequestMode(RequestEnum requestEnum); +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ITarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/ITarget.java index 527e8078..8b4b998a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/ITarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/ITarget.java @@ -15,53 +15,50 @@ */ package com.arialyy.aria.core.inf; -import android.support.annotation.NonNull; -import com.arialyy.aria.core.common.RequestEnum; -import java.util.Map; - /** * Created by AriaL on 2017/6/29. */ public interface ITarget { /** - * 任务文件大小 + * 获取任务状态 + * + * @return {@link IEntity} */ - long getSize(); + int getTaskState(); /** - * 转换后的大小 + * 任务是否在执行 + * + * @return {@code true} 任务正在执行 */ - String getConvertSize(); + boolean isRunning(); /** - * 获取任务进度百分比 + * 任务是否存在 + * + * @return {@code true} 任务存在 */ - int getPercent(); + boolean taskExists(); /** - * 获取任务进度,如果任务存在,则返回当前进度 + * 任务文件大小 */ - long getCurrentProgress(); + long getSize(); /** - * 给url请求添加头部 - * - * @param key 头部key - * @param header 头部value + * 转换后的大小 */ - TARGET addHeader(@NonNull String key, @NonNull String header) ; + String getConvertSize(); /** - * 给url请求添加头部 + * 获取任务进度百分比 */ - TARGET addHeaders(Map headers); + int getPercent(); /** - * 设置请求类型 - * - * @param requestEnum {@link RequestEnum} + * 获取任务进度,如果任务存在,则返回当前进度 */ - TARGET setRequestMode(RequestEnum requestEnum); + long getCurrentProgress(); /** * 开始下载 @@ -82,5 +79,4 @@ public interface ITarget { * 取消下载 */ void cancel(); - } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java index 3387c49d..e6196375 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java @@ -15,8 +15,6 @@ */ package com.arialyy.aria.core.inf; -import com.arialyy.aria.core.inf.IEventListener; - /** * Created by lyy on 2017/2/9. * 上传监听 diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEFactory.java new file mode 100644 index 00000000..61104545 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEFactory.java @@ -0,0 +1,169 @@ +/* + * 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.manager; + +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.download.wrapper.DGEWrapper; +import com.arialyy.aria.core.download.wrapper.DGTEWrapper; +import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.util.CommonUtil; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Aria.Lao on 2017/11/1. + * 任务实体工厂 + */ +class DGTEFactory implements IGTEFactory { + private static final String TAG = "DTEFactory"; + private static volatile DGTEFactory INSTANCE = null; + + private DGTEFactory() { + } + + public static DGTEFactory getInstance() { + if (INSTANCE == null) { + synchronized (DGTEFactory.class) { + INSTANCE = new DGTEFactory(); + } + } + return INSTANCE; + } + + @Override public DownloadGroupTaskEntity getGTE(String groupName, List urls) { + DownloadGroupEntity entity = createDGroupEntity(groupName, urls); + List wrapper = + DbEntity.findRelationData(DGTEWrapper.class, "DownloadGroupTaskEntity.key=?", + entity.getGroupName()); + DownloadGroupTaskEntity gte; + + if (wrapper != null && !wrapper.isEmpty()) { + gte = wrapper.get(0).taskEntity; + if (gte == null) { + // 创建新的任务组任务实体 + gte = new DownloadGroupTaskEntity(); + //创建子任务的任务实体 + gte.setSubTaskEntities(createDGSubTaskEntity(entity)); + } else if (gte.getSubTaskEntities() == null || gte.getSubTaskEntities().isEmpty()) { + gte.setSubTaskEntities(createDGSubTaskEntity(entity)); + } + } else { + gte = new DownloadGroupTaskEntity(); + gte.setSubTaskEntities(createDGSubTaskEntity(entity)); + } + gte.setKey(entity.getGroupName()); + gte.setEntity(entity); + + return gte; + } + + @Override public DownloadGroupTaskEntity getFTE(String ftpUrl) { + List wrapper = + DbEntity.findRelationData(DGTEWrapper.class, "DownloadGroupTaskEntity.key=?", + ftpUrl); + DownloadGroupTaskEntity fte; + + if (wrapper != null && !wrapper.isEmpty()) { + fte = wrapper.get(0).taskEntity; + if (fte == null) { + fte = new DownloadGroupTaskEntity(); + DownloadGroupEntity dge = new DownloadGroupEntity(); + dge.setGroupName(ftpUrl); + fte.setEntity(dge); + } else if (fte.getEntity() == null) { + DownloadGroupEntity dge = new DownloadGroupEntity(); + dge.setGroupName(ftpUrl); + fte.setEntity(dge); + } + } else { + fte = new DownloadGroupTaskEntity(); + DownloadGroupEntity dge = new DownloadGroupEntity(); + dge.setGroupName(ftpUrl); + fte.setEntity(dge); + } + fte.setKey(ftpUrl); + fte.setUrlEntity(CommonUtil.getFtpUrlInfo(ftpUrl)); + + if (fte.getEntity().getSubEntities() == null) { + fte.getEntity().setSubEntities(new ArrayList()); + } + if (fte.getSubTaskEntities() == null) { + fte.setSubTaskEntities(new ArrayList()); + } + return fte; + } + + /** + * 创建任务组子任务的任务实体 + */ + private List createDGSubTaskEntity(DownloadGroupEntity dge) { + List list = new ArrayList<>(); + for (DownloadEntity entity : dge.getSubEntities()) { + DownloadTaskEntity taskEntity = new DownloadTaskEntity(); + taskEntity.setEntity(entity); + taskEntity.setKey(entity.getDownloadPath()); + taskEntity.setGroupName(dge.getKey()); + taskEntity.setGroupTask(true); + taskEntity.setUrl(entity.getUrl()); + list.add(taskEntity); + } + return list; + } + + /** + * 查询任务组实体,如果数据库不存在该实体,则新创建一个新的任务组实体 + */ + private DownloadGroupEntity createDGroupEntity(String groupName, List urls) { + List wrapper = + DbEntity.findRelationData(DGEWrapper.class, "DownloadGroupEntity.groupName=?", + groupName); + + DownloadGroupEntity groupEntity; + if (wrapper != null && !wrapper.isEmpty()) { + groupEntity = wrapper.get(0).groupEntity; + if (groupEntity == null) { + groupEntity = new DownloadGroupEntity(); + groupEntity.setSubEntities(createSubTask(groupName, urls)); + } + } else { + groupEntity = new DownloadGroupEntity(); + groupEntity.setSubEntities(createSubTask(groupName, urls)); + } + groupEntity.setGroupName(groupName); + groupEntity.setUrls(urls); + return groupEntity; + } + + /** + * 创建子任务 + */ + private List createSubTask(String groupName, List urls) { + List list = new ArrayList<>(); + for (int i = 0, len = urls.size(); i < len; i++) { + DownloadEntity entity = new DownloadEntity(); + entity.setUrl(urls.get(i)); + entity.setDownloadPath(groupName + "_" + i); + entity.setFileName(groupName + "_" + i); + entity.setGroupName(groupName); + entity.setGroupChild(true); + list.add(entity); + } + return list; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java deleted file mode 100644 index f68d7bad..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.manager; - -import android.text.TextUtils; -import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.download.DownloadGroupTaskEntity; -import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.util.CommonUtil; -import java.util.List; - -/** - * Created by Aria.Lao on 2017/11/1. - * 任务实体工厂 - */ -class DGTEntityFactory implements ITEntityFactory, - IGTEntityFactory { - private static final String TAG = "DTEntityFactory"; - private static volatile DGTEntityFactory INSTANCE = null; - - private DGTEntityFactory() { - } - - public static DGTEntityFactory getInstance() { - if (INSTANCE == null) { - synchronized (DGTEntityFactory.class) { - INSTANCE = new DGTEntityFactory(); - } - } - return INSTANCE; - } - - /** - * 通过下载实体创建任务实体 - */ - @Override public DownloadGroupTaskEntity create(DownloadGroupEntity entity) { - DownloadGroupTaskEntity dgTaskEntity = - DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", entity.getGroupName()); - if (dgTaskEntity == null) { - dgTaskEntity = new DownloadGroupTaskEntity(); - dgTaskEntity.save(entity); - } - if (dgTaskEntity.entity == null || TextUtils.isEmpty(dgTaskEntity.entity.getKey())) { - dgTaskEntity.save(entity); - } - return dgTaskEntity; - } - - /** - * 对于任务组,不能使用这个,可用于FTP文件夹下载 - * - * @deprecated {@link #create(String, List)} - */ - @Override @Deprecated public DownloadGroupTaskEntity create(String key) { - DownloadGroupTaskEntity dgTaskEntity = - DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", key); - if (dgTaskEntity == null) { - dgTaskEntity = new DownloadGroupTaskEntity(); - dgTaskEntity.save(getDownloadGroupEntity(key, null)); - } - if (dgTaskEntity.entity == null || TextUtils.isEmpty(dgTaskEntity.entity.getKey())) { - dgTaskEntity.save(getDownloadGroupEntity(key, null)); - } - dgTaskEntity.urlEntity = CommonUtil.getFtpUrlInfo(key); - return dgTaskEntity; - } - - @Override public DownloadGroupTaskEntity create(String groupName, List urls) { - return create(getDownloadGroupEntity(groupName, urls)); - } - - /** - * 查询任务组实体,如果数据库不存在该实体,则新创建一个新的任务组实体 - */ - private DownloadGroupEntity getDownloadGroupEntity(String groupName, List urls) { - DownloadGroupEntity entity = - DbEntity.findFirst(DownloadGroupEntity.class, "groupName=?", groupName); - if (entity == null) { - entity = new DownloadGroupEntity(); - entity.setGroupName(groupName); - entity.setUrls(urls); - } - return entity; - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DTEFactory.java similarity index 59% rename from Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java rename to Aria/src/main/java/com/arialyy/aria/core/manager/DTEFactory.java index 437b4171..16a5c84d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DTEFactory.java @@ -18,25 +18,28 @@ package com.arialyy.aria.core.manager; import android.text.TextUtils; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.download.wrapper.DTEWrapper; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.orm.DbEntity; import java.io.File; +import java.util.List; +import java.util.UUID; /** * Created by Aria.Lao on 2017/11/1. * 任务实体工厂 */ -class DTEntityFactory implements ITEntityFactory { - private static final String TAG = "DTEntityFactory"; - private static volatile DTEntityFactory INSTANCE = null; +class DTEFactory implements INormalTEFactory { + private static final String TAG = "DTEFactory"; + private static volatile DTEFactory INSTANCE = null; - private DTEntityFactory() { + private DTEFactory() { } - public static DTEntityFactory getInstance() { + public static DTEFactory getInstance() { if (INSTANCE == null) { - synchronized (DTEntityFactory.class) { - INSTANCE = new DTEntityFactory(); + synchronized (DTEFactory.class) { + INSTANCE = new DTEFactory(); } } return INSTANCE; @@ -45,18 +48,24 @@ class DTEntityFactory implements ITEntityFactory wrapper = DbEntity.findRelationData(DTEWrapper.class, + "DownloadTaskEntity.key=? and DownloadTaskEntity.isGroupTask='false' and DownloadTaskEntity.url=?", + entity.getDownloadPath(), entity.getUrl()); + DownloadTaskEntity taskEntity; + if (wrapper != null && !wrapper.isEmpty()) { + taskEntity = wrapper.get(0).taskEntity; + if (taskEntity == null) { + taskEntity = new DownloadTaskEntity(); + } else if (taskEntity.getEntity() == null || TextUtils.isEmpty(taskEntity.getEntity().getUrl())) { + taskEntity.setEntity(entity); + } + } else { taskEntity = new DownloadTaskEntity(); - taskEntity.save(entity); - } else if (taskEntity.entity == null || TextUtils.isEmpty(taskEntity.entity.getUrl())) { - taskEntity.save(entity); - } else if (!taskEntity.entity.getUrl().equals(entity.getUrl())) { //处理地址切换而保存路径不变 - taskEntity.save(entity); } + taskEntity.setKey(entity.getDownloadPath()); + taskEntity.setUrl(entity.getUrl()); + taskEntity.setEntity(entity); return taskEntity; } @@ -80,6 +89,8 @@ class DTEntityFactory implements ITEntityFactory> { + + /** + * 获取任务组的任务实体, + * 1、创建实体和任务实体之间的关联 + * 2、如果在数据库中查找不到对应的数据,则新创建任务实体 + * + * @param groupName 任务组名 + * @param urls 子任务的下载地址 + */ + TASK_ENTITY getGTE(String groupName, List urls); + + /** + * 获取FTP文件夹的任务实体,该方法需要以下操作: + * 1、创建实体和任务实体之间的关联 + * 2、如果在数据库中查找不到对应的数据,则新创建任务实体 + * + * @param ftpUrl ftp文件夹下载路径 + */ + TASK_ENTITY getFTE(String ftpUrl); +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/INormalTEFactory.java similarity index 82% rename from Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java rename to Aria/src/main/java/com/arialyy/aria/core/manager/INormalTEFactory.java index d92f7f2c..b55ddcf7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/INormalTEFactory.java @@ -21,11 +21,7 @@ import com.arialyy.aria.core.inf.AbsTaskEntity; /** * Created by Aria.Lao on 2017/11/1. */ -interface ITEntityFactory> { - /** - * 通过信息实体创建任务实体 - */ - TASK_ENTITY create(ENTITY entity); +interface INormalTEFactory> { /** * 通过key创建任务,只适应于单任务 diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java b/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java index 56018a88..1e8ccad1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java @@ -18,7 +18,6 @@ package com.arialyy.aria.core.manager; import android.support.v4.util.LruCache; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadTaskEntity; -import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.util.ALog; @@ -29,7 +28,7 @@ import java.util.concurrent.locks.ReentrantLock; /** * Created by Aria.Lao on 2017/11/1. - * 任务实体管理器,负责 + * 任务实体管理器 */ public class TEManager { private static final String TAG = "TaskManager"; @@ -51,18 +50,17 @@ public class TEManager { } /** - * 通过key创建任务,只适应于单任务,不能用于HTTP任务组,可用于Ftp文件夹 - * 如果是任务组,请使用{@link #createGTEntity(Class, List)} + * 通过key创建任务,只适应于单任务 * * @return 如果任务实体创建失败,返回null */ - public TE createTEntity(Class clazz, String key) { + private TE createNormalTE(Class clazz, String key) { final Lock lock = this.lock; lock.lock(); try { AbsTaskEntity tEntity = cache.get(convertKey(key)); if (tEntity == null || tEntity.getClass() != clazz) { - ITEntityFactory factory = chooseFactory(clazz); + INormalTEFactory factory = chooseNormalFactory(clazz); if (factory == null) { ALog.e(TAG, "任务实体创建失败"); return null; @@ -77,23 +75,44 @@ public class TEManager { } /** - * 创建任务组实体 + * 通过key创建不需要缓存的任务实体,只适应于单任务 * * @return 如果任务实体创建失败,返回null */ - public TE createGTEntity(Class clazz, List urls) { + public TE createNormalNoCacheTE(Class clazz, String key) { + final Lock lock = this.lock; + lock.lock(); + try { + INormalTEFactory factory = chooseNormalFactory(clazz); + if (factory == null) { + ALog.e(TAG, "任务实体创建失败"); + return null; + } + AbsTaskEntity tEntity = factory.create(key); + return (TE) tEntity; + } finally { + lock.unlock(); + } + } + + /** + * 创建任务组 + * + * @return 如果任务实体创建失败,返回null + */ + private TE createGTEntity(Class clazz, List urls) { final Lock lock = this.lock; lock.lock(); try { String groupName = CommonUtil.getMd5Code(urls); AbsTaskEntity tEntity = cache.get(convertKey(groupName)); if (tEntity == null || tEntity.getClass() != clazz) { - IGTEntityFactory factory = chooseGroupFactory(clazz); + IGTEFactory factory = chooseGroupFactory(clazz); if (factory == null) { ALog.e(TAG, "任务实体创建失败"); return null; } - tEntity = factory.create(groupName, urls); + tEntity = factory.getGTE(groupName, urls); cache.put(convertKey(groupName), tEntity); } return (TE) tEntity; @@ -103,23 +122,23 @@ public class TEManager { } /** - * 通过实体创建任务 + * 通过ftp文件夹路径,创建FTP文件夹实体 * * @return 如果任务实体创建失败,返回null */ - public TE createTEntity(Class clazz, AbsEntity absEntity) { + private TE createFDTE(Class clazz, String key) { final Lock lock = this.lock; lock.lock(); try { - AbsTaskEntity tEntity = cache.get(convertKey(absEntity.getKey())); + AbsTaskEntity tEntity = cache.get(convertKey(key)); if (tEntity == null || tEntity.getClass() != clazz) { - ITEntityFactory factory = chooseFactory(clazz); + IGTEFactory factory = chooseGroupFactory(clazz); if (factory == null) { ALog.e(TAG, "任务实体创建失败"); return null; } - tEntity = factory.create(absEntity); - cache.put(convertKey(absEntity.getKey()), tEntity); + tEntity = factory.getFTE(key); + cache.put(convertKey(key), tEntity); } return (TE) tEntity; } finally { @@ -127,26 +146,26 @@ public class TEManager { } } - private IGTEntityFactory chooseGroupFactory(Class clazz) { + private IGTEFactory chooseGroupFactory(Class clazz) { if (clazz == DownloadGroupTaskEntity.class) { - return DGTEntityFactory.getInstance(); + return DGTEFactory.getInstance(); } return null; } - private ITEntityFactory chooseFactory(Class clazz) { + private INormalTEFactory chooseNormalFactory(Class clazz) { if (clazz == DownloadTaskEntity.class) { - return DTEntityFactory.getInstance(); + return DTEFactory.getInstance(); } else if (clazz == UploadTaskEntity.class) { - return UTEntityFactory.getInstance(); - } else if (clazz == DownloadGroupTaskEntity.class) { - return DGTEntityFactory.getInstance(); + return UTEFactory.getInstance(); } return null; } /** - * 从任务实体管理器中获取任务实体 + * 从缓存中获取单任务实体,如果任务实体不存在,则创建任务实体 + * + * @return 创建失败,返回null */ public TE getTEntity(Class clazz, String key) { final Lock lock = this.lock; @@ -154,7 +173,53 @@ public class TEManager { try { AbsTaskEntity tEntity = cache.get(convertKey(key)); if (tEntity == null) { - return null; + return createNormalTE(clazz, key); + } else { + return (TE) tEntity; + } + } finally { + lock.unlock(); + } + } + + /** + * 从缓存中获取FTP文件夹任务实体,如果任务实体不存在,则创建任务实体 + * + * @return 创建失败,返回null + */ + public TE getFDTEntity(Class clazz, String key) { + final Lock lock = this.lock; + lock.lock(); + try { + AbsTaskEntity tEntity = cache.get(convertKey(key)); + if (tEntity == null) { + return createFDTE(clazz, key); + } else { + return (TE) tEntity; + } + } finally { + lock.unlock(); + } + } + + /** + * 从缓存中获取HTTP任务组的任务实体,如果任务实体不存在,则创建任务实体 + * + * @param urls HTTP任务组的子任务下载地址列表 + * @return 地址列表为null或创建实体失败,返回null + */ + public TE getGTEntity(Class clazz, List urls) { + if (urls == null || urls.isEmpty()) { + ALog.e(TAG, "获取HTTP任务组实体失败:任务组的子任务下载地址列表为null"); + return null; + } + final Lock lock = this.lock; + lock.lock(); + try { + String groupName = CommonUtil.getMd5Code(urls); + AbsTaskEntity tEntity = cache.get(convertKey(groupName)); + if (tEntity == null) { + return createGTEntity(clazz, urls); } else { return (TE) tEntity; } @@ -189,7 +254,7 @@ public class TEManager { final Lock lock = this.lock; lock.lock(); try { - return cache.put(convertKey(te.key), te) != null; + return cache.put(convertKey(te.getKey()), te) != null; } finally { lock.unlock(); } @@ -210,6 +275,7 @@ public class TEManager { } private String convertKey(String key) { + key = key.trim(); final Lock lock = this.lock; lock.lock(); try { diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/TaskManager.java b/Aria/src/main/java/com/arialyy/aria/core/manager/TaskManager.java deleted file mode 100644 index e0f17a0b..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/TaskManager.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.manager; - -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Created by Aria.Lao on 2017/9/1. - * 任务管理器 - */ -class TaskManager { - private static final String TAG = "TaskManager"; - private static volatile TaskManager INSTANCE = null; - private Map map = new ConcurrentHashMap<>(); - - public static TaskManager getInstance() { - if (INSTANCE == null) { - synchronized (TaskManager.class) { - INSTANCE = new TaskManager(); - } - } - return INSTANCE; - } - - private TaskManager() { - - } - - /** - * 管理器添加任务 - * - * @param key 任务的key,下载为保存路径,任务组为任务组名,上传为文件上传路径 - * @param task 任务 - * @return {@code true}添加成功 - */ - public boolean addTask(String key, Class clazz, T task) { - String hash = CommonUtil.keyToHashKey(key); - if (map.keySet().contains(hash)) { - ALog.e(TAG, "任务【" + key + "】已存在"); - return false; - } - map.put(CommonUtil.keyToHashKey(key), task); - return true; - } - - /** - * 移除任务 - * - * @param key 任务的key,下载为保存路径,任务组为任务组名,上传为文件上传路径 - */ - public void removeTask(String key) { - String hash = CommonUtil.keyToHashKey(key); - for (Iterator> iter = map.entrySet().iterator(); iter.hasNext(); ) { - Map.Entry entry = iter.next(); - if (entry.getKey().equals(hash)) iter.remove(); - } - } - - /** - * 通过key获取任务 - * - * @return 如果找不到任务,返回null,否则返回key对应的任务 - */ - public AbsTask getTask(String key) { - return map.get(CommonUtil.keyToHashKey(key)); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/UTEFactory.java similarity index 59% rename from Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java rename to Aria/src/main/java/com/arialyy/aria/core/manager/UTEFactory.java index 629daa5d..759b2dbd 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/UTEFactory.java @@ -15,43 +15,55 @@ */ package com.arialyy.aria.core.manager; +import android.text.TextUtils; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadTaskEntity; +import com.arialyy.aria.core.upload.wrapper.UTEWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.Regular; +import java.util.List; import java.util.regex.Pattern; /** * Created by Aria.Lao on 2017/11/1. * 任务实体工厂 */ -class UTEntityFactory implements ITEntityFactory { - private static final String TAG = "DTEntityFactory"; - private static volatile UTEntityFactory INSTANCE = null; +class UTEFactory implements INormalTEFactory { + private static final String TAG = "DTEFactory"; + private static volatile UTEFactory INSTANCE = null; - private UTEntityFactory() { + private UTEFactory() { } - public static UTEntityFactory getInstance() { + public static UTEFactory getInstance() { if (INSTANCE == null) { - synchronized (UTEntityFactory.class) { - INSTANCE = new UTEntityFactory(); + synchronized (UTEFactory.class) { + INSTANCE = new UTEFactory(); } } return INSTANCE; } - @Override public UploadTaskEntity create(UploadEntity entity) { - UploadTaskEntity uTaskEntity = - DbEntity.findFirst(UploadTaskEntity.class, "key=?", entity.getFilePath()); - if (uTaskEntity == null) { - uTaskEntity = new UploadTaskEntity(); - uTaskEntity.entity = entity; - } - if (uTaskEntity.entity == null) { - uTaskEntity.entity = entity; + private UploadTaskEntity create(UploadEntity entity) { + List wrapper = + DbEntity.findRelationData(UTEWrapper.class, "UploadTaskEntity.key=?", + entity.getFilePath()); + + if (wrapper != null && !wrapper.isEmpty()) { + UploadTaskEntity uTaskEntity = wrapper.get(0).taskEntity; + if (uTaskEntity == null) { + uTaskEntity = new UploadTaskEntity(); + uTaskEntity.setEntity(entity); + } else if (uTaskEntity.getEntity() == null || TextUtils.isEmpty( + uTaskEntity.getEntity().getFilePath())) { + uTaskEntity.setEntity(entity); + } + return uTaskEntity; + } else { + UploadTaskEntity uTaskEntity = new UploadTaskEntity(); + uTaskEntity.setEntity(entity); + return uTaskEntity; } - return uTaskEntity; } @Override public UploadTaskEntity create(String key) { @@ -67,14 +79,12 @@ class UTEntityFactory implements ITEntityFactory UploadEntity entity = UploadEntity.findFirst(UploadEntity.class, "filePath=?", filePath); if (entity == null) { entity = new UploadEntity(); - //String regex = "[/|\\\\|//]"; String regex = Regular.REG_FILE_NAME; Pattern p = Pattern.compile(regex); String[] strs = p.split(filePath); String fileName = strs[strs.length - 1]; entity.setFileName(fileName); entity.setFilePath(filePath); - entity.insert(); } return entity; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java index b8c02a25..7e96d1d0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java @@ -150,10 +150,10 @@ abstract class AbsTaskQueue && mExecutePool.getTask(entity.getEntity().getKey()) == null) { task = (UploadTask) TaskFactory.getInstance() .createTask(targetName, entity, UploadSchedulers.getInstance()); - entity.key = entity.getEntity().getFilePath(); + entity.setKey(entity.getEntity().getFilePath()); mCachePool.putTask(task); } else { ALog.w(TAG, "任务已存在"); diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java index 83f82b28..5cfdfced 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java @@ -18,6 +18,7 @@ package com.arialyy.aria.core.scheduler; import android.os.CountDownTimer; import android.os.Message; import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.download.DownloadGroupTask; import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.AbsNormalEntity; @@ -38,8 +39,8 @@ import java.util.concurrent.ConcurrentHashMap; * Created by lyy on 2017/6/4. * 事件调度器,用于处理任务状态的调度 */ -abstract class AbsSchedulers, QUEUE extends ITaskQueue> - implements ISchedulers { +abstract class AbsSchedulers, + QUEUE extends ITaskQueue> implements ISchedulers { private final String TAG = "AbsSchedulers"; protected QUEUE mQueue; @@ -54,12 +55,12 @@ abstract class AbsSchedulers listener = mObservers.get(targetName); + AbsSchedulerListener listener = mObservers.get(getKey(obj)); if (listener == null) { listener = createListener(targetName); if (listener != null) { listener.setListener(obj); - mObservers.put(targetName, listener); + mObservers.put(getKey(obj), listener); } else { ALog.e(TAG, "注册错误,没有【" + targetName + "】观察者"); } @@ -67,18 +68,22 @@ abstract class AbsSchedulers>> iter = mObservers.entrySet().iterator(); iter.hasNext(); ) { Map.Entry> entry = iter.next(); - if (entry.getKey().equals(obj.getClass().getName())) { + if (entry.getKey().equals(getKey(obj))) { iter.remove(); } } } + private String getKey(Object obj) { + return obj.getClass().getName() + obj.hashCode(); + } + /** * 创建代理类 * @@ -178,7 +183,9 @@ abstract class AbsSchedulers reTryNum) { + callback(FAIL, task); + mQueue.removeTaskFormQueue(task.getKey()); + startNextTask(); + TEManager.getInstance().removeTEntity(task.getKey()); + return; + } CountDownTimer timer = new CountDownTimer(interval, 1000) { @Override public void onTick(long millisUntilFinished) { @@ -269,7 +283,7 @@ abstract class AbsSchedulers> { +abstract class AbsUploadTarget + extends AbsTarget { - /** - * 通过key创建任务 - */ - TASK_ENTITY create(String groupName, List urls); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/BaseNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/BaseNormalTarget.java new file mode 100644 index 00000000..ad08ddb8 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/BaseNormalTarget.java @@ -0,0 +1,133 @@ +/* + * 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.upload; + +import android.support.annotation.NonNull; +import android.text.TextUtils; +import com.arialyy.aria.core.manager.TEManager; +import com.arialyy.aria.core.queue.UploadTaskQueue; +import com.arialyy.aria.util.ALog; +import java.io.File; + +/** + * Created by AriaL on 2018/3/9. + */ +abstract class BaseNormalTarget + extends AbsUploadTarget { + + protected String mTempUrl; + + void initTarget(String filePath) { + mTaskEntity = TEManager.getInstance().getTEntity(UploadTaskEntity.class, filePath); + mEntity = mTaskEntity.getEntity(); + File file = new File(filePath); + mEntity.setFileName(file.getName()); + mEntity.setFileSize(file.length()); + mTempUrl = mEntity.getUrl(); + } + + /** + * 设置上传路径 + * + * @param uploadUrl 上传路径 + */ + public TARGET setUploadUrl(@NonNull String uploadUrl) { + mTempUrl = uploadUrl; + return (TARGET) this; + } + + /** + * 上传任务是否存在 + * + * @return {@code true}存在 + */ + @Override public boolean taskExists() { + return UploadTaskQueue.getInstance().getTask(mEntity.getFilePath()) != null; + } + + /** + * 是否在上传 + * + * @deprecated {@link #isRunning()} + */ + public boolean isUploading() { + return isRunning(); + } + + @Override public boolean isRunning() { + UploadTask task = UploadTaskQueue.getInstance().getTask(mEntity.getKey()); + return task != null && task.isRunning(); + } + + @Override protected boolean checkEntity() { + boolean b = checkUrl() && checkFilePath(); + if (b) { + mEntity.save(); + mTaskEntity.save(); + } + return b; + } + + /** + * 检查上传文件路径是否合法 + * + * @return {@code true} 合法 + */ + private boolean checkFilePath() { + String filePath = mEntity.getFilePath(); + if (TextUtils.isEmpty(filePath)) { + ALog.e(TAG, "上传失败,文件路径为null"); + return false; + } else if (!filePath.startsWith("/")) { + ALog.e(TAG, "上传失败,文件路径【" + filePath + "】不合法"); + return false; + } + + File file = new File(mEntity.getFilePath()); + if (!file.exists()) { + ALog.e(TAG, "上传失败,文件【" + filePath + "】不存在"); + return false; + } + if (file.isDirectory()) { + ALog.e(TAG, "上传失败,文件【" + filePath + "】不能是文件夹"); + return false; + } + return true; + } + + /** + * 检查普通任务的下载地址 + * + * @return {@code true}地址合法 + */ + protected boolean checkUrl() { + final String url = mTempUrl; + 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 false; + } + mEntity.setUrl(url); + return true; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/BaseUListener.java b/Aria/src/main/java/com/arialyy/aria/core/upload/BaseUListener.java index 48aa5b73..fcb7ad05 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/BaseUListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/BaseUListener.java @@ -136,11 +136,11 @@ class BaseUListener { - private final String TAG = "FtpUploadTarget"; +public class FtpUploadTarget extends BaseNormalTarget + implements IFtpTarget { + private FtpDelegate mDelegate; + + private String mAccount, mUser, mPw; + private boolean needLogin = false; FtpUploadTarget(String filePath, String targetName) { this.mTargetName = targetName; @@ -41,84 +39,52 @@ public class FtpUploadTarget } private void initTask(String filePath) { - mTaskEntity = TEManager.getInstance().getTEntity(UploadTaskEntity.class, filePath); - if (mTaskEntity == null) { - mTaskEntity = TEManager.getInstance().createTEntity(UploadTaskEntity.class, filePath); - } - mEntity = mTaskEntity.entity; - File file = new File(filePath); - mEntity.setFileName(file.getName()); - mEntity.setFileSize(file.length()); - mTaskEntity.requestType = AbsTaskEntity.U_FTP; + initTarget(filePath); + mTaskEntity.setRequestType(AbsTaskEntity.U_FTP); + mDelegate = new FtpDelegate<>(this, mTaskEntity); } /** - * 设置上传路径,FTP上传路径必须是从"/"开始的完整路径 - * - * @param uploadUrl 上传路径 + * 添加任务 */ - public FtpUploadTarget setUploadUrl(@NonNull String uploadUrl) { - uploadUrl = CheckUtil.checkUrl(uploadUrl); - if (!uploadUrl.endsWith("/")) { - uploadUrl += "/"; + public void add() { + if (checkEntity()) { + AriaManager.getInstance(AriaManager.APP) + .setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_CREATE, + checkTaskType())) + .exe(); } - mTaskEntity.urlEntity = CommonUtil.getFtpUrlInfo(uploadUrl); - if (mEntity.getUrl().equals(uploadUrl)) return this; - mEntity.setUrl(uploadUrl); - mEntity.update(); - return this; } - /** - * 设置字符编码 - */ - public FtpUploadTarget charSet(String charSet) { - if (TextUtils.isEmpty(charSet)) return this; - mTaskEntity.charSet = charSet; - return this; + @Override protected boolean checkUrl() { + boolean b = super.checkUrl(); + if (!b) { + return false; + } + mTaskEntity.setUrlEntity(CommonUtil.getFtpUrlInfo(mTempUrl)); + mTaskEntity.getUrlEntity().account = mAccount; + mTaskEntity.getUrlEntity().user = mUser; + mTaskEntity.getUrlEntity().password = mPw; + mTaskEntity.getUrlEntity().needLogin = needLogin; + return true; } - /** - * ftp 用户登录信。 - * 设置登录信息需要在设置上传链接之后{@link #setUploadUrl(String)} - * - * @param userName ftp用户名 - * @param password ftp用户密码 - */ - public FtpUploadTarget login(String userName, String password) { - return login(userName, password, null); + @Override public FtpUploadTarget charSet(String charSet) { + return mDelegate.charSet(charSet); } - /** - * ftp 用户登录信息 - * 设置登录信息需要在设置上传链接之后{@link #setUploadUrl(String)} - * - * @param userName ftp用户名 - * @param password ftp用户密码 - * @param account ftp账号 - */ - public FtpUploadTarget login(String userName, String password, String account) { - if (TextUtils.isEmpty(userName)) { - ALog.e(TAG, "用户名不能为null"); - return this; - } else if (TextUtils.isEmpty(password)) { - ALog.e(TAG, "密码不能为null"); - return this; - } - mTaskEntity.urlEntity.needLogin = true; - mTaskEntity.urlEntity.user = userName; - mTaskEntity.urlEntity.password = password; - mTaskEntity.urlEntity.account = account; + @Override public FtpUploadTarget login(String userName, String password) { + needLogin = true; + mUser = userName; + mPw = password; return this; } - /** - * 添加任务 - */ - public void add() { - AriaManager.getInstance(AriaManager.APP) - .setCmd(CommonUtil.createNormalCmd(mTargetName, mTaskEntity, NormalCmdFactory.TASK_CREATE, - checkTaskType())) - .exe(); + @Override public FtpUploadTarget login(String userName, String password, String account) { + needLogin = true; + mUser = userName; + mPw = password; + mAccount = account; + return this; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java index 7c273459..bf80d9ff 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java @@ -19,15 +19,17 @@ import android.os.Parcel; import android.os.Parcelable; import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.orm.Foreign; -import com.arialyy.aria.orm.Primary; +import com.arialyy.aria.orm.annotation.Primary; /** * Created by lyy on 2017/2/9. * 上传文件实体 */ public class UploadEntity extends AbsNormalEntity implements Parcelable { - @Primary @Foreign(table = UploadTaskEntity.class, column = "key") private String filePath; //文件路径 + /** + * 文件上传路径 + */ + @Primary private String filePath; /** * 上传完成后服务器返回的数据 diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index a86c2219..2a56a287 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -20,9 +20,8 @@ import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.command.ICmd; import com.arialyy.aria.core.command.normal.NormalCmdFactory; import com.arialyy.aria.core.common.ProxyHelper; -import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.AbsReceiver; -import com.arialyy.aria.core.scheduler.ISchedulerListener; import com.arialyy.aria.core.scheduler.UploadSchedulers; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.CheckUtil; @@ -36,7 +35,6 @@ import java.util.Set; */ public class UploadReceiver extends AbsReceiver { private static final String TAG = "UploadReceiver"; - public ISchedulerListener listener; /** * 加载HTTP单文件上传任务 @@ -72,10 +70,32 @@ public class UploadReceiver extends AbsReceiver { return DbEntity.findFirst(UploadEntity.class, "filePath=?", filePath) != null; } + /** + * 获取所有普通上传任务 + * 获取未完成的普通任务列表{@link #getAllNotCompletTask()} + * 获取已经完成的普通任务列表{@link #getAllCompleteTask()} + */ @Override public List getTaskList() { return DbEntity.findAllData(UploadEntity.class); } + /** + * 获取所有未完成的普通上传任务 + */ + public List getAllNotCompletTask() { + return UploadEntity.findDatas(UploadEntity.class, + "isGroupChild=? and isComplete=?", "false", "false"); + } + + /** + * 获取所有已经完成的普通任务 + */ + public List getAllCompleteTask() { + return UploadEntity.findDatas(UploadEntity.class, + "isGroupChild=? and isComplete=?", "false", "true"); + } + + @Override public void stopAllTask() { AriaManager.getInstance(AriaManager.APP) .setCmd(NormalCmdFactory.getInstance() @@ -93,7 +113,7 @@ public class UploadReceiver extends AbsReceiver { @Override public void removeAllTask(boolean removeFile) { final AriaManager am = AriaManager.getInstance(AriaManager.APP); - am.setCmd(CommonUtil.createNormalCmd(targetName, new DownloadTaskEntity(), + am.setCmd(CommonUtil.createNormalCmd(targetName, new UploadTaskEntity(), NormalCmdFactory.TASK_CANCEL_ALL, ICmd.TASK_TYPE_UPLOAD)).exe(); Set keys = am.getReceiver().keySet(); @@ -104,7 +124,6 @@ public class UploadReceiver extends AbsReceiver { @Override public void destroy() { targetName = null; - listener = null; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java index b3b49956..dd9ac48a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadTarget.java @@ -16,16 +16,19 @@ package com.arialyy.aria.core.upload; import android.support.annotation.NonNull; -import com.arialyy.aria.core.inf.AbsUploadTarget; -import com.arialyy.aria.core.manager.TEManager; +import com.arialyy.aria.core.common.RequestEnum; +import com.arialyy.aria.core.delegate.HttpHeaderDelegate; import com.arialyy.aria.core.inf.AbsTaskEntity; -import java.io.File; +import com.arialyy.aria.core.inf.IHttpHeaderTarget; +import java.util.Map; /** * Created by lyy on 2017/2/28. - * http 当文件上传 + * http 单文件上传 */ -public class UploadTarget extends AbsUploadTarget { +public class UploadTarget extends BaseNormalTarget + implements IHttpHeaderTarget { + private HttpHeaderDelegate mDelegate; UploadTarget(String filePath, String targetName) { this.mTargetName = targetName; @@ -33,24 +36,19 @@ public class UploadTarget extends AbsUploadTarget(this, mTaskEntity); } /** * 设置userAgent */ public UploadTarget setUserAngent(@NonNull String userAgent) { - mTaskEntity.userAgent = userAgent; + mTaskEntity.setUserAgent(userAgent); return this; } @@ -60,7 +58,7 @@ public class UploadTarget extends AbsUploadTarget headers) { + return mDelegate.addHeaders(headers); + } + + @Override public UploadTarget setRequestMode(RequestEnum requestEnum) { + return mDelegate.setRequestMode(requestEnum); + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFileInfoThread.java b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFileInfoThread.java index 987330d2..f0a5e0f5 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFileInfoThread.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFileInfoThread.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.upload.uploader; import com.arialyy.aria.core.common.AbsFtpInfoThread; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadTaskEntity; @@ -39,8 +40,7 @@ class FtpFileInfoThread extends AbsFtpInfoThread } @Override protected String setRemotePath() { - String url = mEntity.getUrl(); - return mTaskEntity.urlEntity.remotePath + "/" + mEntity.getFileName(); + return mTaskEntity.getUrlEntity().remotePath + "/" + mEntity.getFileName(); } /** @@ -57,6 +57,7 @@ class FtpFileInfoThread extends AbsFtpInfoThread //远程文件已完成 if (ftpFile.getSize() == mEntity.getFileSize()) { isComplete = true; + ALog.d(TAG, "FTP服务器上已存在该文件【" + ftpFile.getName() + "】"); } else { ALog.w(TAG, "FTP服务器已存在未完成的文件【" + ftpFile.getName() @@ -64,16 +65,16 @@ class FtpFileInfoThread extends AbsFtpInfoThread + ftpFile.getSize() + "】" + "尝试从位置:" - + ftpFile.getSize() + + (ftpFile.getSize() - 1) + "开始上传"); File configFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName())); Properties pro = CommonUtil.loadConfig(configFile); String key = mEntity.getFileName() + "_record_" + 0; - mTaskEntity.isNewTask = false; + mTaskEntity.setNewTask(false); long oldRecord = Long.parseLong(pro.getProperty(key, "0")); - if (oldRecord == 0) { + if (oldRecord == 0 || oldRecord != ftpFile.getSize()) { //修改本地保存的停止地址为服务器上对应文件的大小 - pro.setProperty(key, ftpFile.getSize() + ""); + pro.setProperty(key, (ftpFile.getSize() - 1) + ""); CommonUtil.saveConfig(configFile, pro); } } @@ -82,6 +83,6 @@ class FtpFileInfoThread extends AbsFtpInfoThread @Override protected void onPreComplete(int code) { super.onPreComplete(code); - mCallback.onComplete(mEntity.getKey(), isComplete ? CODE_COMPLETE : code); + mCallback.onComplete(mEntity.getKey(), new CompleteInfo(isComplete ? CODE_COMPLETE : code)); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java index f5ee778a..c6d7c497 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java @@ -32,7 +32,7 @@ import org.apache.commons.net.ftp.OnFtpInputStreamListener; /** * Created by Aria.Lao on 2017/7/28. - * FTP 单线程上传任务,需要FTP 服务器给用户打开删除和读入IO的权限 + * FTP 单线程上传任务,需要FTP 服务器给用户打开append和write的权限 */ class FtpThreadTask extends AbsFtpThreadTask { private final String TAG = "FtpUploadThreadTask"; @@ -112,9 +112,9 @@ class FtpThreadTask extends AbsFtpThreadTask { } private void initPath() throws UnsupportedEncodingException { - dir = new String(mTaskEntity.urlEntity.remotePath.getBytes(charSet), SERVER_CHARSET); + dir = new String(mTaskEntity.getUrlEntity().remotePath.getBytes(charSet), SERVER_CHARSET); remotePath = new String( - ("/" + mTaskEntity.urlEntity.remotePath + mEntity.getFileName()).getBytes(charSet), + ("/" + mTaskEntity.getUrlEntity().remotePath + "/" + mEntity.getFileName()).getBytes(charSet), SERVER_CHARSET); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/HttpThreadTask.java b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/HttpThreadTask.java index 8783aaeb..6457da57 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/HttpThreadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/HttpThreadTask.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.core.upload.uploader; +import android.util.Log; import com.arialyy.aria.core.common.AbsThreadTask; import com.arialyy.aria.core.common.StateConstance; import com.arialyy.aria.core.common.SubThreadConfig; @@ -65,30 +66,33 @@ class HttpThreadTask extends AbsThreadTask { try { url = new URL(mEntity.getUrl()); mHttpConn = (HttpURLConnection) url.openConnection(); + mHttpConn.setRequestMethod(mTaskEntity.getRequestEnum().name); mHttpConn.setUseCaches(false); mHttpConn.setDoOutput(true); mHttpConn.setDoInput(true); + mHttpConn.setRequestProperty("Connection", "Keep-Alive"); mHttpConn.setRequestProperty("Content-Type", - mTaskEntity.contentType + "; boundary=" + BOUNDARY); - mHttpConn.setRequestProperty("User-Agent", mTaskEntity.userAgent); + mTaskEntity.getContentType() + "; boundary=" + BOUNDARY); + mHttpConn.setRequestProperty("User-Agent", mTaskEntity.getUserAgent()); + mHttpConn.setConnectTimeout(5000); //mHttpConn.setRequestProperty("Range", "bytes=" + 0 + "-" + "100"); //内部缓冲区---分段上传防止oom mHttpConn.setChunkedStreamingMode(1024); //添加Http请求头部 - Set keys = mTaskEntity.headers.keySet(); + Set keys = mTaskEntity.getHeaders().keySet(); for (String key : keys) { - mHttpConn.setRequestProperty(key, mTaskEntity.headers.get(key)); + mHttpConn.setRequestProperty(key, mTaskEntity.getHeaders().get(key)); } mOutputStream = mHttpConn.getOutputStream(); PrintWriter writer = - new PrintWriter(new OutputStreamWriter(mOutputStream, mTaskEntity.charSet), true); + new PrintWriter(new OutputStreamWriter(mOutputStream, mTaskEntity.getCharSet()), true); //添加文件上传表单字段 - keys = mTaskEntity.formFields.keySet(); + keys = mTaskEntity.getFormFields().keySet(); for (String key : keys) { - addFormField(writer, key, mTaskEntity.formFields.get(key)); + addFormField(writer, key, mTaskEntity.getFormFields().get(key)); } - uploadFile(writer, mTaskEntity.attachment, uploadFile); + uploadFile(writer, mTaskEntity.getAttachment(), uploadFile); mTaskEntity.getEntity().setResponseStr(finish(writer)); mListener.onComplete(); } catch (Exception e) { @@ -119,7 +123,7 @@ class HttpThreadTask extends AbsThreadTask { .append("\"") .append(LINE_END); writer.append("Content-Type: text/plain; charset=") - .append(mTaskEntity.charSet) + .append(mTaskEntity.getCharSet()) .append(LINE_END); writer.append(LINE_END); writer.append(value).append(LINE_END); diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java index 94c9d486..9f4d1c1c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.core.upload.uploader; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.IUtil; import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.inf.AbsTaskEntity; @@ -48,11 +49,11 @@ public class SimpleUploadUtil implements IUtil, Runnable { @Override public void run() { mListener.onPre(); - switch (mTaskEntity.requestType) { + switch (mTaskEntity.getRequestType()) { case AbsTaskEntity.U_FTP: new FtpFileInfoThread(mTaskEntity, new OnFileInfoCallback() { - @Override public void onComplete(String url, int code) { - if (code == FtpFileInfoThread.CODE_COMPLETE) { + @Override public void onComplete(String url, CompleteInfo info) { + if (info.code == FtpFileInfoThread.CODE_COMPLETE) { mListener.onComplete(); } else { mUploader.start(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java index a8b5b607..48c7aa3b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java @@ -50,7 +50,7 @@ class Uploader extends AbsFileer { */ protected void checkTask() { mConfigFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName())); - if (!mTaskEntity.isSupportBP) { + if (!mTaskEntity.isSupportBP()) { isNewTask = true; return; } @@ -65,16 +65,16 @@ class Uploader extends AbsFileer { } } - @Override protected void handleNewTask() { - + @Override protected boolean handleNewTask() { + return true; } - @Override protected int getNewTaskThreadNum() { + @Override protected int setNewTaskThreadNum() { return 1; } @Override protected AbsThreadTask selectThreadTask(SubThreadConfig config) { - switch (mTaskEntity.requestType) { + switch (mTaskEntity.getRequestType()) { case AbsTaskEntity.U_FTP: return new FtpThreadTask(mConstance, mListener, config); case AbsTaskEntity.U_HTTP: diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/wrapper/UTEWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/upload/wrapper/UTEWrapper.java new file mode 100644 index 00000000..e94de626 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/wrapper/UTEWrapper.java @@ -0,0 +1,47 @@ +/* + * 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.upload.wrapper; + +import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.upload.UploadTaskEntity; +import com.arialyy.aria.orm.AbsWrapper; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Wrapper; +import java.util.List; + +/** + * Created by laoyuyu on 2018/3/30. + */ +@Wrapper +public class UTEWrapper extends AbsWrapper { + + @One + public UploadEntity entity; + + @Many(parentColumn = "filePath", entityColumn = "key") + private List taskEntitys = null; + + public UploadTaskEntity taskEntity; + + @Override public void handleConvert() { + //taskEntity.entity = (tEntity == null || tEntity.isEmpty()) ? null : tEntity.get(0); + taskEntity = (taskEntitys == null || taskEntitys.isEmpty()) ? null : taskEntitys.get(0); + if (taskEntity != null) { + taskEntity.setEntity(entity); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/AbsDelegate.java b/Aria/src/main/java/com/arialyy/aria/orm/AbsDelegate.java new file mode 100644 index 00000000..fa5dd96d --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/AbsDelegate.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.support.v4.util.LruCache; +import android.text.TextUtils; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; +import java.lang.reflect.Field; +import java.net.URLEncoder; + +/** + * Created by laoyuyu on 2018/3/22. + */ +abstract class AbsDelegate { + static final String TAG = "AbsDelegate"; + static final int CREATE_TABLE = 0; + static final int TABLE_EXISTS = 1; + static final int INSERT_DATA = 2; + static final int MODIFY_DATA = 3; + static final int FIND_DATA = 4; + static final int FIND_ALL_DATA = 5; + static final int DEL_DATA = 6; + static final int ROW_ID = 7; + static final int RELATION = 8; + static final int DROP_TABLE = 9; + + static LruCache 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 FIND_ALL_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; + } + + /** + * 检查list参数是否合法,list只能是{@code List} + * + * @return {@code true} 合法 + */ + boolean checkList(Field list) { + Class t = CommonUtil.getListParamType(list); + if (t != null && t == String.class) { + return true; + } else { + ALog.d(TAG, "map参数错误,支持List的参数字段"); + return false; + } + } + + /** + * 检查map参数是否合法,map只能是{@code Map} + * + * @return {@code true} 合法 + */ + boolean checkMap(Field map) { + Class[] ts = CommonUtil.getMapParamType(map); + if (ts != null + && ts[0] != null + && ts[1] != null + && ts[0] == String.class + && ts[1] == String.class) { + return true; + } else { + ALog.d(TAG, "map参数错误,支持Map的参数字段"); + return false; + } + } + + /** + * 为了防止特殊字符串导致存储失败,需要使用URL编码保存的字符串 + * + * @param value 需要保存的内容 + * @return 转换后的内容 + */ + String convertValue(String value) { + if (!TextUtils.isEmpty(value) && value.contains("'")) { + return URLEncoder.encode(value); + } + return value; + } + + void closeCursor(Cursor cursor) { + if (cursor != null && !cursor.isClosed()) { + try { + cursor.close(); + } catch (android.database.SQLException e) { + e.printStackTrace(); + } + } + } + + void close(SQLiteDatabase db) { + //if (db != null && db.isOpen()) db.close(); + } + + /** + * 检查数据库是否关闭,已经关闭的话,打开数据库 + * + * @return 返回数据库 + */ + SQLiteDatabase checkDb(SQLiteDatabase db) { + if (db == null || !db.isOpen()) { + db = SqlHelper.INSTANCE.getWritableDatabase(); + } + return db; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/AbsWrapper.java b/Aria/src/main/java/com/arialyy/aria/orm/AbsWrapper.java new file mode 100644 index 00000000..49f4eca1 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/AbsWrapper.java @@ -0,0 +1,27 @@ +/* + * 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; + +/** + * Created by laoyuyu on 2018/3/30. + */ +public abstract class AbsWrapper { + + /** + * 处理转换 + */ + protected abstract void handleConvert(); +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/ActionPolicy.java b/Aria/src/main/java/com/arialyy/aria/orm/ActionPolicy.java new file mode 100644 index 00000000..614a35dc --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/ActionPolicy.java @@ -0,0 +1,62 @@ +/* + * 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 com.arialyy.aria.orm.annotation.Foreign; + +/** + * Created by laoyuyu on 2018/3/22. + * on update 或 on delete 都可跟不同action功能 + * + * @see + * {@link Foreign#onDelete()}、{@link Foreign#onUpdate()} + */ +public enum ActionPolicy { + + /** + * 如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作 + */ + NO_ACTION("NO ACTION"), + + /** + * 和NO ACTION 作用一致,和NO ACTION的区别是: + * 主表update/delete执行时,马上就触发约束; + * 而NO ACTION 是执行完成语句后才触发约束, + */ + RESTRICT("RESTRICT"), + + /** + * 在父表上update/delete记录时,将子表上匹配记录的列设为null (要注意子表的外键列不能为not null) + */ + SET_NULL("SET NULL"), + + /** + * 父表有变更时,子表将外键列设置成一个默认的值,default配置的值 + */ + SET_DEFAULT("SET DEFAULT"), + + /** + * 在父表上update/delete记录时,同步update/delete掉子表的匹配记录 + */ + CASCADE("CASCADE"); + + String function; + + ActionPolicy(String function) { + this.function = function; + } + +} 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 5196af1b..8d9fb2ac 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -16,8 +16,6 @@ package com.arialyy.aria.orm; import android.text.TextUtils; -import com.arialyy.aria.core.ErrorEntity; -import com.arialyy.aria.core.UrlMapping; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; @@ -32,9 +30,15 @@ import java.util.Map; * 数据库配置信息 */ class DBConfig { + /*adb pull /mnt/sdcard/Android/data/com.arialyy.simple/files/DB/AriaLyyDb d:/db*/ static Map mapping = new HashMap<>(); static String DB_NAME; - static int VERSION = 23; + static int VERSION = 31; + + /** + * 是否将数据库保存在Sd卡,{@code true} 是 + */ + static final boolean SAVE_IN_SDCARD = false; static { if (TextUtils.isEmpty(DB_NAME)) { @@ -46,13 +50,11 @@ class DBConfig { } static { - mapping.put("DownloadEntity", DownloadEntity.class); + mapping.put("DownloadGroupTaskEntity", DownloadGroupTaskEntity.class); mapping.put("DownloadGroupEntity", DownloadGroupEntity.class); mapping.put("DownloadTaskEntity", DownloadTaskEntity.class); - mapping.put("DownloadGroupTaskEntity", DownloadGroupTaskEntity.class); - mapping.put("UploadEntity", UploadEntity.class); mapping.put("UploadTaskEntity", UploadTaskEntity.class); - mapping.put("ErrorEntity", ErrorEntity.class); - //mapping.put("UrlMapping", UrlMapping.class); + mapping.put("DownloadEntity", DownloadEntity.class); + mapping.put("UploadEntity", UploadEntity.class); } } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DatabaseContext.java b/Aria/src/main/java/com/arialyy/aria/orm/DatabaseContext.java new file mode 100644 index 00000000..c606676a --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DatabaseContext.java @@ -0,0 +1,93 @@ +/* + * 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.content.ContextWrapper; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import com.arialyy.aria.util.CommonUtil; +import java.io.File; +import java.io.IOException; + +/** + * 保存在sd卡的数据库使用的Context + */ +class DatabaseContext extends ContextWrapper { + public DatabaseContext(Context context) { + super(context); + } + + /** + * 获得数据库路径,如果不存在,则创建对象对象 + */ + @Override + public File getDatabasePath(String name) { + String dbDir = CommonUtil.getAppPath(getBaseContext()); + + dbDir += "DB";//数据库所在目录 + String dbPath = dbDir + "/" + name;//数据库路径 + //判断目录是否存在,不存在则创建该目录 + File dirFile = new File(dbDir); + if (!dirFile.exists()) { + dirFile.mkdirs(); + } + + //数据库文件是否创建成功 + boolean isFileCreateSuccess = false; + //判断文件是否存在,不存在则创建该文件 + File dbFile = new File(dbPath); + if (!dbFile.exists()) { + try { + isFileCreateSuccess = dbFile.createNewFile();//创建文件 + } catch (IOException e) { + e.printStackTrace(); + } + } else { + isFileCreateSuccess = true; + } + + //返回数据库文件对象 + if (isFileCreateSuccess) { + return dbFile; + } else { + return null; + } + } + + /** + * 重载这个方法,是用来打开SD卡上的数据库的,android 2.3及以下会调用这个方法。 + */ + @Override + public SQLiteDatabase openOrCreateDatabase(String name, int mode, + SQLiteDatabase.CursorFactory factory) { + return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null); + } + + /** + * Android 4.0会调用此方法获取数据库。 + * + * @see android.content.ContextWrapper#openOrCreateDatabase(java.lang.String, int, + * android.database.sqlite.SQLiteDatabase.CursorFactory, + * android.database.DatabaseErrorHandler) + */ + @Override + public SQLiteDatabase openOrCreateDatabase(String name, int mode, + SQLiteDatabase.CursorFactory factory, + DatabaseErrorHandler errorHandler) { + return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null); + } +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java b/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java index 8b6ced31..9466bef0 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java @@ -16,35 +16,29 @@ package com.arialyy.aria.orm; -import android.support.annotation.NonNull; -import com.arialyy.aria.util.CommonUtil; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * Created by lyy on 2015/11/2. * 所有数据库实体父类 */ -public class DbEntity { +public abstract class DbEntity { private static final Object LOCK = new Object(); - protected int rowID = -1; + protected long rowID = -1; protected DbEntity() { } - ///** - // * 关键字模糊检索全文 - // * - // * @param column 需要查找的列 - // * @param mathSql 关键字语法,exsimple “white OR green”、“blue AND red”、“white NOT green” - // */ - //public static List searchData(Class clazz, String column, String mathSql) { - // return DbUtil.getInstance().searchData(clazz, column, mathSql); - //} + /** + * 查询关联数据 + * + * @param expression 查询条件 + */ + public static List findRelationData(Class clazz, + String... expression) { + return DelegateWrapper.getInstance().findRelationData(clazz, expression); + } /** * 检查某个字段的值是否存在 @@ -53,21 +47,21 @@ public class DbEntity { * @return {@code true}该字段的对应的value已存在 */ public static boolean checkDataExist(Class clazz, String... expression) { - return DbUtil.getInstance().checkDataExist(clazz, expression); + return DelegateWrapper.getInstance().checkDataExist(clazz, expression); } /** * 清空表数据 */ public static void clean(Class clazz) { - DbUtil.getInstance().clean(clazz); + DelegateWrapper.getInstance().clean(clazz); } /** * 直接执行sql语句 */ public static void exeSql(String sql) { - DbUtil.getInstance().exeSql(sql); + DelegateWrapper.getInstance().exeSql(sql); } /** @@ -76,7 +70,7 @@ public class DbEntity { * @return 没有数据返回null */ public static List findAllData(Class clazz) { - DbUtil util = DbUtil.getInstance(); + DelegateWrapper util = DelegateWrapper.getInstance(); return util.findAllData(clazz); } @@ -97,7 +91,7 @@ public class DbEntity { * @return 没有数据返回null */ public static List findDatas(Class clazz, String... expression) { - DbUtil util = DbUtil.getInstance(); + DelegateWrapper util = DelegateWrapper.getInstance(); return util.findData(clazz, expression); } @@ -110,25 +104,11 @@ public class DbEntity { * @return 没有数据返回null */ public static T findFirst(Class clazz, String... expression) { - DbUtil util = DbUtil.getInstance(); + DelegateWrapper util = DelegateWrapper.getInstance(); List datas = util.findData(clazz, expression); return datas == null ? null : datas.size() > 0 ? datas.get(0) : null; } - /** - * 获取所有行的rowid - */ - public int[] getRowIds() { - return DbUtil.getInstance().getRowId(getClass()); - } - - /** - * 获取rowid - */ - public int getRowId(@NonNull Object[] wheres, @NonNull Object[] values) { - return DbUtil.getInstance().getRowId(getClass(), wheres, values); - } - /** * 删除当前数据 */ @@ -143,7 +123,7 @@ public class DbEntity { * */ public static void deleteData(Class clazz, String... expression) { - DbUtil util = DbUtil.getInstance(); + DelegateWrapper util = DelegateWrapper.getInstance(); util.delData(clazz, expression); } @@ -151,11 +131,12 @@ public class DbEntity { * 修改数据 */ public void update() { - DbUtil.getInstance().modifyData(this); + DelegateWrapper.getInstance().modifyData(this); } /** * 保存自身,如果表中已经有数据,则更新数据,否则插入数据 + * 只有 target中checkEntity成功后才能保存,创建实体部分也不允许保存 */ public void save() { synchronized (LOCK) { @@ -171,58 +152,14 @@ public class DbEntity { * 查找数据在表中是否存在 */ private boolean thisIsExist() { - DbUtil util = DbUtil.getInstance(); - return util.isExist(getClass(), rowID); + DelegateWrapper util = DelegateWrapper.getInstance(); + return rowID != -1 && util.isExist(getClass(), rowID); } /** - * 插入数据 + * 插入数据,只有 target中checkEntity成功后才能插入,创建实体部分也不允许操作 */ public void insert() { - DbUtil.getInstance().insertData(this); - updateRowID(); - } - - private T findFirst(Class clazz, @NonNull String[] wheres, - @NonNull String[] values) { - DbUtil util = DbUtil.getInstance(); - List list = util.findData(clazz, wheres, values); - return list == null ? null : list.get(0); - } - - private void updateRowID() { - try { - List fields = CommonUtil.getAllFields(getClass()); - List where = new ArrayList<>(); - List values = new ArrayList<>(); - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - where.add(field.getName()); - Type type = field.getType(); - if (SqlUtil.isOneToOne(field)) { - values.add(SqlUtil.getOneToOneParams(field)); - } else if (type == List.class) { - if (SqlUtil.isOneToMany(field)) { - values.add(SqlUtil.getOneToManyElementParams(field)); - } else { - values.add(SqlUtil.list2Str(this, field)); - } - } else if (type == Map.class) { - values.add(SqlUtil.map2Str((Map) field.get(this))); - } else { - values.add(field.get(this) + ""); - } - } - DbEntity entity = findFirst(getClass(), where.toArray(new String[where.size()]), - values.toArray(new String[values.size()])); - if (entity != null) { - rowID = entity.rowID; - } - } catch (IllegalAccessException e) { - e.printStackTrace(); - } + DelegateWrapper.getInstance().insertData(this); } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java b/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java deleted file mode 100644 index 7e1bf8d9..00000000 --- a/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.arialyy.aria.orm; - -import android.app.Application; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.support.annotation.NonNull; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CheckUtil; -import com.arialyy.aria.util.CommonUtil; -import java.util.List; - -/** - * Created by lyy on 2015/2/11. - * 数据库操作工具 - */ -public class DbUtil { - private static final String TAG = "DbUtil"; - private volatile static DbUtil INSTANCE = null; - private int ROW_ID = 7; - private SQLiteDatabase mDb; - private SqlHelper mHelper; - - private DbUtil() { - - } - - private DbUtil(Context context) { - mHelper = SqlHelper.init(context.getApplicationContext()); - } - - public static DbUtil init(Context context) { - if (context instanceof Application) { - synchronized (AriaManager.LOCK) { - if (INSTANCE == null) { - INSTANCE = new DbUtil(context); - } - } - } - return INSTANCE; - } - - protected static DbUtil getInstance() { - if (INSTANCE == null) { - throw new NullPointerException("请在Application中调用init进行数据库工具注册注册"); - } - return INSTANCE; - } - - ///** - // * 关键字模糊检索全文 - // * - // * @param column 需要查找的列 - // * @param mathSql 关键字语法,exsimple “white OR green”、“blue AND red”、“white NOT green” - // */ - //public List searchData(Class clazz, String column, String mathSql) { - // checkDb(); - // return SqlHelper.searchData(mDb, clazz, column, mathSql); - //} - - /** - * 检查某个字段的值是否存在 - * - * @param expression 字段和值"url=xxx" - * @return {@code true}该字段的对应的value已存在 - */ - synchronized boolean checkDataExist(Class clazz, String... expression) { - checkDb(); - return SqlHelper.checkDataExist(mDb, clazz, expression); - } - - /** - * 清空表数据 - */ - synchronized void clean(Class clazz) { - checkDb(); - String tableName = CommonUtil.getClassName(clazz); - if (tableExists(clazz)) { - String sql = "DELETE FROM " + tableName; - exeSql(sql); - } - } - - /** - * 执行sql语句 - */ - void exeSql(String sql) { - mDb.execSQL(sql); - } - - /** - * 删除某条数据 - */ - synchronized void delData(Class clazz, String... expression) { - CheckUtil.checkSqlExpression(expression); - checkDb(); - SqlHelper.delData(mDb, clazz, expression); - } - - /** - * 修改某行数据 - */ - synchronized void modifyData(DbEntity dbEntity) { - checkDb(); - SqlHelper.modifyData(mDb, dbEntity); - } - - /** - * 遍历所有数据 - */ - synchronized List findAllData(Class clazz) { - checkDb(); - return SqlHelper.findAllData(mDb, clazz); - } - - /** - * 条件查寻数据 - */ - synchronized List findData(Class clazz, String... expression) { - checkDb(); - return SqlHelper.findData(mDb, clazz, expression); - } - - /** - * 通过rowId判断数据是否存在 - */ - synchronized boolean isExist(Class clazz, int rowId) { - checkDb(); - String sql = "SELECT rowid FROM " + CommonUtil.getClassName(clazz) + " WHERE rowid=" + rowId; - Cursor cursor = mDb.rawQuery(sql, null); - boolean isExist = cursor.getCount() > 0; - cursor.close(); - return isExist; - } - - /** - * 条件查寻数据 - */ - @Deprecated synchronized List findData(Class clazz, - @NonNull String[] wheres, @NonNull String[] values) { - checkDb(); - return SqlHelper.findData(mDb, clazz, wheres, values); - } - - /** - * 插入数据 - */ - synchronized void insertData(DbEntity dbEntity) { - checkDb(); - SqlHelper.insertData(mDb, dbEntity); - } - - /** - * 查找某张表是否存在 - */ - synchronized boolean tableExists(Class clazz) { - checkDb(); - return SqlHelper.tableExists(mDb, clazz); - } - - synchronized void createTable(Class clazz, String tableName) { - checkDb(); - SqlHelper.createTable(mDb, clazz, tableName); - } - - private void checkDb() { - if (mDb == null || !mDb.isOpen()) { - mDb = mHelper.getReadableDatabase(); - } - } - - /** - * 创建表 - */ - private synchronized void createTable(Class clazz) { - createTable(clazz, null); - } - - /** - * 关闭数据库 - */ - private synchronized void close() { - if (mDb != null) { - mDb.close(); - } - } - - /** - * 获取所在行Id - */ - synchronized int[] getRowId(Class clazz) { - checkDb(); - Cursor cursor = mDb.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(); - return ids; - } - - /** - * 获取行Id - */ - synchronized int getRowId(Class clazz, Object[] wheres, Object[] values) { - checkDb(); - if (wheres.length <= 0 || values.length <= 0) { - ALog.e(TAG, "请输入删除条件"); - return -1; - } else if (wheres.length != values.length) { - ALog.e(TAG, "groupName 和 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++; - } - SqlHelper.print(ROW_ID, sb.toString()); - Cursor c = mDb.rawQuery(sb.toString(), null); - int id = c.getColumnIndex("rowid"); - c.close(); - close(); - return id; - } -} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java new file mode 100644 index 00000000..c23fabf6 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.text.TextUtils; +import com.arialyy.aria.orm.annotation.Default; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Primary; +import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.CommonUtil; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by laoyuyu on 2018/3/22. + * 通用委托,创建表,检查字段 + */ +class DelegateCommon extends AbsDelegate { + private DelegateCommon() { + } + + /** + * 删除指定的表 + */ + void dropTable(SQLiteDatabase db, String tableName) { + db = checkDb(db); + String deleteSQL = "DROP TABLE IF EXISTS ".concat(tableName); + print(DROP_TABLE, deleteSQL); + //db.beginTransaction(); + db.execSQL(deleteSQL); + //db.setTransactionSuccessful(); + //db.endTransaction(); + } + + /** + * 清空表数据 + */ + void clean(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + String tableName = CommonUtil.getClassName(clazz); + if (tableExists(db, clazz)) { + String sql = "DELETE FROM " + tableName; + db.execSQL(sql); + } + } + + /** + * 查找表是否存在 + * + * @param clazz 数据库实体 + * @return true,该数据库实体对应的表存在;false,不存在 + */ + boolean tableExists(SQLiteDatabase db, Class clazz) { + return tableExists(db, CommonUtil.getClassName(clazz)); + } + + private boolean tableExists(SQLiteDatabase db, String tableName) { + db = checkDb(db); + Cursor cursor = null; + try { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT COUNT(*) AS c FROM sqlite_master WHERE type='table' AND name='"); + sb.append(tableName); + sb.append("'"); + print(TABLE_EXISTS, sb.toString()); + cursor = db.rawQuery(sb.toString(), null); + if (cursor != null && cursor.moveToNext()) { + int count = cursor.getInt(0); + if (count > 0) { + return true; + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + closeCursor(cursor); + close(db); + } + return false; + } + + /** + * 检查某个字段的值是否存在 + * + * @param expression 字段和值"url=xxx" + * @return {@code true}该字段的对应的value已存在 + */ + boolean checkDataExist(SQLiteDatabase db, Class clazz, String... expression) { + db = checkDb(db); + CheckUtil.checkSqlExpression(expression); + String sql = + "SELECT rowid, * FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; + sql = sql.replace("?", "%s"); + Object[] params = new String[expression.length - 1]; + for (int i = 0, len = params.length; i < len; i++) { + params[i] = "'" + 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; + } + + /** + * 创建表 + * + * @param clazz 数据库实体 + */ + void createTable(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + List fields = CommonUtil.getAllFields(clazz); + if (fields != null && fields.size() > 0) { + //外键Map,在Sqlite3中foreign修饰的字段必须放在最后 + final List foreignArray = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE TABLE ") + .append(CommonUtil.getClassName(clazz)) + .append(" ("); + for (Field field : fields) { + field.setAccessible(true); + if (SqlUtil.isIgnore(field)) { + continue; + } + Class type = field.getType(); + sb.append(field.getName()); + if (type == String.class || type.isEnum()) { + sb.append(" VARCHAR"); + } else if (type == int.class || type == Integer.class) { + sb.append(" INTEGER"); + } else if (type == float.class || type == Float.class) { + sb.append(" FLOAT"); + } else if (type == double.class || type == Double.class) { + sb.append(" DOUBLE"); + } else if (type == long.class || type == Long.class) { + sb.append(" BIGINT"); + } else if (type == boolean.class || type == Boolean.class) { + sb.append(" BOOLEAN"); + } else if (type == java.util.Date.class || type == java.sql.Date.class) { + sb.append(" DATA"); + } else if (type == byte.class || type == Byte.class) { + sb.append(" BLOB"); + } else if (type == Map.class || type == List.class) { + sb.append(" TEXT"); + } else { + continue; + } + if (SqlUtil.isPrimary(field)) { + Primary pk = field.getAnnotation(Primary.class); + sb.append(" PRIMARY KEY"); + if (pk.autoincrement() && (type == int.class || type == Integer.class)) { + sb.append(" AUTOINCREMENT"); + } + } + + if (SqlUtil.isForeign(field)) { + foreignArray.add(field); + } + + if (SqlUtil.isNoNull(field)) { + sb.append(" NOT NULL"); + } + + if (SqlUtil.isDefault(field)) { + Default d = field.getAnnotation(Default.class); + if (!TextUtils.isEmpty(d.value())) { + sb.append(" DEFAULT ").append("'").append(d.value()).append("'"); + } + } + + sb.append(","); + } + + for (Field field : foreignArray) { + Foreign foreign = field.getAnnotation(Foreign.class); + sb.append("FOREIGN KEY (") + .append(field.getName()) + .append(") REFERENCES ") + .append(CommonUtil.getClassName(foreign.parent())) + .append("(") + .append(foreign.column()) + .append(")"); + ActionPolicy update = foreign.onUpdate(); + ActionPolicy delete = foreign.onDelete(); + if (update != ActionPolicy.NO_ACTION) { + sb.append(" ON UPDATE ").append(update.function); + } + + if (delete != ActionPolicy.NO_ACTION) { + sb.append(" ON DELETE ").append(update.function); + } + sb.append(","); + } + + String str = sb.toString(); + str = str.substring(0, str.length() - 1) + ");"; + 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 new file mode 100644 index 00000000..2b76ec6b --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java @@ -0,0 +1,512 @@ +/* + * 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.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * 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 Wrapper}注解,将返回null + * 如果实体中没有{@link One}或{@link Many}注解,将返回null + * 如果实体中有多个{@link One}或{@link Many}注解,将返回nul + */ + List findRelationData(SQLiteDatabase db, Class clazz, + String... expression) { + db = checkDb(db); + + if (SqlUtil.isWrapper(clazz)) { + StringBuilder sb = new StringBuilder(); + 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; + } + 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; + } + 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] = "'" + convertValue(expression[i + 1]) + "'"; + } + sql = String.format(sql, params); + } else { + sql = sb.toString(); + } + 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.w(TAG, "查询数据失败,实体类没有使用@Wrapper 注解"); + return null; + } + return null; + } + + /** + * 创建关联查询的数据 + * + * @param pColumn 父表的所有字段 + * @param cColumn 字表的所有字段 + * @param pColumnAlias 关联查询父表别名 + * @param cColumnAlias 关联查询子表别名 + */ + private 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 WeakHashMap<>(); // 所有父表元素,key为父表主键的值 + Map> tempChild = new WeakHashMap<>(); // 所有的字表元素,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 = + "SELECT rowid, * FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; + sql = sql.replace("?", "%s"); + Object[] params = new String[expression.length - 1]; + for (int i = 0, len = params.length; i < len; i++) { + params[i] = "'" + convertValue(expression[i + 1]) + "'"; + } + sql = String.format(sql, params); + print(FIND_DATA, sql); + Cursor cursor = db.rawQuery(sql, null); + List data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null; + closeCursor(cursor); + close(db); + return data; + } + + /** + * 查找表的所有数据 + */ + List findAllData(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + StringBuilder sb = new StringBuilder(); + sb.append("SELECT rowid, * FROM ").append(CommonUtil.getClassName(clazz)); + print(FIND_ALL_DATA, sb.toString()); + Cursor cursor = db.rawQuery(sb.toString(), null); + List data = cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null; + closeCursor(cursor); + close(db); + return data; + } + + /** + * 根据数据游标创建一个具体的对象 + */ + private 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 (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(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(temp)); + } + } else if (type == List.class) { + String value = cursor.getString(column); + if (!TextUtils.isEmpty(value)) { + field.set(entity, SqlUtil.str2List(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, "groupName 和 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) { + db = checkDb(db); + String sql = "SELECT rowid FROM " + CommonUtil.getClassName(clazz) + " WHERE rowid=" + rowId; + print(ROW_ID, sql); + 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/DelegateManager.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateManager.java new file mode 100644 index 00000000..802a8bc6 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateManager.java @@ -0,0 +1,69 @@ +/* + * 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.util.SparseArray; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + * Created by laoyuyu on 2018/3/22. + * Delegate管理器 + */ +class DelegateManager { + private final String TAG = "ModuleFactory"; + + private SparseArray mDelegates = new SparseArray<>(); + private static volatile DelegateManager INSTANCE = null; + + private DelegateManager() { + + } + + static DelegateManager getInstance() { + if (INSTANCE == null) { + synchronized (DelegateManager.class) { + INSTANCE = new DelegateManager(); + } + } + return INSTANCE; + } + + /** + * 获取Module + */ + M getDelegate(Class clazz) { + M delegate = (M) mDelegates.get(clazz.hashCode()); + try { + if (delegate == null) { + Constructor c = clazz.getDeclaredConstructor(); + c.setAccessible(true); + delegate = (M) c.newInstance(); + mDelegates.put(clazz.hashCode(), delegate); + return delegate; + } + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + return delegate; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java new file mode 100644 index 00000000..4a9ca900 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java @@ -0,0 +1,182 @@ +/* + * 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.ContentValues; +import android.database.sqlite.SQLiteDatabase; +import android.text.TextUtils; +import android.util.Log; +import com.arialyy.aria.orm.annotation.Primary; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.CommonUtil; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +/** + * Created by laoyuyu on 2018/3/22. + * 增加数据、更新数据 + */ +class DelegateUpdate extends AbsDelegate { + private DelegateUpdate() { + } + + /** + * 删除某条数据 + */ + void delData(SQLiteDatabase db, Class clazz, String... expression) { + db = checkDb(db); + CheckUtil.checkSqlExpression(expression); + + String sql = "DELETE FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; + sql = sql.replace("?", "%s"); + Object[] params = new String[expression.length - 1]; + for (int i = 0, len = params.length; i < len; i++) { + params[i] = "'" + expression[i + 1] + "'"; + } + sql = String.format(sql, params); + print(DEL_DATA, sql); + db.execSQL(sql); + close(db); + } + + /** + * 修改某行数据 + */ + void modifyData(SQLiteDatabase db, DbEntity dbEntity) { + db = checkDb(db); + Class clazz = dbEntity.getClass(); + List fields = CommonUtil.getAllFields(clazz); + //DbEntity cacheEntity = mDataCache.get(getCacheKey(dbEntity)); + if (fields != null && fields.size() > 0) { + ContentValues values = new ContentValues(); + try { + for (Field field : fields) { + field.setAccessible(true); + if (isIgnore(dbEntity, field)) { + continue; + } + //if (cacheEntity != null + // && field.get(dbEntity).equals(field.get(cacheEntity)) + // && !field.getName().equals("state")) { //在LruCache中 state字段总是不能重新赋值... + // Log.d(TAG, field.get(dbEntity) + ""); + // Log.d(TAG, field.get(cacheEntity) + ""); + // + // continue; + //} + String value; + Type type = field.getType(); + if (type == Map.class && checkMap(field)) { + value = SqlUtil.map2Str((Map) field.get(dbEntity)); + } else if (type == List.class && checkList(field)) { + value = SqlUtil.list2Str(dbEntity, field); + } else { + Object obj = field.get(dbEntity); + value = obj == null ? "" : convertValue(obj.toString()); + } + values.put(field.getName(), value); + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + if (values.size() > 0) { + db.update(CommonUtil.getClassName(dbEntity), values, "rowid=?", + new String[] { String.valueOf(dbEntity.rowID) }); + } else { + ALog.d(TAG, "没有数据更新"); + } + } + //mDataCache.put(getCacheKey(dbEntity), dbEntity); + close(db); + } + + /** + * 插入数据 + */ + void insertData(SQLiteDatabase db, DbEntity dbEntity) { + db = checkDb(db); + Class clazz = dbEntity.getClass(); + List fields = CommonUtil.getAllFields(clazz); + if (fields != null && fields.size() > 0) { + ContentValues values = new ContentValues(); + try { + for (Field field : fields) { + field.setAccessible(true); + if (isIgnore(dbEntity, field)) { + continue; + } + String value = null; + Type type = field.getType(); + if (type == Map.class && checkMap(field)) { + value = SqlUtil.map2Str((Map) field.get(dbEntity)); + } else if (type == List.class && checkList(field)) { + value = SqlUtil.list2Str(dbEntity, field); + } else { + Object obj = field.get(dbEntity); + if (obj != null) { + value = convertValue(field.get(dbEntity).toString()); + } + } + values.put(field.getName(), value); + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + //print(INSERT_DATA, ); + dbEntity.rowID = db.insert(CommonUtil.getClassName(dbEntity), null, values); + } + close(db); + } + + /** + * {@code true}自动增长的主键和需要忽略的字段 + */ + private boolean isIgnore(Object obj, Field field) throws IllegalAccessException { + if (SqlUtil.isIgnore(field)) { + return true; + } + Object value = field.get(obj); + if (value == null) { // 忽略为空的字段 + return true; + } + if (value instanceof String) { + if (TextUtils.isEmpty(String.valueOf(value))) { + return true; + } + } + if (value instanceof List) { + if (((List) value).size() == 0) { + return true; + } + } + if (value instanceof Map) { + if (((Map) value).size() == 0) { + return true; + } + } + + if (SqlUtil.isPrimary(field)) { //忽略自动增长的主键 + Primary p = field.getAnnotation(Primary.class); + if (p.autoincrement()) { + return true; + } + } + + return false; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java b/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java new file mode 100644 index 00000000..6a817885 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java @@ -0,0 +1,155 @@ +/* + * 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.sqlite.SQLiteDatabase; +import java.util.List; + +/** + * Created by lyy on 2015/2/11. + * 数据库操作工具 + */ +public class DelegateWrapper { + private static final String TAG = "DelegateWrapper"; + private volatile static DelegateWrapper INSTANCE = null; + + private SQLiteDatabase mDb; + private DelegateManager mDManager; + + private DelegateWrapper() { + + } + + private DelegateWrapper(Context context) { + SqlHelper helper = SqlHelper.init(context.getApplicationContext()); + mDb = helper.getWritableDatabase(); + mDManager = DelegateManager.getInstance(); + } + + public static void init(Context context) { + synchronized (DelegateWrapper.class) { + if (INSTANCE == null) { + INSTANCE = new DelegateWrapper(context); + } + } + } + + static DelegateWrapper getInstance() { + if (INSTANCE == null) { + throw new NullPointerException("请在Application中调用init进行数据库工具注册注册"); + } + return INSTANCE; + } + + /** + * 查询关联表数据 + * + * @param expression 查询条件 + */ + List findRelationData(Class clazz, String... expression) { + return mDManager.getDelegate(DelegateFind.class).findRelationData(mDb, clazz, expression); + } + + /** + * 检查某个字段的值是否存在 + * + * @param expression 字段和值"url=xxx" + * @return {@code true}该字段的对应的value已存在 + */ + boolean checkDataExist(Class clazz, String... expression) { + return mDManager.getDelegate(DelegateCommon.class) + .checkDataExist(mDb, clazz, expression); + } + + /** + * 清空表数据 + */ + void clean(Class clazz) { + mDManager.getDelegate(DelegateCommon.class).clean(mDb, clazz); + } + + /** + * 执行sql语句 + */ + void exeSql(String sql) { + mDb.execSQL(sql); + } + + /** + * 删除某条数据 + */ + void delData(Class clazz, String... expression) { + mDManager.getDelegate(DelegateUpdate.class).delData(mDb, clazz, expression); + } + + /** + * 修改某行数据 + */ + void modifyData(DbEntity dbEntity) { + mDManager.getDelegate(DelegateUpdate.class).modifyData(mDb, dbEntity); + } + + /** + * 遍历所有数据 + */ + List findAllData(Class clazz) { + return mDManager.getDelegate(DelegateFind.class).findAllData(mDb, clazz); + } + + /** + * 条件查寻数据 + */ + List findData(Class clazz, String... expression) { + return mDManager.getDelegate(DelegateFind.class).findData(mDb, clazz, expression); + } + + /** + * 通过rowId判断数据是否存在 + */ + boolean isExist(Class clazz, long rowId) { + return mDManager.getDelegate(DelegateFind.class).itemExist(mDb, clazz, rowId); + } + + /** + * 插入数据 + */ + void insertData(DbEntity dbEntity) { + mDManager.getDelegate(DelegateUpdate.class).insertData(mDb, dbEntity); + } + + /** + * 查找某张表是否存在 + */ + boolean tableExists(Class clazz) { + return mDManager.getDelegate(DelegateCommon.class).tableExists(mDb, clazz); + } + + /** + * 获取所在行Id + */ + int[] getRowId(Class clazz) { + return mDManager.getDelegate(DelegateFind.class).getRowId(mDb, clazz); + } + + /** + * 获取行Id + */ + int getRowId(Class clazz, Object[] wheres, Object[] values) { + return mDManager.getDelegate(DelegateFind.class).getRowId(mDb, clazz, wheres, values); + } +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/NormalList.java b/Aria/src/main/java/com/arialyy/aria/orm/NormalList.java deleted file mode 100644 index 2d2ba512..00000000 --- a/Aria/src/main/java/com/arialyy/aria/orm/NormalList.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.orm; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Created by AriaL on 2017/7/4. - * 基本类型的List,只能用于常见的数据类型,如果是一对多的复杂数据结构,需要使用{@link OneToMany} - */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface NormalList { - /** - * 数据类型 - */ - Class clazz(); -} 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 72be07c5..fa633970 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -20,19 +20,17 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.support.annotation.NonNull; -import android.support.v4.util.LruCache; import android.text.TextUtils; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.upload.UploadTaskEntity; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CheckUtil; -import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.net.URLDecoder; -import java.net.URLEncoder; import java.util.ArrayList; -import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -43,31 +41,29 @@ import java.util.Set; */ final class SqlHelper extends SQLiteOpenHelper { private static final String TAG = "SqlHelper"; - private static final int CREATE_TABLE = 0; - private static final int TABLE_EXISTS = 1; - private static final int INSERT_DATA = 2; - private static final int MODIFY_DATA = 3; - private static final int FIND_DATA = 4; - private static final int FIND_ALL_DATA = 5; - private static final int DEL_DATA = 6; + static volatile SqlHelper INSTANCE = null; - private static volatile SqlHelper INSTANCE = null; - private static LruCache mDataCache = new LruCache<>(1024); - //private static Map mDataCache = new ConcurrentHashMap<>(); + private DelegateCommon mDelegate; static SqlHelper init(Context context) { if (INSTANCE == null) { - synchronized (AriaManager.LOCK) { - INSTANCE = new SqlHelper(context.getApplicationContext()); + synchronized (SqlHelper.class) { + DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class); + INSTANCE = new SqlHelper(context.getApplicationContext(), delegate); SQLiteDatabase db = INSTANCE.getWritableDatabase(); - db = checkDb(db); + 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 = null; clazz = DBConfig.mapping.get(tableName); - if (!tableExists(db, clazz)) { - createTable(db, clazz, null); + if (!delegate.tableExists(db, clazz)) { + delegate.createTable(db, clazz); } } } @@ -75,8 +71,10 @@ final class SqlHelper extends SQLiteOpenHelper { return INSTANCE; } - private SqlHelper(Context context) { - super(context, DBConfig.DB_NAME, null, DBConfig.VERSION); + 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) { @@ -85,7 +83,11 @@ final class SqlHelper extends SQLiteOpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion < newVersion) { - handleDbUpdate(db); + if (oldVersion < 31) { + handle314AriaUpdate(db); + } else { + handleDbUpdate(db); + } } } @@ -110,635 +112,125 @@ final class SqlHelper extends SQLiteOpenHelper { Set tables = DBConfig.mapping.keySet(); for (String tableName : tables) { Class clazz = DBConfig.mapping.get(tableName); - if (tableExists(db, clazz)) { + if (mDelegate.tableExists(db, clazz)) { String countColumnSql = "SELECT rowid FROM " + tableName; Cursor cursor = db.rawQuery(countColumnSql, null); int dbColumnNum = cursor.getColumnCount(); - int newEntityColumnNum = getEntityAttr(clazz); + List fields = SqlUtil.getAllNotIgnoreField(clazz); + int newEntityColumnNum = (fields == null || fields.isEmpty()) ? 0 : fields.size(); if (dbColumnNum != newEntityColumnNum) { - back(db, clazz); - } - } - } - } - - /** - * 备份 - */ - private void back(SQLiteDatabase db, Class clazz) { - db = checkDb(db); - String oldTableName = CommonUtil.getClassName(clazz); - //备份数据 - List list = findAllData(db, clazz); - //修改原来表名字 - String alertSql = "alter table " + oldTableName + " rename to " + oldTableName + "_temp"; - db.beginTransaction(); - db.execSQL(alertSql); - //创建一个原来新表 - createTable(db, clazz, null); - if (list != null && list.size() > 0) { - for (DbEntity entity : list) { - insertData(db, entity); - } - } - //删除原来的表 - String deleteSQL = "drop table IF EXISTS " + oldTableName + "_temp"; - db.execSQL(deleteSQL); - db.setTransactionSuccessful(); - db.endTransaction(); - close(db); - } - - /** - * 获取实体的字段数 - */ - private int getEntityAttr(Class clazz) { - int count = 1; - List fields = CommonUtil.getAllFields(clazz); - if (fields != null && fields.size() > 0) { - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - count++; - } - } - return count; - } - - ///** - // * 关键字模糊检索全文 - // * - // * @param column 需要查找的列 - // * @param mathSql 关键字语法,exsimple “white OR green”、“blue AND red”、“white NOT green” - // */ - //public static List searchData(SQLiteDatabase db, Class clazz, - // String column, String mathSql) { - // String sql = "SELECT * FROM " - // + CommonUtil.getClassName(clazz) - // + " WHERE " - // + column - // + " MATCH '" - // + mathSql - // + "'"; - // - // Cursor cursor = db.rawQuery(sql, null); - // List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; - // closeCursor(cursor); - // return data; - //} - - /** - * 检查某个字段的值是否存在 - * - * @param expression 字段和值"url=xxx" - * @return {@code true}该字段的对应的value已存在 - */ - static synchronized boolean checkDataExist(SQLiteDatabase db, Class clazz, String... expression) { - db = checkDb(db); - CheckUtil.checkSqlExpression(expression); - String sql = - "SELECT rowid, * FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; - sql = sql.replace("?", "%s"); - Object[] params = new String[expression.length - 1]; - for (int i = 0, len = params.length; i < len; i++) { - params[i] = "'" + 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; - } - - /** - * 条件查寻数据 - */ - static synchronized List findData(SQLiteDatabase db, Class clazz, - String... expression) { - db = checkDb(db); - CheckUtil.checkSqlExpression(expression); - String sql = - "SELECT rowid, * FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; - sql = sql.replace("?", "%s"); - Object[] params = new String[expression.length - 1]; - for (int i = 0, len = params.length; i < len; i++) { - params[i] = "'" + checkValue(expression[i + 1]) + "'"; - } - sql = String.format(sql, params); - print(FIND_DATA, sql); - Cursor cursor = db.rawQuery(sql, null); - List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; - closeCursor(cursor); - close(db); - return data; - } - - /** - * 条件查寻数据 - */ - static synchronized List findData(SQLiteDatabase db, String tableName, - String... expression) { - Class clazz = null; - try { - clazz = (Class) Class.forName(tableName); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - return findData(db, clazz, expression); - } - - /** - * 条件查寻数据 - */ - @Deprecated static synchronized List findData(SQLiteDatabase db, - Class clazz, @NonNull String[] wheres, @NonNull String[] values) { - db = checkDb(db); - if (wheres.length <= 0 || values.length <= 0) { - ALog.e(TAG, "请输入查询条件"); - return null; - } else if (wheres.length != values.length) { - ALog.e(TAG, "groupName 和 vaule 长度不相等"); - return null; - } - 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(checkValue(values[i])).append("'"); - sb.append(i >= wheres.length - 1 ? "" : " AND "); - i++; - } - print(FIND_DATA, sb.toString()); - Cursor cursor = db.rawQuery(sb.toString(), null); - List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; - closeCursor(cursor); - close(db); - return data; - } - - private static String checkValue(String value) { - if (value.contains("'")) { - return URLEncoder.encode(value); - } - return value; - } - - /** - * 查找表的所有数据 - */ - static synchronized List findAllData(SQLiteDatabase db, Class clazz) { - db = checkDb(db); - StringBuilder sb = new StringBuilder(); - sb.append("SELECT rowid, * FROM ").append(CommonUtil.getClassName(clazz)); - print(FIND_ALL_DATA, sb.toString()); - Cursor cursor = db.rawQuery(sb.toString(), null); - List data = cursor.getCount() > 0 ? newInstanceEntity(db, clazz, cursor) : null; - closeCursor(cursor); - close(db); - return data; - } - - /** - * 删除某条数据 - */ - static synchronized void delData(SQLiteDatabase db, Class clazz, - String... expression) { - db = checkDb(db); - CheckUtil.checkSqlExpression(expression); - - //List fields = CommonUtil.getAllFields(clazz); - //for (Field field : fields) { - // if (SqlUtil.isOneToOne(field)) { - // OneToOne oto = field.getAnnotation(OneToOne.class); - // delData(db, oto.table(), oto.key() + "=?", ); - // } else if (SqlUtil.isOneToMany(field)) { - // OneToMany otm = field.getAnnotation(OneToMany.class); - // delData(db, otm.table(), otm.key() + "=?", otm.key()); - // } - //} - - String sql = "DELETE FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; - sql = sql.replace("?", "%s"); - Object[] params = new String[expression.length - 1]; - for (int i = 0, len = params.length; i < len; i++) { - params[i] = "'" + expression[i + 1] + "'"; - } - sql = String.format(sql, params); - SqlHelper.print(DEL_DATA, sql); - db.execSQL(sql); - close(db); - } - - /** - * 修改某行数据 - */ - static synchronized void modifyData(SQLiteDatabase db, DbEntity dbEntity) { - db = checkDb(db); - Class clazz = dbEntity.getClass(); - List fields = CommonUtil.getAllFields(clazz); - DbEntity cacheEntity = mDataCache.get(getCacheKey(dbEntity)); - if (fields != null && fields.size() > 0) { - StringBuilder sql = new StringBuilder(); - StringBuilder prams = new StringBuilder(); - sql.append("UPDATE ").append(CommonUtil.getClassName(dbEntity)).append(" SET "); - int i = 0; - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - try { - if (cacheEntity != null - && field.get(dbEntity) == field.get(cacheEntity) - && !field.getName().equals("state")) { //在LruCache中 state字段总是不能重新赋值... - //if (dbEntity instanceof DownloadEntity && field.getName().equals("state")) { - // Log.i(TAG, "cacheState => " - // + ((DownloadEntity) cacheEntity).getState() - // + ", newState => " - // + ((DownloadEntity) dbEntity).getState()); - //} - continue; - } - - //sb.append(i > 0 ? ", " : ""); - //sb.append(field.getName()).append("='"); - String value; - prams.append(i > 0 ? ", " : ""); - prams.append(field.getName()).append("='"); - Type type = field.getType(); - if (type == Map.class) { - value = SqlUtil.map2Str((Map) field.get(dbEntity)); - } else if (type == List.class) { - if (SqlUtil.isOneToMany(field)) { - value = SqlUtil.getOneToManyElementParams(field); - } else { - value = SqlUtil.list2Str(dbEntity, field); + db = mDelegate.checkDb(db); + //备份数据 + List list = + DelegateManager.getInstance().getDelegate(DelegateFind.class).findAllData(db, clazz); + //修改表名为中介表名 + String alertSql = "ALTER TABLE " + tableName + " RENAME TO " + tableName + "_temp"; + //db.beginTransaction(); + db.execSQL(alertSql); + + //创建一个原本的表 + mDelegate.createTable(db, clazz); + //传入原来的数据 + if (list != null && list.size() > 0) { + DelegateUpdate update = DelegateManager.getInstance().getDelegate(DelegateUpdate.class); + for (DbEntity entity : list) { + update.insertData(db, entity); } - } else if (SqlUtil.isOneToOne(field)) { - value = SqlUtil.getOneToOneParams(field); - } else { - Object obj = field.get(dbEntity); - value = obj == null ? "" : checkValue(obj.toString()); } - - //sb.append(value == null ? "" : value); - //sb.append("'"); - prams.append(TextUtils.isEmpty(value) ? "" : value); - prams.append("'"); - } catch (IllegalAccessException e) { - e.printStackTrace(); + //删除中介表 + mDelegate.dropTable(db, tableName + "_temp"); } - i++; - } - if (!TextUtils.isEmpty(prams.toString())) { - sql.append(prams.toString()); - sql.append(" where rowid=").append(dbEntity.rowID); - print(MODIFY_DATA, sql.toString()); - db.execSQL(sql.toString()); } } - mDataCache.put(getCacheKey(dbEntity), dbEntity); - close(db); - } - - private static String getCacheKey(DbEntity dbEntity) { - return dbEntity.getClass().getName() + "_" + dbEntity.rowID; + //db.setTransactionSuccessful(); + //db.endTransaction(); + mDelegate.close(db); } /** - * 插入数据 + * 处理3.4版本之前数据库迁移,主要是修改子表外键字段对应的值 */ - static synchronized void insertData(SQLiteDatabase db, DbEntity dbEntity) { - db = checkDb(db); - Class clazz = dbEntity.getClass(); - List fields = CommonUtil.getAllFields(clazz); - if (fields != null && fields.size() > 0) { - StringBuilder sb = new StringBuilder(); - sb.append("INSERT INTO ").append(CommonUtil.getClassName(dbEntity)).append("("); - int i = 0; - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - sb.append(i > 0 ? ", " : ""); - sb.append(field.getName()); - i++; - } - sb.append(") VALUES ("); - i = 0; - try { - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - sb.append(i > 0 ? ", " : ""); - sb.append("'"); - Type type = field.getType(); - if (type == Map.class) { - sb.append(SqlUtil.map2Str((Map) field.get(dbEntity))); - } else if (type == List.class) { - if (SqlUtil.isOneToMany(field)) { - sb.append(SqlUtil.getOneToManyElementParams(field)); - } else { - sb.append(SqlUtil.list2Str(dbEntity, field)); - } - } else if (SqlUtil.isOneToOne(field)) { - sb.append(SqlUtil.getOneToOneParams(field)); - } else { - sb.append(checkValue(field.get(dbEntity).toString())); - } - sb.append("'"); - i++; - } - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - sb.append(")"); - print(INSERT_DATA, sb.toString()); - db.execSQL(sb.toString()); - } - close(db); - } - - /** - * 查找表是否存在 - * - * @param clazz 数据库实体 - * @return true,该数据库实体对应的表存在;false,不存在 - */ - static synchronized boolean tableExists(SQLiteDatabase db, Class clazz) { - return tableExists(db, CommonUtil.getClassName(clazz)); - } - - static synchronized boolean tableExists(SQLiteDatabase db, String tableName) { - db = checkDb(db); - Cursor cursor = null; - try { - StringBuilder sb = new StringBuilder(); - sb.append("SELECT COUNT(*) AS c FROM sqlite_master WHERE type='table' AND name='"); - sb.append(tableName); - sb.append("'"); - print(TABLE_EXISTS, sb.toString()); - cursor = db.rawQuery(sb.toString(), null); - if (cursor != null && cursor.moveToNext()) { - int count = cursor.getInt(0); - if (count > 0) { - return true; - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - closeCursor(cursor); - close(db); - } - return false; - } - - /** - * 创建表 - * - * @param clazz 数据库实体 - * @param tableName 数据库实体的类名 - */ - static synchronized void createTable(SQLiteDatabase db, Class clazz, String tableName) { - db = checkDb(db); - createFTSTable(db); - List fields = CommonUtil.getAllFields(clazz); - if (fields != null && fields.size() > 0) { - //外键Map,在Sqlite3中foreign修饰的字段必须放在最后 - final List foreignArray = new ArrayList<>(); - StringBuilder sb = new StringBuilder(); - //sb.append("create VIRTUAL table ") - sb.append("create table ") - .append(TextUtils.isEmpty(tableName) ? CommonUtil.getClassName(clazz) : tableName) - //.append(" USING fts4("); - .append(" ("); - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - Class type = field.getType(); - sb.append(field.getName()); - if (type == String.class || SqlUtil.isOneToOne(field) || type.isEnum()) { - sb.append(" varchar"); - } else if (type == int.class || type == Integer.class) { - sb.append(" interger"); - } else if (type == float.class || type == Float.class) { - sb.append(" float"); - } else if (type == double.class || type == Double.class) { - sb.append(" double"); - } else if (type == long.class || type == Long.class) { - sb.append(" bigint"); - } else if (type == boolean.class || type == Boolean.class) { - sb.append(" boolean"); - } else if (type == java.util.Date.class || type == java.sql.Date.class) { - sb.append(" data"); - } else if (type == byte.class || type == Byte.class) { - sb.append(" blob"); - } else if (type == Map.class || type == List.class) { - sb.append(" text"); - } else { - continue; - } - if (SqlUtil.isPrimary(field)) { - //sb.append(" PRIMARY KEY"); - - } - if (SqlUtil.isForeign(field)) { - //foreignArray.add(field); - } - - if (SqlUtil.isNoNull(field)) { - sb.append(" NOT NULL"); - } - sb.append(","); - } + private void handle314AriaUpdate(SQLiteDatabase db) { + Set tables = DBConfig.mapping.keySet(); + Map> map = new HashMap<>(); + for (String tableName : tables) { + Class clazz = DBConfig.mapping.get(tableName); - for (Field field : foreignArray) { - Foreign foreign = field.getAnnotation(Foreign.class); - sb.append("FOREIGN KEY (") - .append(field.getName()) - .append(") REFERENCES ") - .append(CommonUtil.getClassName(foreign.table())) - .append("(") - .append(foreign.column()) - .append("),"); + String pColumn = SqlUtil.getPrimaryName(clazz); + if (!TextUtils.isEmpty(pColumn)) { + //删除所有主键为null的数据 + String nullSql = + "DELETE FROM " + tableName + " WHERE " + pColumn + " = '' OR " + pColumn + " IS NULL"; + ALog.d(TAG, nullSql); + db.execSQL(nullSql); + + //删除所有主键重复的数据 + String repeatSql = "DELETE FROM " + + tableName + + " WHERE " + + pColumn + + " in (SELECT " + + pColumn + + " FROM " + + tableName + + " GROUP BY " + pColumn + " having count(" + pColumn + ") > 1)"; + + ALog.d(TAG, repeatSql); + db.execSQL(repeatSql); } - String str = sb.toString(); - str = str.substring(0, str.length() - 1) + ");"; - print(CREATE_TABLE, str); - db.execSQL(str); - } - close(db); - } + //备份数据 + List list = + DelegateManager.getInstance().getDelegate(DelegateFind.class).findAllData(db, clazz); - /** - * 创建分词表 - */ - private static void createFTSTable(SQLiteDatabase db) { - String tableName = "ariaFts"; - String sql = "CREATE VIRTUAL TABLE " + tableName + " USING fts4(tokenize= porter)"; - if (!tableExists(db, tableName)) { - db.execSQL(sql); - } - } + map.put(tableName, list); - /** - * 打印数据库日志 - * - * @param type {@link DbUtil} - */ - static void print(int type, String sql) { - if (true) { - 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 FIND_ALL_DATA: - str = "遍历整个数据库 >>>> "; - break; - } - ALog.v(TAG, str + sql); - } + //修改表名为中介表名 + String alertSql = "ALTER TABLE " + tableName + " RENAME TO " + tableName + "_temp"; + db.execSQL(alertSql); - /** - * 根据数据游标创建一个具体的对象 - */ - private static synchronized List newInstanceEntity(SQLiteDatabase db, - 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(); - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.ignoreField(field)) { - continue; - } - Class type = field.getType(); - int column = cursor.getColumnIndex(field.getName()); - if (column == -1) continue; - if (type == String.class) { - field.set(entity, URLDecoder.decode(cursor.getString(column))); - } 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) { - field.setBoolean(entity, !cursor.getString(column).equalsIgnoreCase("false")); - } else if (type == java.util.Date.class || type == java.sql.Date.class) { - field.set(entity, new Date(cursor.getString(column))); - } else if (type == byte[].class) { - field.set(entity, cursor.getBlob(column)); - } else if (type == Map.class) { - field.set(entity, SqlUtil.str2Map(cursor.getString(column))); - } else if (type == List.class) { - String value = cursor.getString(column); - if (SqlUtil.isOneToMany(field)) { - //主键字段 - String primaryKey = SqlUtil.getPrimaryName(clazz); - if (TextUtils.isEmpty(primaryKey)) { - throw new IllegalArgumentException("List中的元素对象必须需要@Primary注解的字段"); - } - //list字段保存的数据 - int kc = cursor.getColumnIndex(primaryKey); - String primaryData = cursor.getString(kc); - if (TextUtils.isEmpty(primaryData)) continue; - List list = findForeignData(db, primaryData, value); - if (list == null) continue; - field.set(entity, findForeignData(db, primaryData, value)); - } else { - field.set(entity, SqlUtil.str2List(value, field)); - } - } else if (SqlUtil.isOneToOne(field)) { - String primaryKey = SqlUtil.getPrimaryName(clazz); - if (TextUtils.isEmpty(primaryKey)) { - throw new IllegalArgumentException("@OneToOne的注解对象必须需要@Primary注解的字段"); + //创建一个原本的表 + mDelegate.createTable(db, clazz); + //插入数据 + if (list != null && list.size() > 0) { + DelegateUpdate update = DelegateManager.getInstance().getDelegate(DelegateUpdate.class); + try { + for (DbEntity entity : list) { + if (entity instanceof DownloadTaskEntity && ((DownloadTaskEntity) entity).isGroupTask()) { + if (TextUtils.isEmpty(((DownloadTaskEntity) entity).getKey())) { + ALog.w(TAG, "DownloadTaskEntity的key为空,将忽略该条数据"); + continue; } - int kc = cursor.getColumnIndex(primaryKey); - String params = cursor.getString(column); - String primaryData = cursor.getString(kc); - if (TextUtils.isEmpty(primaryData) || primaryData.equalsIgnoreCase("null")) continue; - List list = findForeignData(db, primaryData, params); - if (list != null && list.size() > 0) { - field.set(entity, list.get(0)); + if (TextUtils.isEmpty(((DownloadTaskEntity) entity).getUrl())) { + List temp = map.get("DownloadEntity"); + boolean isRefresh = false; + for (DbEntity dbEntity : temp) { + if (((DownloadEntity) dbEntity).getDownloadPath() + .equals(((DownloadTaskEntity) entity).getKey())) { + ((DownloadTaskEntity) entity).setUrl(((DownloadEntity) dbEntity).getUrl()); + isRefresh = true; + break; + } + } + if (isRefresh) { + update.insertData(db, entity); + } } + } else { + update.insertData(db, entity); } } - entity.rowID = cursor.getInt(cursor.getColumnIndex("rowid")); - mDataCache.put(getCacheKey(entity), entity); - entitys.add(entity); + } catch (Exception e) { + ALog.e(TAG, ALog.getExceptionString(e)); } - closeCursor(cursor); - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); } - } - return entitys; - } - /** - * 查找一对多、一对一的关联数据 - * - * @param primary 当前表的主键 - * @param childParams 当前表关联数据的类名 $$ 主键名 - */ - private static List findForeignData(SQLiteDatabase db, String primary, - String childParams) { - String[] params = childParams.split("\\$\\$"); - return findData(db, params[0], params[1] + "=?", primary); - } + //删除中介表 + mDelegate.dropTable(db, tableName + "_temp"); - private static void closeCursor(Cursor cursor) { - if (cursor != null && !cursor.isClosed()) { - try { - cursor.close(); - } catch (android.database.SQLException e) { - e.printStackTrace(); - } + mDelegate.close(db); } - } - - private static void close(SQLiteDatabase db) { - //if (db != null && db.isOpen()) db.close(); - } - private static SQLiteDatabase checkDb(SQLiteDatabase db) { - if (db == null || !db.isOpen()) { - db = INSTANCE.getWritableDatabase(); - } - return db; + map.clear(); } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java b/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java index 877f10dd..525f90cd 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java @@ -16,6 +16,14 @@ package com.arialyy.aria.orm; import android.text.TextUtils; +import com.arialyy.aria.orm.annotation.Default; +import com.arialyy.aria.orm.annotation.Foreign; +import com.arialyy.aria.orm.annotation.Ignore; +import com.arialyy.aria.orm.annotation.Many; +import com.arialyy.aria.orm.annotation.NoNull; +import com.arialyy.aria.orm.annotation.One; +import com.arialyy.aria.orm.annotation.Primary; +import com.arialyy.aria.orm.annotation.Wrapper; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -32,31 +40,41 @@ import java.util.Set; final class SqlUtil { /** - * 获取一对一参数 + * 获取主键字段名 */ - static String getOneToOneParams(Field field) { - OneToOne oneToOne = field.getAnnotation(OneToOne.class); - if (oneToOne == null) { - throw new IllegalArgumentException("@OneToOne注解的对象必须要有@Primary注解的字段"); + static String getPrimaryName(Class clazz) { + List fields = CommonUtil.getAllFields(clazz); + String column; + if (fields != null && !fields.isEmpty()) { + + for (Field field : fields) { + field.setAccessible(true); + if (isPrimary(field)) { + column = field.getName(); + return column; + } + } } - return oneToOne.table().getName() + "$$" + oneToOne.key(); + return null; } /** - * 获取List一对多参数 - * - * @param field list反射字段 + * 获取类中所有不被忽略的字段 */ - static String getOneToManyElementParams(Field field) { - OneToMany oneToMany = field.getAnnotation(OneToMany.class); - if (oneToMany == null) { - throw new IllegalArgumentException("一对多元素必须被@OneToMany注解"); + static List getAllNotIgnoreField(Class clazz) { + List fields = CommonUtil.getAllFields(clazz); + List temp = new ArrayList<>(); + if (fields != null && fields.size() > 0) { + for (Field f : fields) { + f.setAccessible(true); + if (!isIgnore(f)) { + temp.add(f); + } + } + return temp; + } else { + return null; } - //关联的表名 - String tableName = oneToMany.table().getName(); - //关联的字段 - String key = oneToMany.key(); - return tableName + "$$" + key; } /** @@ -65,10 +83,6 @@ final class SqlUtil { * @param field list反射字段 */ static String list2Str(DbEntity dbEntity, Field field) throws IllegalAccessException { - NormalList normalList = field.getAnnotation(NormalList.class); - if (normalList == null) { - throw new IllegalArgumentException("List中元素必须被@NormalList注解"); - } List list = (List) field.get(dbEntity); if (list == null || list.isEmpty()) return ""; StringBuilder sb = new StringBuilder(); @@ -85,17 +99,17 @@ final class SqlUtil { * @return 如果str为null,则返回null */ static List str2List(String str, Field field) { - NormalList normalList = field.getAnnotation(NormalList.class); - if (normalList == null) { - throw new IllegalArgumentException("List中元素必须被@NormalList注解"); - } if (TextUtils.isEmpty(str)) return null; String[] datas = str.split("\\$\\$"); List list = new ArrayList(); - String type = normalList.clazz().getName(); - for (String data : datas) { - list.add(checkData(type, data)); + Class clazz = CommonUtil.getListParamType(field); + if (clazz != null) { + String type = clazz.getName(); + for (String data : datas) { + list.add(checkData(type, data)); + } } + return list; } @@ -152,9 +166,12 @@ final class SqlUtil { } /** + * shadow$_klass_、shadow$_monitor_、{@link Ignore}、rowID、{@link Field#isSynthetic()}、{@link + * Modifier#isFinal(int)}、{@link Modifier#isStatic(int)}将被忽略 + * * @return true 忽略该字段 */ - static boolean ignoreField(Field field) { + static boolean isIgnore(Field field) { // field.isSynthetic(), 使用as热启动App时,AS会自动给你的class添加change字段 Ignore ignore = field.getAnnotation(Ignore.class); int modifiers = field.getModifiers(); @@ -164,19 +181,29 @@ final class SqlUtil { .isStatic(modifiers) || Modifier.isFinal(modifiers); } + /** + * 判断是否是Wrapper注解 + * + * @return {@code true} 是 + */ + static boolean isWrapper(Class clazz) { + Wrapper w = (Wrapper) clazz.getAnnotation(Wrapper.class); + return w != null; + } + /** * 判断是否一对多注解 */ - static boolean isOneToMany(Field field) { - OneToMany oneToMany = field.getAnnotation(OneToMany.class); + static boolean isMany(Field field) { + Many oneToMany = field.getAnnotation(Many.class); return oneToMany != null; } /** * 判断是否是一对一注解 */ - static boolean isOneToOne(Field field) { - OneToOne oneToOne = field.getAnnotation(OneToOne.class); + static boolean isOne(Field field) { + One oneToOne = field.getAnnotation(One.class); return oneToOne != null; } @@ -210,6 +237,16 @@ final class SqlUtil { return nn != null; } + /** + * 判断是否是default + * + * @return {@code true}为default + */ + static boolean isDefault(Field field) { + Default nn = field.getAnnotation(Default.class); + return nn != null; + } + private static Object checkData(String type, String data) { if (type.equalsIgnoreCase("java.lang.String")) { return data; @@ -222,17 +259,4 @@ final class SqlUtil { } return null; } - - /** - * 查找class的主键字段 - * - * @return 返回主键字段名 - */ - static String getPrimaryName(Class clazz) { - List fields = CommonUtil.getAllFields(clazz); - for (Field field : fields) { - if (isPrimary(field)) return field.getName(); - } - return null; - } } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/OneToMany.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Default.java similarity index 79% rename from Aria/src/main/java/com/arialyy/aria/orm/OneToMany.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/Default.java index 8f529e1b..dcc81434 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/OneToMany.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Default.java @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.orm; + +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -21,16 +22,9 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Created by AriaL on 2017/7/4. - * 一对多 + * Created by lyy on 2015/11/2. + * 默认数据 */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface OneToMany { - /** - * 关联的表 - */ - Class table(); - /** - * 关联的主键 - */ - String key(); -} +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Default { + String value() default ""; +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java new file mode 100644 index 00000000..6371a4d7 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.orm.annotation; + +import com.arialyy.aria.orm.ActionPolicy; +import com.arialyy.aria.orm.DbEntity; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by AriaL on 2017/7/4. + * 外键约束 + */ +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Foreign { + + /** + * 关联的表 + */ + Class parent(); + + /** + * 父表对应的列名 + */ + String column(); + + /** + * ON UPDATE 约束 + */ + ActionPolicy onUpdate() default ActionPolicy.NO_ACTION; + + /** + * ON DELETE 约束 + */ + ActionPolicy onDelete() default ActionPolicy.NO_ACTION; +} diff --git a/Aria/src/main/java/com/arialyy/aria/orm/Ignore.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/orm/Ignore.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java index 33c4523f..d8a9fe0d 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/Ignore.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/Aria/src/main/java/com/arialyy/aria/orm/PrimaryAndForeign.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Many.java similarity index 72% rename from Aria/src/main/java/com/arialyy/aria/orm/PrimaryAndForeign.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/Many.java index 2b50b18d..03f6fd72 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/PrimaryAndForeign.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Many.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -22,15 +22,23 @@ import java.lang.annotation.Target; /** * Created by AriaL on 2017/7/4. - * 主键和外键约束 + * 一对多 */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface PrimaryAndForeign { +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Many { /** - * 关联的表 + * 父表对应的字段名 */ - Class table(); + String parentColumn(); + /** - * 关联的列 + * 父表在子表对应的字段 */ - String column(); + String entityColumn(); + + ///** + // * 是否是一对一关系 + // * + // * @return {@code true} 是,{@code false} 不是 + // */ + //boolean isOne2One() default false; } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/NoNull.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/orm/NoNull.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java index 212efc4b..d29c8ad9 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/NoNull.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/Aria/src/main/java/com/arialyy/aria/orm/OneToOne.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/One.java similarity index 82% rename from Aria/src/main/java/com/arialyy/aria/orm/OneToOne.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/One.java index 7641fd55..3f8df08e 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/OneToOne.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/One.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -22,17 +22,8 @@ import java.lang.annotation.Target; /** * Created by AriaL on 2017/7/4. - * 一对一 + * 一 */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface OneToOne { +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface One { - /** - * 关联的表 - */ - Class table(); - - /** - * 关联的主键 - */ - String key(); } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/Primary.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Primary.java similarity index 87% rename from Aria/src/main/java/com/arialyy/aria/orm/Primary.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/Primary.java index 365f1713..4ac2fb1c 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/Primary.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Primary.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -27,4 +27,9 @@ import java.lang.annotation.Target; */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Primary { int value() default -1; + + /** + * 字段需要int类型才可以自增 + */ + boolean autoincrement() default false; } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Table.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Table.java new file mode 100644 index 00000000..e2cbbb94 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Table.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.arialyy.aria.orm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by lyy on 2015/11/2. + * 设置表名,如果不是使用该注解,默认为类名 + */ +@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Table { + String tableName(); +} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/Foreign.java b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java similarity index 73% rename from Aria/src/main/java/com/arialyy/aria/orm/Foreign.java rename to Aria/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java index cc577d24..e0894440 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/Foreign.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.orm; +package com.arialyy.aria.orm.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -21,16 +21,9 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Created by AriaL on 2017/7/4. - * 外键约束 + * Created by laoyuyu on 2018/3/21. + * 关系包裹 */ -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Foreign { - /** - * 关联的表 - */ - Class table(); - /** - * 关联的列 - */ - String column(); +@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) +public @interface Wrapper { } diff --git a/Aria/src/main/java/com/arialyy/aria/util/ALog.java b/Aria/src/main/java/com/arialyy/aria/util/ALog.java index f24a3a30..d4b2dc96 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/ALog.java +++ b/Aria/src/main/java/com/arialyy/aria/util/ALog.java @@ -19,13 +19,19 @@ package com.arialyy.aria.util; import android.text.TextUtils; import android.util.Log; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; /** * Created by Aria.Lao on 2017/10/25. * Aria日志工具 */ public class ALog { - + public static final boolean DEBUG = true; public static final int LOG_LEVEL_VERBOSE = 2; public static final int LOG_LEVEL_DEBUG = 3; public static final int LOG_LEVEL_INFO = 4; @@ -58,7 +64,53 @@ public class ALog { } public static int e(String tag, Throwable e) { - return println(Log.ERROR, tag, getExceptionString(e)); + String msg = getExceptionString(e); + ErrorHelp.saveError(tag, "", msg); + return println(Log.ERROR, tag, msg); + } + + /** + * 打印MAp,debug级别日志 + */ + public static void m(String tag, Map map) { + if (LOG_LEVEL <= Log.DEBUG) { + Set set = map.entrySet(); + if (set.size() < 1) { + d(tag, "[]"); + return; + } + int i = 0; + String[] s = new String[set.size()]; + for (Object aSet : set) { + Map.Entry entry = (Map.Entry) aSet; + s[i] = entry.getKey() + " = " + entry.getValue() + ",\n"; + i++; + } + println(Log.DEBUG, tag, Arrays.toString(s)); + } + } + + /** + * 打印JSON,debug级别日志 + */ + public static void j(String tag, String jsonStr) { + if (LOG_LEVEL <= Log.DEBUG) { + String message; + try { + if (jsonStr.startsWith("{")) { + JSONObject jsonObject = new JSONObject(jsonStr); + message = jsonObject.toString(4); //这个是核心方法 + } else if (jsonStr.startsWith("[")) { + JSONArray jsonArray = new JSONArray(jsonStr); + message = jsonArray.toString(4); + } else { + message = jsonStr; + } + } catch (JSONException e) { + message = jsonStr; + } + println(Log.DEBUG, tag, message); + } } /** diff --git a/Aria/src/main/java/com/arialyy/aria/util/AriaCrashHandler.java b/Aria/src/main/java/com/arialyy/aria/util/AriaCrashHandler.java new file mode 100644 index 00000000..0684cd72 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/util/AriaCrashHandler.java @@ -0,0 +1,75 @@ +/* + * 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.util; + +import android.util.Log; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Created by Aria.Lao on 2017/10/25. + * 程序异常日志捕获器 + */ +public class AriaCrashHandler implements Thread.UncaughtExceptionHandler { + private Thread.UncaughtExceptionHandler mDefaultHandler; + private ExecutorService mExecutorService; + + public AriaCrashHandler() { + mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); + mExecutorService = Executors.newSingleThreadExecutor(); + } + + @Override + public void uncaughtException(Thread thread, Throwable ex) { + ex.printStackTrace(); + //ALog.d(thread.getName(), ex.getLocalizedMessage()); + handleException(thread.getName(), ex); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + mDefaultHandler.uncaughtException(thread, ex); + exit(); + } + } + + /** + * 处理异常 + */ + private void handleException(final String name, final Throwable ex) { + if (ex == null) { + return; + } + + mExecutorService.execute(new Runnable() { + @Override + public void run() { + ErrorHelp.saveError(name, "", ALog.getExceptionString(ex)); + } + }); + } + + /** + * 退出当前应用 + */ + private void exit() { + android.os.Process.killProcess(android.os.Process.myPid()); + System.exit(1); + } +} \ 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 103a5153..2e61f8c1 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -74,7 +74,8 @@ public class CheckUtil { * 检查下载实体 */ public static void checkDownloadEntity(DownloadEntity entity) { - entity.setUrl(checkUrl(entity.getUrl())); + checkUrlInvalidThrow(entity.getUrl()); + entity.setUrl(entity.getUrl()); checkPath(entity.getDownloadPath()); } @@ -88,9 +89,9 @@ public class CheckUtil { } /** - * 检测下载链接是否合法,如果地址中path是"//"而不是"/"将会改为"/"; + * 检测url是否合法,如果url不合法,将抛异常 */ - public static String checkUrl(String url) { + public static void checkUrlInvalidThrow(String url) { if (TextUtils.isEmpty(url)) { throw new IllegalArgumentException("url不能为null"); } else if (!url.startsWith("http") && !url.startsWith("ftp")) { @@ -100,13 +101,26 @@ public class CheckUtil { if (index == -1) { throw new IllegalArgumentException("url不合法"); } - String temp = url.substring(index + 3, url.length()); - if (temp.contains("//")) { - temp = url.substring(0, index + 3) + temp.replaceAll("//", "/"); - ALog.w(TAG, "url中含有//,//将转换为/,转换后的url为:" + temp); - return temp; + } + + /** + * 检测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 url; + return true; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java index ee215f6f..96f06349 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; +import android.os.Environment; import android.text.TextUtils; import android.util.Base64; import com.arialyy.aria.core.AriaManager; @@ -30,13 +31,10 @@ import com.arialyy.aria.core.command.group.GroupCmdFactory; import com.arialyy.aria.core.command.normal.AbsNormalCmd; import com.arialyy.aria.core.command.normal.NormalCmdFactory; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadGroupTaskEntity; -import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.inf.AbsGroupTaskEntity; import com.arialyy.aria.core.inf.AbsTaskEntity; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.upload.UploadTaskEntity; -import com.arialyy.aria.orm.DbEntity; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; @@ -46,11 +44,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; -import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URLEncoder; @@ -62,6 +57,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.regex.Matcher; @@ -73,6 +69,139 @@ import java.util.regex.Pattern; public class CommonUtil { private static final String TAG = "CommonUtil"; + /** + * 拦截window.location.replace数据 + * + * @return 重定向url + */ + public static String getWindowReplaceUrl(String text) { + if (TextUtils.isEmpty(text)) { + ALog.e(TAG, "拦截数据为null"); + return null; + } + String reg = Regular.REG_WINLOD_REPLACE; + Pattern p = Pattern.compile(reg); + Matcher m = p.matcher(text); + if (m.find()){ + String s = m.group(); + s = s.substring(9, s.length() - 2); + return s; + } + return null; + } + + /** + * 获取sdcard app的缓存目录 + * + * @return "/mnt/sdcard/Android/data/{package_name}/files/" + */ + public static String getAppPath(Context context) { + //判断是否存在sd卡 + boolean sdExist = android.os.Environment.MEDIA_MOUNTED.equals( + android.os.Environment.getExternalStorageState()); + if (!sdExist) { + return null; + } else { + //获取sd卡路径 + File file = context.getExternalFilesDir(null); + String dir; + if (file != null) { + dir = file.getPath() + "/"; + } else { + dir = Environment.getExternalStorageDirectory().getPath() + + "/Android/data/" + + context.getPackageName() + + "/files/"; + } + return dir; + } + } + + /** + * 获取map泛型类型 + * + * @param map list类型字段 + * @return 泛型类型 + */ + public static Class[] getMapParamType(Field map) { + Class type = map.getType(); + if (!type.isAssignableFrom(Map.class)) { + ALog.d(TAG, "字段类型不是Map"); + return null; + } + + Type fc = map.getGenericType(); + + if (fc == null) { + ALog.d(TAG, "该字段没有泛型参数"); + return null; + } + + if (fc instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) fc; + Type[] types = pt.getActualTypeArguments(); + Class[] clazz = new Class[2]; + clazz[0] = (Class) types[0]; + clazz[1] = (Class) types[1]; + return clazz; + } + return null; + } + + /** + * 获取list泛型类型 + * + * @param list list类型字段 + * @return 泛型类型 + */ + + public static Class getListParamType(Field list) { + Class type = list.getType(); + if (!type.isAssignableFrom(List.class)) { + ALog.d(TAG, "字段类型不是List"); + return null; + } + + Type fc = list.getGenericType(); // 关键的地方,如果是List类型,得到其Generic的类型 + + if (fc == null) { + ALog.d(TAG, "该字段没有泛型参数"); + return null; + } + + if (fc instanceof ParameterizedType) { //如果是泛型参数的类型 + ParameterizedType pt = (ParameterizedType) fc; + return (Class) pt.getActualTypeArguments()[0]; //得到泛型里的class类型对象。 + } + return null; + } + + /** + * 创建文件名,如果url链接有后缀名,则使用url中的后缀名 + * + * @return url 的 hashKey + */ + public static String createFileName(String url) { + int end = url.indexOf("?"); + String tempUrl, fileName = ""; + if (end > 0) { + tempUrl = url.substring(0, end); + int tempEnd = tempUrl.lastIndexOf("/"); + if (tempEnd > 0) { + fileName = tempUrl.substring(tempEnd + 1, tempUrl.length()); + } + } else { + int tempEnd = url.lastIndexOf("/"); + if (tempEnd > 0) { + fileName = url.substring(tempEnd + 1, url.length()); + } + } + if (TextUtils.isEmpty(fileName)) { + fileName = CommonUtil.keyToHashKey(url); + } + return fileName; + } + /** * 分割获取url,协议,ip/域名,端口,内容 * @@ -204,55 +333,6 @@ public class CommonUtil { return null; } - /** - * 实例化泛型的实际类型参数 - * - * @throws Exception - */ - public static void typeCheck(Type type) throws Exception { - System.out.println("该类型是" + type); - // 参数化类型 - if (type instanceof ParameterizedType) { - Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments(); - for (int i = 0; i < typeArguments.length; i++) { - // 类型变量 - if (typeArguments[i] instanceof TypeVariable) { - System.out.println("第" + (i + 1) + "个泛型参数类型是类型变量" + typeArguments[i] + ",无法实例化。"); - } - // 通配符表达式 - else if (typeArguments[i] instanceof WildcardType) { - System.out.println("第" + (i + 1) + "个泛型参数类型是通配符表达式" + typeArguments[i] + ",无法实例化。"); - } - // 泛型的实际类型,即实际存在的类型 - else if (typeArguments[i] instanceof Class) { - System.out.println("第" + (i + 1) + "个泛型参数类型是:" + typeArguments[i] + ",可以直接实例化对象"); - } - } - // 参数化类型数组或类型变量数组 - } else if (type instanceof GenericArrayType) { - System.out.println("该泛型类型是参数化类型数组或类型变量数组,可以获取其原始类型。"); - Type componentType = ((GenericArrayType) type).getGenericComponentType(); - // 类型变量 - if (componentType instanceof TypeVariable) { - System.out.println("该类型变量数组的原始类型是类型变量" + componentType + ",无法实例化。"); - } - // 参数化类型,参数化类型数组或类型变量数组 - // 参数化类型数组或类型变量数组也可以是多维的数组,getGenericComponentType()方法仅仅是去掉最右边的[] - else { - // 递归调用方法自身 - typeCheck(componentType); - } - } else if (type instanceof TypeVariable) { - System.out.println("该类型是类型变量"); - } else if (type instanceof WildcardType) { - System.out.println("该类型是通配符表达式"); - } else if (type instanceof Class) { - System.out.println("该类型不是泛型类型"); - } else { - throw new Exception(); - } - } - /** * 根据下载任务组的url创建key * @@ -282,28 +362,27 @@ public class CommonUtil { * {@code false}如果任务已经完成,只删除任务数据库记录 */ public static void delDownloadGroupTaskConfig(boolean removeFile, - DownloadGroupTaskEntity tEntity) { - List tasks = - DbEntity.findDatas(DownloadTaskEntity.class, "groupName=?", tEntity.key); - if (tasks != null && !tasks.isEmpty()) { - for (DownloadTaskEntity taskEntity : tasks) { - delDownloadTaskConfig(removeFile, taskEntity); - } + DownloadGroupEntity groupEntity) { + if (groupEntity == null) { + ALog.e(TAG, "删除下载任务组记录失败,任务组实体为null"); + return; } - if (tEntity.getEntity() != null) { - File dir = new File(tEntity.getEntity().getDirPath()); - if (removeFile) { - if (dir.exists()) { - dir.delete(); - } - } else { - if (!tEntity.getEntity().isComplete()) { - dir.delete(); - } + for (DownloadEntity taskEntity : groupEntity.getSubEntities()) { + delDownloadTaskConfig(removeFile, taskEntity); + } + + File dir = new File(groupEntity.getDirPath()); + if (removeFile) { + if (dir.exists()) { + dir.delete(); + } + } else { + if (!groupEntity.isComplete()) { + dir.delete(); } - tEntity.deleteData(); } + groupEntity.deleteData(); } /** @@ -312,8 +391,7 @@ public class CommonUtil { * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经删除完成的文件 * {@code false}如果任务已经完成,只删除任务数据库记录 */ - public static void delUploadTaskConfig(boolean removeFile, UploadTaskEntity tEntity) { - UploadEntity uEntity = tEntity.getEntity(); + public static void delUploadTaskConfig(boolean removeFile, UploadEntity uEntity) { if (uEntity == null) { return; } @@ -327,8 +405,8 @@ public class CommonUtil { if (config.exists()) { config.delete(); } - //uEntity.deleteData(); - tEntity.deleteData(); + //下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除 + uEntity.deleteData(); } /** @@ -337,8 +415,7 @@ public class CommonUtil { * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经下载完成的文件 * {@code false}如果任务已经完成,只删除任务数据库记录 */ - public static void delDownloadTaskConfig(boolean removeFile, DownloadTaskEntity tEntity) { - DownloadEntity dEntity = tEntity.getEntity(); + public static void delDownloadTaskConfig(boolean removeFile, DownloadEntity dEntity) { if (dEntity == null) return; File file = new File(dEntity.getDownloadPath()); if (removeFile) { @@ -357,9 +434,8 @@ public class CommonUtil { if (config.exists()) { config.delete(); } - //dEntity.deleteData(); - //tEntity.deleteData(); - tEntity.deleteData(); + //下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除 + dEntity.deleteData(); } /** @@ -741,7 +817,7 @@ public class CommonUtil { return; } File file = new File(path); - if (!file.getParentFile().exists()) { + if (file.getParentFile() == null || !file.getParentFile().exists()) { ALog.d(TAG, "目标文件所在路径不存在,准备创建……"); if (!createDir(file.getParent())) { ALog.d(TAG, "创建目录文件所在的目录失败!文件路径【" + path + "】"); @@ -762,34 +838,6 @@ public class CommonUtil { } } - /** - * 设置打印的异常格式 - */ - public static String getPrintException(Throwable ex) { - if (ex == null) return ""; - StringBuilder err = new StringBuilder(); - err.append("ExceptionDetailed:\n"); - err.append("====================Exception Info====================\n"); - err.append(ex.toString()); - err.append("\n"); - StackTraceElement[] stack = ex.getStackTrace(); - for (StackTraceElement stackTraceElement : stack) { - err.append(stackTraceElement.toString()).append("\n"); - } - Throwable cause = ex.getCause(); - if (cause != null) { - err.append("【Caused by】: "); - err.append(cause.toString()); - err.append("\n"); - StackTraceElement[] stackTrace = cause.getStackTrace(); - for (StackTraceElement stackTraceElement : stackTrace) { - err.append(stackTraceElement.toString()).append("\n"); - } - } - err.append("==================================================="); - return err.toString(); - } - /** * 通过文件名获取下载配置文件路径 * diff --git a/Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java b/Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java index 4a56c186..8f53fc75 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java +++ b/Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java @@ -15,11 +15,15 @@ */ package com.arialyy.aria.util; -import com.arialyy.aria.core.ErrorEntity; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.upload.UploadEntity; +import android.annotation.SuppressLint; +import android.util.Log; +import com.arialyy.aria.core.AriaManager; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; /** * Created by Aria.Lao on 2017/8/29. @@ -30,29 +34,69 @@ public class ErrorHelp { /** * 保存错误信息 * - * @param taskType 任务类型 - * @param entity 任务实体 * @param msg 错误提示 * @param ex 异常 */ - public static void saveError(String taskType, AbsEntity entity, String msg, String ex) { - ErrorEntity errorEntity = new ErrorEntity(); - errorEntity.insertTime = System.currentTimeMillis(); - errorEntity.err = ex; - errorEntity.msg = msg; - errorEntity.taskType = taskType; - String name = ""; - String key = entity.getKey(); - if (entity instanceof DownloadEntity) { - name = ((DownloadEntity) entity).getFileName(); - } else if (entity instanceof DownloadGroupEntity) { - name = ((DownloadGroupEntity) entity).getGroupName(); - } else if (entity instanceof UploadEntity) { - name = ((UploadEntity) entity).getFileName(); + public static void saveError(String tag, String msg, String ex) { + String message = "\nmsg【" + msg + "】\nException:" + ex; + writeLogToFile(tag, message); + } + + /** + * 返回日志路径 + * + * @return "/mnt/sdcard/Android/data/{package_name}/files/log/*" + */ + private static String getLogPath() { + String path = CommonUtil.getAppPath(AriaManager.APP) + + "log/AriaCrash_" + + getData("yyyy-MM-dd_HH:mm:ss") + + ".log"; + + File log = new File(path); + if (!log.getParentFile().exists()) { + log.getParentFile().mkdirs(); + } + if (!log.exists()) { + try { + log.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } } + return path; + } + + /** + * 把日志记录到文件 + */ + private static int writeLogToFile(String tag, String message) { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(getData("yyyy-MM-dd HH:mm:ss")); + stringBuffer.append(" "); + stringBuffer.append(tag); + stringBuffer.append(" "); + stringBuffer.append(message); + stringBuffer.append("\n\n"); + PrintWriter writer = null; + try { + writer = new PrintWriter(new FileWriter(getLogPath(), true)); + writer.append(stringBuffer); + writer.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (writer != null) { + writer.close(); + } + } + return 0; + } - errorEntity.taskName = name; - errorEntity.key = key; - errorEntity.insert(); + @SuppressLint("SimpleDateFormat") + private static String getData(String format) { + Date date = new Date(System.currentTimeMillis()); + SimpleDateFormat sdf = new SimpleDateFormat(format); + return sdf.format(date); } } diff --git a/Aria/src/main/java/com/arialyy/aria/util/Regular.java b/Aria/src/main/java/com/arialyy/aria/util/Regular.java index ba26b6df..829b5dda 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/Regular.java +++ b/Aria/src/main/java/com/arialyy/aria/util/Regular.java @@ -29,4 +29,9 @@ public interface Regular { * 匹配双字节字符、空格、制表符、换行符 */ String REG_DOUBLE_CHAR_AND_SPACE = "[^\\x00-\\xff]|[\\s\\p{Zs}]"; + + /** + * 匹配window.location.replace + */ + String REG_WINLOD_REPLACE = "replace\\(\".*\"\\)"; } diff --git a/AriaFtpPlug/src/main/java/org/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java b/AriaFtpPlug/src/main/java/org/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java index fd96c8d0..5752a9b9 100644 --- a/AriaFtpPlug/src/main/java/org/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java +++ b/AriaFtpPlug/src/main/java/org/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java @@ -29,7 +29,8 @@ import org.apache.commons.net.ftp.FTPFile; * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) * @since 3.1 */ -public class MacOsPeterFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { +public class +MacOsPeterFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { static final String DEFAULT_DATE_FORMAT = "MMM d yyyy"; //Nov 9 2001 diff --git a/DEV_LOG.md b/DEV_LOG.md index 8caf37f3..961a3125 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,4 +1,16 @@ ## 开发日志 + + 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有"//"将替换为"/" diff --git a/README.md b/README.md index 2973c78d..0036279e 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ Aria有以下特点: [![Download](https://api.bintray.com/packages/arialyy/maven/AriaApi/images/download.svg)](https://bintray.com/arialyy/maven/AriaApi/_latestVersion) [![Download](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.3.14' -annotationProcessor 'com.arialyy.aria:aria-compiler:3.3.14' +compile 'com.arialyy.aria:aria-core:3.4' +annotationProcessor 'com.arialyy.aria:aria-compiler:3.4' ``` -如果出现android support,请将 `compile 'com.arialyy.aria:aria-core:3.3.13'`替换为 +如果出现android support,请将 `compile 'com.arialyy.aria:aria-core:'`替换为 ``` compile('com.arialyy.aria:aria-core:'){ exclude group: 'com.android.support' @@ -94,20 +94,17 @@ protected void onCreate(Bundle savedInstanceState) { //在这里处理任务完成的状态 } ``` -[更多注解使用方法](https://github.com/AriaLyy/Aria/wiki/%E6%B3%A8%E8%A7%A3%E4%BD%BF%E7%94%A8) -### [HTTP任务组下载\FTP下载;HTTP\FTP文件上传](https://github.com/AriaLyy/Aria/wiki/Aria%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8) - -### [参数配置](https://github.com/AriaLyy/Aria/wiki/Aria%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE) - -### [更多说明,见WIKI](https://github.com/AriaLyy/Aria/wiki) +### [更多说明,见WIKI](http://aria.laoyuyu.me/aria_doc/) ### 版本日志 - + v_3.3.14 - - 修复ftp上传和下载的兼容性问题 - - 如果url中的path有"//"将替换为"/" - - 修复http上传成功后,如果服务器没有设置返回码导致上传失败的问题 - - 上传实体UploadEntity增加responseStr字段,http上传完成后,在被`@Upload.onComplete`注解的方法中,可通过`task.getEntity().getResponseStr())`获取服务器返回的数据 + + 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`携带的文件名 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) diff --git a/app/build.gradle b/app/build.gradle index f7b3c632..c966701e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,8 +53,8 @@ dependencies { compile project(':Aria') compile project(':AriaCompiler') // compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" - // compile 'com.arialyy.aria:aria-core:3.2.13' -// annotationProcessor 'com.arialyy.aria:aria-compiler:3.2.13' +// compile 'com.arialyy.aria:aria-core:3.3.16' +// annotationProcessor 'com.arialyy.aria:aria-compiler:3.3.16' } repositories { mavenCentral() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6730d35c..c11fae91 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,8 +14,11 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme.NoActionBar"> + + + diff --git a/app/src/main/assets/aria_config.xml b/app/src/main/assets/aria_config.xml index ce92a575..3c437798 100644 --- a/app/src/main/assets/aria_config.xml +++ b/app/src/main/assets/aria_config.xml @@ -1,6 +1,14 @@ + + + + + + + + diff --git a/app/src/main/java/com/arialyy/simple/download/DownloadDialog.java b/app/src/main/java/com/arialyy/simple/download/DownloadDialog.java index 234fb81e..68d656e4 100644 --- a/app/src/main/java/com/arialyy/simple/download/DownloadDialog.java +++ b/app/src/main/java/com/arialyy/simple/download/DownloadDialog.java @@ -57,8 +57,8 @@ public class DownloadDialog extends AbsDialog { } private void init() { - Aria.download(this).register(); - DownloadEntity entity = Aria.download(this).getDownloadEntity(DOWNLOAD_URL); + Aria.download(getContext()).register(); + DownloadEntity entity = Aria.download(getContext()).getDownloadEntity(DOWNLOAD_URL); if (entity != null) { mSize.setText(CommonUtil.formatFileSize(entity.getFileSize())); int p = (int) (entity.getCurrentProgress() * 100 / entity.getFileSize()); @@ -73,16 +73,16 @@ public class DownloadDialog extends AbsDialog { @OnClick({ R.id.start, R.id.stop, R.id.cancel }) public void onClick(View view) { switch (view.getId()) { case R.id.start: - Aria.download(this) + Aria.download(getContext()) .load(DOWNLOAD_URL) .setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/飞机大战.apk") .start(); break; case R.id.stop: - Aria.download(this).load(DOWNLOAD_URL).pause(); + Aria.download(getContext()).load(DOWNLOAD_URL).pause(); break; case R.id.cancel: - Aria.download(this).load(DOWNLOAD_URL).cancel(); + Aria.download(getContext()).load(DOWNLOAD_URL).cancel(); break; } } diff --git a/app/src/main/java/com/arialyy/simple/download/DownloadDialogFragment.java b/app/src/main/java/com/arialyy/simple/download/DownloadDialogFragment.java index 77d59ce1..ec7a0153 100644 --- a/app/src/main/java/com/arialyy/simple/download/DownloadDialogFragment.java +++ b/app/src/main/java/com/arialyy/simple/download/DownloadDialogFragment.java @@ -31,8 +31,8 @@ import com.arialyy.simple.databinding.DialogFragmentDownloadBinding; @Override protected void init(Bundle savedInstanceState) { super.init(savedInstanceState); - Aria.download(this).register(); - DownloadEntity entity = Aria.download(this).getDownloadEntity(DOWNLOAD_URL); + Aria.download(getContext()).register(); + DownloadEntity entity = Aria.download(getContext()).getDownloadEntity(DOWNLOAD_URL); if (entity != null) { getBinding().setFileSize(CommonUtil.formatFileSize(entity.getFileSize())); getBinding().setProgress((int) (entity.getCurrentProgress() * 100 / entity.getFileSize())); @@ -45,7 +45,7 @@ import com.arialyy.simple.databinding.DialogFragmentDownloadBinding; @Override public void onDestroy() { super.onDestroy(); - Aria.download(this).unRegister(); + Aria.download(getContext()).unRegister(); } @Download.onPre(DOWNLOAD_URL) protected void onPre(DownloadTask task) { @@ -96,16 +96,16 @@ import com.arialyy.simple.databinding.DialogFragmentDownloadBinding; @OnClick({ R.id.start, R.id.stop, R.id.cancel }) public void onClick(View view) { switch (view.getId()) { case R.id.start: - Aria.download(this) + Aria.download(getContext()) .load(DOWNLOAD_URL) .setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/放置江湖.apk") .start(); break; case R.id.stop: - Aria.download(this).load(DOWNLOAD_URL).stop(); + Aria.download(getContext()).load(DOWNLOAD_URL).stop(); break; case R.id.cancel: - Aria.download(this).load(DOWNLOAD_URL).cancel(); + Aria.download(getContext()).load(DOWNLOAD_URL).cancel(); break; } } diff --git a/app/src/main/java/com/arialyy/simple/download/DownloadPopupWindow.java b/app/src/main/java/com/arialyy/simple/download/DownloadPopupWindow.java index a8c7188d..f6fae8ac 100644 --- a/app/src/main/java/com/arialyy/simple/download/DownloadPopupWindow.java +++ b/app/src/main/java/com/arialyy/simple/download/DownloadPopupWindow.java @@ -59,13 +59,13 @@ public class DownloadPopupWindow extends AbsPopupWindow { } private void initWidget() { - if (Aria.download(this).taskExists(DOWNLOAD_URL)) { - DownloadTarget target = Aria.download(this).load(DOWNLOAD_URL); + if (Aria.download(getContext()).taskExists(DOWNLOAD_URL)) { + DownloadTarget target = Aria.download(getContext()).load(DOWNLOAD_URL); int p = (int) (target.getCurrentProgress() * 100 / target.getFileSize()); mPb.setProgress(p); } - Aria.download(this).register(); - DownloadEntity entity = Aria.download(this).getDownloadEntity(DOWNLOAD_URL); + Aria.download(getContext()).register(); + DownloadEntity entity = Aria.download(getContext()).getDownloadEntity(DOWNLOAD_URL); if (entity != null) { mSize.setText(CommonUtil.formatFileSize(entity.getFileSize())); int state = entity.getState(); @@ -78,16 +78,16 @@ public class DownloadPopupWindow extends AbsPopupWindow { @OnClick({ R.id.start, R.id.stop, R.id.cancel }) public void onClick(View view) { switch (view.getId()) { case R.id.start: - Aria.download(this) + Aria.download(getContext()) .load(DOWNLOAD_URL) .setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/消消乐.apk") .start(); break; case R.id.stop: - Aria.download(this).load(DOWNLOAD_URL).pause(); + Aria.download(getContext()).load(DOWNLOAD_URL).pause(); break; case R.id.cancel: - Aria.download(this).load(DOWNLOAD_URL).cancel(); + Aria.download(getContext()).load(DOWNLOAD_URL).cancel(); break; } } diff --git a/app/src/main/java/com/arialyy/simple/download/FtpDownloadActivity.java b/app/src/main/java/com/arialyy/simple/download/FtpDownloadActivity.java index ef5fccf8..842cef85 100644 --- a/app/src/main/java/com/arialyy/simple/download/FtpDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/FtpDownloadActivity.java @@ -37,7 +37,11 @@ import java.io.File; public class FtpDownloadActivity extends BaseActivity { //private final String URL = "ftp://192.168.1.9:21/下载/AriaPrj.zip"; //private final String URL = "ftp://182.92.180.213:21/video/572fed5c2ad48_1024.jpg"; +<<<<<<< HEAD private final String URL = "ftp://182.92.180.213:21/DATA/20180205/rar/1111.rar"; +======= + private final String URL = "ftp://192.168.1.7:21/download//AriaPrj.zip"; +>>>>>>> v_3.4 //private final String URL = "ftp://d:d@dygodj8.com:12311/咖啡风暴HD大陆公映意语中字[飘花www.piaohua.com].mp4"; @Override protected void init(Bundle savedInstanceState) { @@ -60,9 +64,15 @@ public class FtpDownloadActivity extends BaseActivity>>>>>> v_3.4 break; case R.id.stop: Aria.download(this).loadFtp(URL).stop(); diff --git a/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java index 18123af9..f1a9dc69 100644 --- a/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java @@ -28,6 +28,7 @@ import android.widget.Toast; import butterknife.Bind; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.download.DownloadTarget; import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.IEntity; @@ -38,8 +39,6 @@ import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.databinding.ActivitySingleBinding; import java.io.File; -import java.util.HashMap; -import java.util.Map; public class SingleTaskActivity extends BaseActivity { @@ -47,19 +46,11 @@ public class SingleTaskActivity extends BaseActivity { //"http://kotlinlang.org/docs/kotlin-docs.pdf"; //"https://atom-installer.github.com/v1.13.0/AtomSetup.exe?s=1484074138&ext=.exe"; //"http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apk"; - //"http://sitcac.daxincf.cn/wp-content/uploads/swift_vido/01/element.mp4_1"; - //"http://120.25.196.56:8000/filereq?id=15692406294&ipncid=105635&client=android&filename=20170819185541.avi"; - //"http://down2.xiaoshuofuwuqi.com/d/file/filetxt/20170608/14/%BA%DA%CE%D7%CA%A6%E1%C8%C6%F0.txt"; - //"http://tinghuaapp.oss-cn-shanghai.aliyuncs.com/20170612201739607815"; - //"http://static.gaoshouyou.com/d/36/69/2d3699acfa69e9632262442c46516ad8.apk"; - //"http://oqcpqqvuf.bkt.clouddn.com/ceshi.txt"; - //"http://down8.androidgame-store.com/201706122321/97967927DD4E53D9905ECAA7874C8128/new/game1/19/45319/com.neuralprisma-2.5.2.174-2000174_1494784835.apk?f=web_1"; - //不支持断点的链接 - //"http://ox.konsung.net:5555/ksdc-web/download/downloadFile/?fileName=ksdc_1.0.2.apk&rRange=0-"; - //"http://gdown.baidu.com/data/wisegame/0904344dee4a2d92/QQ_718.apk"; - //"http://qudao.5535.cn/one/game.html?game=531&cpsuser=xiaoeryu2"; - "https://bogoe-res.mytbz.com/tbzengsong/If You're Happy.mp3"; - //"http://ozr0ucjs5.bkt.clouddn.com/51_box-104_20180131202610.apk"; + //"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"; + @Bind(R.id.start) Button mStart; @Bind(R.id.stop) Button mStop; @Bind(R.id.cancel) Button mCancel; @@ -84,7 +75,7 @@ public class SingleTaskActivity extends BaseActivity { } @Override public boolean onMenuItemClick(MenuItem item) { - double speed = -1; + int speed = -1; String msg = ""; switch (item.getItemId()) { case R.id.help: @@ -95,19 +86,19 @@ public class SingleTaskActivity extends BaseActivity { showMsgDialog("tip", msg); break; case R.id.speed_0: - speed = 0.0; + speed = 0; break; case R.id.speed_128: - speed = 128.0; + speed = 128; break; case R.id.speed_256: - speed = 256.0; + speed = 256; break; case R.id.speed_512: - speed = 512.0; + speed = 512; break; case R.id.speed_1m: - speed = 1024.0; + speed = 1024; break; } if (speed > -1) { @@ -119,67 +110,87 @@ public class SingleTaskActivity extends BaseActivity { } @Download.onWait void onWait(DownloadTask task) { - Log.d(TAG, "wait ==> " + task.getDownloadEntity().getFileName()); + if (task.getKey().equals(DOWNLOAD_URL)) { + Log.d(TAG, "wait ==> " + task.getDownloadEntity().getFileName()); + } } @Download.onPre protected void onPre(DownloadTask task) { - setBtState(false); + if (task.getKey().equals(DOWNLOAD_URL)) { + setBtState(false); + } } @Download.onTaskStart void taskStart(DownloadTask task) { - getBinding().setFileSize(task.getConvertFileSize()); + if (task.getKey().equals(DOWNLOAD_URL)) { + getBinding().setFileSize(task.getConvertFileSize()); + } } @Download.onTaskRunning protected void running(DownloadTask task) { - - long len = task.getFileSize(); - if (len == 0) { - getBinding().setProgress(0); - } else { - getBinding().setProgress(task.getPercent()); + if (task.getKey().equals(DOWNLOAD_URL)) { + //Log.d(TAG, task.getKey()); + long len = task.getFileSize(); + if (len == 0) { + getBinding().setProgress(0); + } else { + getBinding().setProgress(task.getPercent()); + } + getBinding().setSpeed(task.getConvertSpeed()); } - getBinding().setSpeed(task.getConvertSpeed()); } @Download.onTaskResume void taskResume(DownloadTask task) { - mStart.setText("暂停"); - setBtState(false); + if (task.getKey().equals(DOWNLOAD_URL)) { + mStart.setText("暂停"); + setBtState(false); + } } @Download.onTaskStop void taskStop(DownloadTask task) { - mStart.setText("恢复"); - setBtState(true); - getBinding().setSpeed(""); + if (task.getKey().equals(DOWNLOAD_URL)) { + mStart.setText("恢复"); + setBtState(true); + getBinding().setSpeed(""); + } } @Download.onTaskCancel void taskCancel(DownloadTask task) { - getBinding().setProgress(0); - Toast.makeText(SingleTaskActivity.this, "取消下载", Toast.LENGTH_SHORT).show(); - mStart.setText("开始"); - setBtState(true); - getBinding().setSpeed(""); - Log.d(TAG, "cancel"); + if (task.getKey().equals(DOWNLOAD_URL)) { + getBinding().setProgress(0); + Toast.makeText(SingleTaskActivity.this, "取消下载", Toast.LENGTH_SHORT).show(); + mStart.setText("开始"); + setBtState(true); + getBinding().setSpeed(""); + Log.d(TAG, "cancel"); + } } @Download.onTaskFail void taskFail(DownloadTask task) { - Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show(); - setBtState(true); + if (task.getKey().equals(DOWNLOAD_URL)) { + Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show(); + setBtState(true); + } } @Download.onTaskComplete void taskComplete(DownloadTask task) { - getBinding().setProgress(100); - Toast.makeText(SingleTaskActivity.this, "下载完成", Toast.LENGTH_SHORT).show(); - mStart.setText("重新开始?"); - 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)); + if (task.getKey().equals(DOWNLOAD_URL)) { + getBinding().setProgress(100); + Toast.makeText(SingleTaskActivity.this, "下载完成", Toast.LENGTH_SHORT).show(); + mStart.setText("重新开始?"); + 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)); + } } @Download.onNoSupportBreakPoint public void onNoSupportBreakPoint(DownloadTask task) { - T.showShort(SingleTaskActivity.this, "该下载链接不支持断点"); + if (task.getKey().equals(DOWNLOAD_URL)) { + T.showShort(SingleTaskActivity.this, "该下载链接不支持断点"); + } } @Override protected int setLayoutId() { @@ -195,7 +206,7 @@ public class SingleTaskActivity extends BaseActivity { mStart.setText("恢复"); mStart.setTextColor(getResources().getColor(android.R.color.holo_blue_light)); setBtState(true); - } else if (target.isDownloading()) { + } else if (target.isRunning()) { setBtState(false); } getBinding().setFileSize(target.getConvertFileSize()); @@ -208,10 +219,11 @@ public class SingleTaskActivity extends BaseActivity { break; case R.id.stop: Aria.download(this).load(DOWNLOAD_URL).stop(); + //startActivity(new Intent(this, SingleTaskActivity.class)); //Aria.download(this).load(DOWNLOAD_URL).removeRecord(); break; case R.id.cancel: - //Aria.download(this).load(DOWNLOAD_URL).cancel(); + Aria.download(this).load(DOWNLOAD_URL).cancel(); Aria.download(this).load(DOWNLOAD_URL).removeRecord(); break; } @@ -220,18 +232,16 @@ public class SingleTaskActivity extends BaseActivity { private void startD() { //Aria.get(this).setLogLevel(ALog.LOG_CLOSE); //Aria.download(this).load("aaaa.apk"); - Map map = new HashMap<>(); - map.put("User-Agent", - "Mozilla/5.0 (Linux; Android 4.4.4; Nexus 5 Build/KTU84P; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 Mobile MQQBrowser/6.2 TBS/043722 Safari/537.36"); - map.put("Cookie", - "BAIDUID=DFC7EF42C60AD1ACF0BA94389AA67F13:FG=1; H_WISE_SIDS=121192_104493_114745_121434_119046_100098_120212_121140_118882_118858_118850_118820_118792_121254_121534_121214_117588_117242_117431_119974_120597_121043_121422_120943_121175_121272_117552_120482_121013_119962_119145_120851_120841_120034_121325_116407_121109_120654_110085_120708; PSINO=7; BDORZ=AE84CDB3A529C0F8A2B9DCDD1D18B695"); Aria.download(SingleTaskActivity.this) .load(DOWNLOAD_URL) - //.addHeader("groupName", "value") - .addHeaders(map) - //.setRequestMode(RequestEnum.POST) - .setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/ggsg1.apk") - .resetState() + //.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("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0") + .useServerFileName(true) + .setRequestMode(RequestEnum.GET) + .setFilePath(Environment.getExternalStorageDirectory().getPath() + "/ggsg3.apk") + //.resetState() .start(); //.add(); } @@ -240,4 +250,9 @@ public class SingleTaskActivity extends BaseActivity { super.onDestroy(); //Aria.download(this).unRegister(); } + + @Override protected void onStop() { + super.onStop(); + //Aria.download(this).unRegister(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java b/app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java index 5fd45147..068ac47f 100644 --- a/app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java +++ b/app/src/main/java/com/arialyy/simple/download/fragment_download/DownloadFragment.java @@ -45,11 +45,11 @@ public class DownloadFragment extends AbsFragment { private static final String DOWNLOAD_URL = "https://res5.d.cn/2137e42d610b3488d9420c6421529386eee5bdbfd9be1fafe0a05d6dabaec8c156ddbd00581055bbaeac03904fb63310e80010680235d16bd4c040b50096a0c20dd1c4b0854529a1.apk"; @Override protected void init(Bundle savedInstanceState) { - if (Aria.download(this).taskExists(DOWNLOAD_URL)) { - DownloadTarget target = Aria.download(this).load(DOWNLOAD_URL); + if (Aria.download(getContext()).taskExists(DOWNLOAD_URL)) { + DownloadTarget target = Aria.download(getContext()).load(DOWNLOAD_URL); getBinding().setProgress(target.getPercent()); } - DownloadEntity entity = Aria.download(this).getDownloadEntity(DOWNLOAD_URL); + DownloadEntity entity = Aria.download(getContext()).getDownloadEntity(DOWNLOAD_URL); if (entity != null) { getBinding().setFileSize(CommonUtil.formatFileSize(entity.getFileSize())); int state = entity.getState(); @@ -57,22 +57,22 @@ public class DownloadFragment extends AbsFragment { } else { setBtState(true); } - Aria.download(this).register(); + Aria.download(getContext()).register(); } @OnClick({ R.id.start, R.id.stop, R.id.cancel }) public void onClick(View view) { switch (view.getId()) { case R.id.start: - Aria.download(this) + Aria.download(getContext()) .load(DOWNLOAD_URL) .setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/王者军团.apk") .start(); break; case R.id.stop: - Aria.download(this).load(DOWNLOAD_URL).pause(); + Aria.download(getContext()).load(DOWNLOAD_URL).pause(); break; case R.id.cancel: - Aria.download(this).load(DOWNLOAD_URL).cancel(); + Aria.download(getContext()).load(DOWNLOAD_URL).cancel(); break; } } diff --git a/app/src/main/java/com/arialyy/simple/download/group/ChildHandleDialog.java b/app/src/main/java/com/arialyy/simple/download/group/ChildHandleDialog.java index 0e16664d..3de6d4f7 100644 --- a/app/src/main/java/com/arialyy/simple/download/group/ChildHandleDialog.java +++ b/app/src/main/java/com/arialyy/simple/download/group/ChildHandleDialog.java @@ -62,13 +62,13 @@ import java.util.List; @Override protected void init(Bundle savedInstanceState) { super.init(savedInstanceState); - Aria.download(this).register(); + Aria.download(getContext()).register(); initWidget(); } @Override public void onDestroy() { super.onDestroy(); - Aria.download(this).unRegister(); + Aria.download(getContext()).unRegister(); } private void initWidget() { @@ -134,10 +134,10 @@ import java.util.List; @OnClick({ R.id.start, R.id.stop, R.id.cancel }) void onClick(View view) { switch (view.getId()) { case R.id.start: - Aria.download(this).load(mUrls).getSubTaskManager().startSubTask(mChildEntity.getUrl()); + Aria.download(getContext()).loadGroup(mUrls).getSubTaskManager().startSubTask(mChildEntity.getUrl()); break; case R.id.stop: - Aria.download(this).load(mUrls).getSubTaskManager().stopSubTask(mChildEntity.getUrl()); + Aria.download(getContext()).loadGroup(mUrls).getSubTaskManager().stopSubTask(mChildEntity.getUrl()); break; //case R.id.cancel: // Aria.download(this).load(mUrls).getSubTaskManager().cancelSubTask(mChildEntity.getUrl()); diff --git a/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java index 75397bd1..dc18256b 100644 --- a/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/group/DownloadGroupActivity.java @@ -17,6 +17,7 @@ package com.arialyy.simple.download.group; import android.os.Bundle; import android.os.Environment; +import android.util.Log; import android.view.View; import butterknife.Bind; import com.arialyy.annotations.DownloadGroup; @@ -45,10 +46,10 @@ public class DownloadGroupActivity extends BaseActivity getUrls() { + public List getUrls() { List urls = new ArrayList<>(); String[] str = getContext().getResources().getStringArray(R.array.group_urls_1); Collections.addAll(urls, str); @@ -44,7 +44,7 @@ public class GroupModule extends BaseModule { return urls; } - List getSubName(){ + List getSubName() { List names = new ArrayList<>(); String[] str = getContext().getResources().getStringArray(R.array.group_names); Collections.addAll(names, str); diff --git a/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java b/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java index 83b75caa..db4284ac 100644 --- a/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java +++ b/app/src/main/java/com/arialyy/simple/download/multi_download/DownloadAdapter.java @@ -217,9 +217,9 @@ public class DownloadAdapter extends AbsRVAdapter 0) { - holder.childList.updateChildProgress(((DownloadGroupEntity) entity).getSubTask()); + holder.childList.updateChildProgress(((DownloadGroupEntity) entity).getSubEntities()); } else { - holder.childList.addData(((DownloadGroupEntity) entity).getSubTask()); + holder.childList.addData(((DownloadGroupEntity) entity).getSubEntities()); } } diff --git a/app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java b/app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java index cd8bbf5d..667e5539 100644 --- a/app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java @@ -26,16 +26,12 @@ import butterknife.Bind; import com.arialyy.annotations.Download; import com.arialyy.annotations.DownloadGroup; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupTask; import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.frame.util.FileUtil; -import com.arialyy.frame.util.show.L; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.databinding.ActivityMultiDownloadBinding; -import java.io.File; import java.util.ArrayList; import java.util.List; @@ -55,7 +51,7 @@ public class MultiDownloadActivity extends BaseActivity temps = Aria.download(this).getTotleTaskList(); + List temps = Aria.download(this).getTotalTaskList(); if (temps != null && !temps.isEmpty()) { mData.addAll(temps); } diff --git a/app/src/main/java/com/arialyy/simple/test/AnyRunActivity.java b/app/src/main/java/com/arialyy/simple/test/AnyRunActivity.java new file mode 100644 index 00000000..f80bd566 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/test/AnyRunActivity.java @@ -0,0 +1,65 @@ +package com.arialyy.simple.test; + +import android.os.Bundle; +import android.view.View; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.util.ALog; +import com.arialyy.simple.R; +import com.arialyy.simple.base.BaseActivity; +import com.arialyy.simple.databinding.ActivityTestBinding; +import java.util.List; + +/** + * Created by laoyuyu on 2018/4/13. + */ + +public class AnyRunActivity extends BaseActivity { + AnyRunnModule module; + String[] urls; + int index = 0; + String URL = "http://static.gaoshouyou.com/d/12/0d/7f120f50c80d2e7b8c4ba24ece4f9cdd.apk"; + + @Override protected int setLayoutId() { + return R.layout.activity_test; + } + + @Override protected void init(Bundle savedInstanceState) { + super.init(savedInstanceState); + Aria.init(this); + mBar.setVisibility(View.GONE); + module = new AnyRunnModule(this); + urls = getResources().getStringArray(R.array.group_urls); + } + + public void onClick(View view) { + switch (view.getId()) { + case R.id.start: + //module.start(); + //if (index < urls.length) { + // module.start(urls[index]); + // index++; + //} + module.start(URL); + List list = Aria.download(this).getTotalTaskList(); + ALog.d(TAG, "size ==> " + list.size()); + break; + case R.id.stop: + //List list = Aria.download(this).getTotalTaskList(); + // + ////module.stop(); + //module.stop(URL); + + break; + case R.id.cancel: + module.cancel(URL); + //module.cancel(); + break; + } + } + + @Override protected void onDestroy() { + super.onDestroy(); + module.unRegister(); + } +} diff --git a/app/src/main/java/com/arialyy/simple/test/AnyRunnModule.java b/app/src/main/java/com/arialyy/simple/test/AnyRunnModule.java new file mode 100644 index 00000000..4b583b98 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/test/AnyRunnModule.java @@ -0,0 +1,88 @@ +package com.arialyy.simple.test; + +import android.content.Context; +import android.os.Environment; +import android.util.Log; +import com.arialyy.annotations.Download; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.common.RequestEnum; +import com.arialyy.aria.core.download.DownloadTask; +import com.arialyy.aria.util.CommonUtil; +import com.arialyy.frame.util.show.L; +import java.io.File; + +/** + * Created by laoyuyu on 2018/4/13. + */ + +public class AnyRunnModule { + String TAG = "AnyRunnModule"; + private Context mContext; + private String mUrl; + + public AnyRunnModule(Context context) { + Aria.download(this).register(); + mContext = context; + } + + @Download.onWait void onWait(DownloadTask task) { + Log.d(TAG, "wait ==> " + task.getDownloadEntity().getFileName()); + } + + @Download.onPre protected void onPre(DownloadTask task) { + Log.d(TAG, "onPre"); + } + + @Download.onTaskStart void taskStart(DownloadTask task) { + Log.d(TAG, "onStart"); + } + + @Download.onTaskRunning protected void running(DownloadTask task) { + Log.d(TAG, "running"); + } + + @Download.onTaskResume void taskResume(DownloadTask task) { + Log.d(TAG, "resume"); + } + + @Download.onTaskStop void taskStop(DownloadTask task) { + Log.d(TAG, "stop"); + } + + @Download.onTaskCancel void taskCancel(DownloadTask task) { + Log.d(TAG, "cancel"); + } + + @Download.onTaskFail void taskFail(DownloadTask task) { + Log.d(TAG, "fail"); + } + + @Download.onTaskComplete void taskComplete(DownloadTask task) { + L.d(TAG, "path ==> " + task.getDownloadEntity().getDownloadPath()); + L.d(TAG, "md5Code ==> " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); + } + + + void start(String url) { + mUrl = url; + Aria.download(this) + .load(url) + .addHeader("Accept-Encoding", "gzip") + .setRequestMode(RequestEnum.GET) + .setFilePath(Environment.getExternalStorageDirectory().getPath() + "/ggsg1234.apk") + .resetState() + .start(); + } + + void stop(String url) { + Aria.download(this).load(url).stop(); + } + + void cancel(String url) { + Aria.download(this).load(url).cancel(); + } + + void unRegister() { + Aria.download(this).unRegister(); + } +} diff --git a/app/src/main/java/com/arialyy/simple/test/TestActivity.java b/app/src/main/java/com/arialyy/simple/test/TestActivity.java new file mode 100644 index 00000000..01db7974 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/test/TestActivity.java @@ -0,0 +1,94 @@ +package com.arialyy.simple.test; + +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import com.arialyy.annotations.Upload; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.common.RequestEnum; +import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.util.CommonUtil; +import com.arialyy.simple.R; +import com.arialyy.simple.base.BaseActivity; +import com.arialyy.simple.databinding.ActivityTestBinding; +import java.io.File; + +/** + * Created by Administrator on 2018/4/12. + */ + +public class TestActivity extends BaseActivity { + String TAG = "TestActivity"; + //String URL = "http://58.210.9.131/tpk/sipgt//TDLYZTGH.tpk"; //chunked 下载 + //private final String URL = "ftp://192.168.1.3:21/download//AriaPrj.rar"; + private final String FILE_PATH = "/mnt/sdcard/SDK_Demo-release.apk"; + //private final String URL = "ftp://192.168.29.140:21/aa//你好"; + private final String URL = "http://192.168.29.140:5000/upload/"; + + @Override protected int setLayoutId() { + return R.layout.activity_test; + } + + @Override protected void init(Bundle savedInstanceState) { + super.init(savedInstanceState); + mBar.setVisibility(View.GONE); + Aria.upload(this).register(); + + } + + @Upload.onWait void onWait(UploadTask task) { + Log.d(TAG, "wait ==> " + task.getEntity().getFileName()); + } + + @Upload.onPre protected void onPre(UploadTask task) { + Log.d(TAG, "onPre"); + } + + @Upload.onTaskStart void taskStart(UploadTask task) { + Log.d(TAG, "onStart"); + } + + @Upload.onTaskRunning protected void running(UploadTask task) { + Log.d(TAG, "running"); + } + + @Upload.onTaskResume void taskResume(UploadTask task) { + Log.d(TAG, "resume"); + } + + @Upload.onTaskStop void taskStop(UploadTask task) { + Log.d(TAG, "stop"); + } + + @Upload.onTaskCancel void taskCancel(UploadTask task) { + Log.d(TAG, "cancel"); + } + + @Upload.onTaskFail void taskFail(UploadTask task) { + Log.d(TAG, "fail"); + } + + @Upload.onTaskComplete void taskComplete(UploadTask task) { + Log.d(TAG, "complete, md5 => " + CommonUtil.getFileMD5(new File(task.getKey()))); + } + + public void onClick(View view) { + switch (view.getId()) { + case R.id.start: + Aria.upload(this) + .load(FILE_PATH) + .setUploadUrl(URL) + .setRequestMode(RequestEnum.POST) + .setExtendField("韩寒哈大双") + .setAttachment("file") + .start(); + break; + case R.id.stop: + Aria.upload(this).load(FILE_PATH).stop(); + break; + case R.id.cancel: + Aria.upload(this).load(FILE_PATH).cancel(); + break; + } + } +} diff --git a/app/src/main/java/com/arialyy/simple/test/TestGroupActivity.java b/app/src/main/java/com/arialyy/simple/test/TestGroupActivity.java new file mode 100644 index 00000000..d8856485 --- /dev/null +++ b/app/src/main/java/com/arialyy/simple/test/TestGroupActivity.java @@ -0,0 +1,100 @@ +package com.arialyy.simple.test; + +import android.os.Bundle; +import android.os.Environment; +import android.view.View; +import com.arialyy.annotations.DownloadGroup; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.download.DownloadGroupTask; +import com.arialyy.frame.util.show.L; +import com.arialyy.simple.R; +import com.arialyy.simple.base.BaseActivity; +import com.arialyy.simple.databinding.ActivityTestBinding; +import com.arialyy.simple.download.group.GroupModule; +import java.util.List; + +/** + * Created by Administrator on 2018/4/12. + */ + +public class TestGroupActivity extends BaseActivity { + List mUrls; + private static final String dir = "ftp://192.168.1.8:21/upload/测试"; + + @Override protected int setLayoutId() { + return R.layout.activity_test; + } + + @Override protected void init(Bundle savedInstanceState) { + super.init(savedInstanceState); + mBar.setVisibility(View.GONE); + Aria.download(this).register(); + mUrls = getModule(GroupModule.class).getUrls(); + } + + @DownloadGroup.onWait void taskWait(DownloadGroupTask task) { + L.d(TAG, task.getTaskName() + "wait"); + } + + @DownloadGroup.onPre() protected void onPre(DownloadGroupTask task) { + L.d(TAG, "group pre"); + } + + @DownloadGroup.onTaskPre() protected void onTaskPre(DownloadGroupTask task) { + L.d(TAG, "group task pre"); + } + + @DownloadGroup.onTaskStart() void taskStart(DownloadGroupTask task) { + L.d(TAG, "group task start"); + } + + @DownloadGroup.onTaskRunning() protected void running(DownloadGroupTask task) { + L.d(TAG, "group task running"); + } + + @DownloadGroup.onTaskResume() void taskResume(DownloadGroupTask task) { + L.d(TAG, "group task resume"); + } + + @DownloadGroup.onTaskStop() void taskStop(DownloadGroupTask task) { + L.d(TAG, "group task stop"); + } + + @DownloadGroup.onTaskCancel() void taskCancel(DownloadGroupTask task) { + L.d(TAG, "group task cancel"); + } + + @DownloadGroup.onTaskFail() void taskFail(DownloadGroupTask task) { + L.d(TAG, "group task fail"); + } + + @DownloadGroup.onTaskComplete() void taskComplete(DownloadGroupTask task) { + L.d(TAG, "group task complete"); + } + + public void onClick(View view) { + switch (view.getId()) { + case R.id.start: + //Aria.download(this) + // .loadGroup(mUrls) + // .setDirPath(Environment.getExternalStorageDirectory().getPath() + "/download/test/") + // .resetState() + // .start(); + Aria.download(this) + .loadFtpDir(dir) + .setDirPath( + Environment.getExternalStorageDirectory().getPath() + "/Download/ftp_dir") + .setGroupAlias("ftp文件夹下载") + //.setSubTaskFileName(getModule(GroupModule.class).getSubName()) + .login("lao", "123456") + .start(); + break; + case R.id.stop: + Aria.download(this).loadFtpDir(dir).stop(); + break; + case R.id.cancel: + Aria.download(this).loadFtpDir(dir).cancel(); + break; + } + } +} diff --git a/app/src/main/java/com/arialyy/simple/upload/FtpUploadActivity.java b/app/src/main/java/com/arialyy/simple/upload/FtpUploadActivity.java index 368e9131..21f8927c 100644 --- a/app/src/main/java/com/arialyy/simple/upload/FtpUploadActivity.java +++ b/app/src/main/java/com/arialyy/simple/upload/FtpUploadActivity.java @@ -35,8 +35,8 @@ import java.io.File; * Ftp 文件上传demo */ public class FtpUploadActivity extends BaseActivity { - private final String FILE_PATH = "/mnt/sdcard/AriaPrj.zip"; - private final String URL = "ftp://192.168.1.6:21/aa/你好"; + private final String FILE_PATH = "/mnt/sdcard/sql.rar"; + private final String URL = "ftp://192.168.1.7:21/aa//你好"; @Override protected void init(Bundle savedInstanceState) { setTile("D_FTP 文件上传"); diff --git a/app/src/main/java/com/arialyy/simple/upload/HttpUploadActivity.java b/app/src/main/java/com/arialyy/simple/upload/HttpUploadActivity.java index 92089de2..c0b942c7 100644 --- a/app/src/main/java/com/arialyy/simple/upload/HttpUploadActivity.java +++ b/app/src/main/java/com/arialyy/simple/upload/HttpUploadActivity.java @@ -21,6 +21,7 @@ import butterknife.Bind; import butterknife.OnClick; import com.arialyy.annotations.Upload; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.upload.UploadTask; import com.arialyy.frame.util.FileUtil; import com.arialyy.frame.util.show.L; @@ -37,7 +38,7 @@ public class HttpUploadActivity extends BaseActivity { private static final String TAG = "HttpUploadActivity"; @Bind(R.id.pb) HorizontalProgressBarWithNumber mPb; - private static final String FILE_PATH = "/mnt/sdcard/test.txt"; + private static final String FILE_PATH = "/mnt/sdcard/test.apk"; @Override protected int setLayoutId() { return R.layout.activity_upload; @@ -51,10 +52,11 @@ public class HttpUploadActivity extends BaseActivity { @OnClick(R.id.upload) void upload() { Aria.upload(HttpUploadActivity.this).load(FILE_PATH) - //.setUploadUrl( - // "http://lib-test.xzxyun.com:8042/Api/upload?data={\"type\":\"1\",\"fileType\":\".txt\"}") - .setUploadUrl("http://192.168.1.6:8080/upload/sign_file/").setAttachment("file") + .setUploadUrl( + "http://lib-test.xzxyun.com:8042/Api/upload?data={\"type\":\"1\",\"fileType\":\".apk\"}") + //.setUploadUrl("http://192.168.1.6:8080/upload/sign_file/").setAttachment("file") //.addHeader("iplanetdirectorypro", "11a09102fb934ad0bc206f9c611d7933") + .setRequestMode(RequestEnum.POST) .start(); } diff --git a/app/src/main/res/layout/activity_test.xml b/app/src/main/res/layout/activity_test.xml new file mode 100644 index 00000000..b5b4cf62 --- /dev/null +++ b/app/src/main/res/layout/activity_test.xml @@ -0,0 +1,40 @@ + + + + + + +