diff --git a/.idea/misc.xml b/.idea/misc.xml
index fbb68289..5d199810 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -37,7 +37,7 @@
-
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
index b0a774eb..5f6de159 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -3,7 +3,9 @@
+
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1ddf..00000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Aria/build.gradle b/Aria/build.gradle
index 4aae21af..b6e5869b 100644
--- a/Aria/build.gradle
+++ b/Aria/build.gradle
@@ -7,8 +7,8 @@ android {
defaultConfig {
minSdkVersion 9
targetSdkVersion 23
- versionCode 85
- versionName "2.3.7"
+ versionCode 86
+ versionName "2.4.2"
}
buildTypes {
release {
diff --git a/Aria/src/main/java/com/arialyy/aria/core/AMReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/AMReceiver.java
index f6d3e804..5ae43a1a 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/AMReceiver.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/AMReceiver.java
@@ -15,9 +15,7 @@
*/
package com.arialyy.aria.core;
-import android.os.Build;
import android.support.annotation.NonNull;
-import android.text.TextUtils;
import com.arialyy.aria.core.scheduler.DownloadSchedulers;
import com.arialyy.aria.core.scheduler.OnSchedulerListener;
import com.arialyy.aria.util.CheckUtil;
diff --git a/Aria/src/main/java/com/arialyy/aria/core/AMUplodReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/AMUplodReceiver.java
new file mode 100644
index 00000000..981c5e6d
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/AMUplodReceiver.java
@@ -0,0 +1,8 @@
+package com.arialyy.aria.core;
+
+/**
+ * Created by Aria.Lao on 2017/1/18.
+ * AM 上传文件接收器
+ */
+public class AMUplodReceiver {
+}
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 4aab85f2..b3c5a6c9 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/Aria.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/Aria.java
@@ -28,6 +28,7 @@ import android.os.Build;
import android.widget.PopupWindow;
import com.arialyy.aria.core.scheduler.OnSchedulerListener;
import com.arialyy.aria.core.task.Task;
+import com.arialyy.aria.util.CheckUtil;
/**
* Created by lyy on 2016/12/1.
@@ -98,12 +99,12 @@ import com.arialyy.aria.core.task.Task;
private Aria() {
}
+
/**
* 接受Activity、Service、Application
*/
public static AMReceiver whit(Context context) {
- //if (context == null) throw new IllegalArgumentException("context 不能为 null");
- checkNull(context);
+ CheckUtil.checkNull(context);
if (context instanceof Activity
|| context instanceof Service
|| context instanceof Application) {
@@ -117,7 +118,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Fragment
*/
public static AMReceiver whit(Fragment fragment) {
- checkNull(fragment);
+ CheckUtil.checkNull(fragment);
return AriaManager.getInstance(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? fragment.getContext()
: fragment.getActivity()).get(fragment);
@@ -127,7 +128,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Fragment
*/
public static AMReceiver whit(android.support.v4.app.Fragment fragment) {
- checkNull(fragment);
+ CheckUtil.checkNull(fragment);
return AriaManager.getInstance(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? fragment.getContext()
: fragment.getActivity()).get(fragment);
@@ -137,7 +138,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Fragment、或者DialogFragment
*/
public static AMReceiver whit(DialogFragment dialog) {
- checkNull(dialog);
+ CheckUtil.checkNull(dialog);
return AriaManager.getInstance(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? dialog.getContext() : dialog.getActivity())
.get(dialog);
@@ -147,7 +148,7 @@ import com.arialyy.aria.core.task.Task;
* 处理popupwindow
*/
public static AMReceiver whit(PopupWindow popupWindow) {
- checkNull(popupWindow);
+ CheckUtil.checkNull(popupWindow);
return AriaManager.getInstance(popupWindow.getContentView().getContext()).get(popupWindow);
}
@@ -155,7 +156,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Dialog
*/
public static AMReceiver whit(Dialog dialog) {
- checkNull(dialog);
+ CheckUtil.checkNull(dialog);
return AriaManager.getInstance(dialog.getContext()).get(dialog);
}
@@ -177,7 +178,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Dialog的通用任务
*/
public static AriaManager get(Dialog dialog) {
- checkNull(dialog);
+ CheckUtil.checkNull(dialog);
return AriaManager.getInstance(dialog.getContext());
}
@@ -185,7 +186,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Dialog的通用任务
*/
public static AriaManager get(PopupWindow popupWindow) {
- checkNull(popupWindow);
+ CheckUtil.checkNull(popupWindow);
return AriaManager.getInstance(popupWindow.getContentView().getContext());
}
@@ -193,7 +194,7 @@ import com.arialyy.aria.core.task.Task;
* 处理Fragment的通用任务
*/
public static AriaManager get(Fragment fragment) {
- checkNull(fragment);
+ CheckUtil.checkNull(fragment);
return AriaManager.getInstance(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? fragment.getContext()
: fragment.getActivity());
@@ -203,16 +204,12 @@ import com.arialyy.aria.core.task.Task;
* 处理Fragment的通用任务
*/
public static AriaManager get(android.support.v4.app.Fragment fragment) {
- checkNull(fragment);
+ CheckUtil.checkNull(fragment);
return AriaManager.getInstance(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? fragment.getContext()
: fragment.getActivity());
}
- private static void checkNull(Object obj) {
- if (obj == null) throw new IllegalArgumentException("不能传入空对象");
- }
-
public static class SimpleSchedulerListener implements OnSchedulerListener {
@Override public void onTaskPre(Task task) {
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 b02cdceb..c7062c66 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java
@@ -30,6 +30,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.widget.PopupWindow;
import com.arialyy.aria.core.command.CmdFactory;
+import com.arialyy.aria.util.CAConfiguration;
import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.core.command.IDownloadCmd;
@@ -73,6 +74,24 @@ import java.util.Set;
return getTarget(obj);
}
+ /**
+ * 设置CA证书信息
+ *
+ * @param caAlias ca证书别名
+ * @param caPath assets 文件夹下的ca证书完整路径
+ */
+ public void setCAInfo(String caAlias, String caPath) {
+ if (TextUtils.isEmpty(caAlias)) {
+ Log.e(TAG, "ca证书别名不能为null");
+ return;
+ } else if (TextUtils.isEmpty(caPath)) {
+ Log.e(TAG, "ca证书路径不能为null");
+ return;
+ }
+ CAConfiguration.CA_ALIAS = caAlias;
+ CAConfiguration.CA_PATH = caPath;
+ }
+
/**
* 获取下载列表
*/
@@ -100,7 +119,7 @@ import java.util.Set;
*/
public void stopAllTask() {
List allEntity = mManager.getAllDownloadEntity();
- List stopCmds = new ArrayList<>();
+ List stopCmds = new ArrayList<>();
for (DownloadEntity entity : allEntity) {
if (entity.getState() == DownloadEntity.STATE_DOWNLOAD_ING) {
stopCmds.add(CommonUtil.createCmd(entity, CmdFactory.TASK_STOP));
@@ -159,8 +178,8 @@ import java.util.Set;
* 删除所有任务
*/
public void cancelAllTask() {
- List allEntity = mManager.getAllDownloadEntity();
- List cancelCmds = new ArrayList<>();
+ List allEntity = mManager.getAllDownloadEntity();
+ List cancelCmds = new ArrayList<>();
for (DownloadEntity entity : allEntity) {
cancelCmds.add(CommonUtil.createCmd(entity, CmdFactory.TASK_CANCEL));
}
@@ -174,9 +193,9 @@ import java.util.Set;
}
private AMReceiver putTarget(Object obj) {
- String clsName = obj.getClass().getName();
- AMReceiver target = null;
- String key = "";
+ String clsName = obj.getClass().getName();
+ AMReceiver target = null;
+ String key = "";
if (!(obj instanceof Activity)) {
if (obj instanceof android.support.v4.app.Fragment) {
key = clsName + "_" + ((Fragment) obj).getActivity().getClass().getName();
@@ -198,8 +217,15 @@ import java.util.Set;
key = clsName;
}
handlePopupWindowLift((PopupWindow) obj);
+ } else if (obj instanceof Service) {
+ key = clsName;
+ } else if (obj instanceof Application) {
+ key = clsName;
}
- } else {
+ }
+ if (obj instanceof Activity || obj instanceof Service) {
+ key = clsName;
+ } else if (obj instanceof Application) {
key = clsName;
}
if (TextUtils.isEmpty(key)) {
@@ -249,12 +275,12 @@ import java.util.Set;
*/
private void handleDialogLift(Dialog dialog) {
try {
- Field dismissField = CommonUtil.getField(dialog.getClass(), "mDismissMessage");
- Message dismissMsg = (Message) dismissField.get(dialog);
+ Field dismissField = CommonUtil.getField(dialog.getClass(), "mDismissMessage");
+ Message dismissMsg = (Message) dismissField.get(dialog);
//如果Dialog已经设置Dismiss事件,则查找cancel事件
if (dismissMsg != null) {
- Field cancelField = CommonUtil.getField(dialog.getClass(), "mCancelMessage");
- Message cancelMsg = (Message) cancelField.get(dialog);
+ Field cancelField = CommonUtil.getField(dialog.getClass(), "mCancelMessage");
+ Message cancelMsg = (Message) cancelField.get(dialog);
if (cancelMsg != null) {
Log.e(TAG, "你已经对Dialog设置了Dismiss和cancel事件。为了防止内存泄露,"
+ "请在dismiss方法中调用Aria.whit(this).removeSchedulerListener();来注销事件");
@@ -316,12 +342,12 @@ import java.util.Set;
* onDestroy
*/
private void destroySchedulerListener(Object obj) {
- Set keys = mTargets.keySet();
- String clsName = obj.getClass().getName();
+ Set keys = mTargets.keySet();
+ String clsName = obj.getClass().getName();
for (Iterator> iter = mTargets.entrySet().iterator();
iter.hasNext(); ) {
Map.Entry entry = iter.next();
- String key = entry.getKey();
+ String key = entry.getKey();
if (key.equals(clsName) || key.contains(clsName)) {
AMReceiver receiver = mTargets.get(key);
if (receiver.obj != null) {
diff --git a/Aria/src/main/java/com/arialyy/aria/core/RequestEnum.java b/Aria/src/main/java/com/arialyy/aria/core/RequestEnum.java
new file mode 100644
index 00000000..43423f82
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/RequestEnum.java
@@ -0,0 +1,16 @@
+package com.arialyy.aria.core;
+
+/**
+ * Created by Aria.Lao on 2017/1/23.
+ */
+
+public enum RequestEnum {
+ GET("GET"), POST("POST");
+
+ String name;
+
+ RequestEnum(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/TaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/TaskEntity.java
new file mode 100644
index 00000000..0cc6be80
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/TaskEntity.java
@@ -0,0 +1,10 @@
+package com.arialyy.aria.core;
+
+/**
+ * Created by Aria.Lao on 2017/1/23.
+ * 任务实体
+ */
+public class TaskEntity {
+ public DownloadEntity downloadEntity;
+ public RequestEnum requestEnum = RequestEnum.GET;
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java
index 06228179..de033a65 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java
@@ -33,9 +33,9 @@ import com.arialyy.aria.util.Configuration;
* 下载任务队列
*/
public class DownloadTaskQueue implements ITaskQueue {
- private static final String TAG = "DownloadTaskQueue";
- private CachePool mCachePool = CachePool.getInstance();
- private ExecutePool mExecutePool = ExecutePool.getInstance();
+ private static final String TAG = "DownloadTaskQueue";
+ private CachePool mCachePool = CachePool.getInstance();
+ private ExecutePool mExecutePool = ExecutePool.getInstance();
private Context mContext;
//private IDownloadSchedulers mSchedulers;
@@ -89,21 +89,15 @@ public class DownloadTaskQueue implements ITaskQueue {
@Override public void stopTask(Task task) {
if (!task.isDownloading()) Log.w(TAG, "停止任务失败,【任务已经停止】");
- task.stop();
- //if (task.isDownloading()) {
- // if (mExecutePool.removeTask(task)) {
- // task.stop();
- // }
- //} else {
- // task.stop();
- // Log.w(TAG, "停止任务失败,【任务已经停止】");
- //}
+ if (mExecutePool.removeTask(task)) {
+ task.stop();
+ } else {
+ task.stop();
+ Log.w(TAG, "停止任务失败,【任务已经停止】");
+ }
}
@Override public void cancelTask(Task task) {
- //if (mExecutePool.removeTask(task) || mCachePool.removeTask(task)) {
- // task.cancel();
- //}
task.cancel();
}
@@ -119,10 +113,6 @@ public class DownloadTaskQueue implements ITaskQueue {
}
}
- //@Override public IDownloadSchedulers getDownloadSchedulers() {
- // return mSchedulers;
- //}
-
@Override public int size() {
return mExecutePool.size();
}
@@ -159,12 +149,10 @@ public class DownloadTaskQueue implements ITaskQueue {
@Override public Task createTask(String target, DownloadEntity entity) {
Task task;
if (TextUtils.isEmpty(target)) {
- //task = TaskFactory.getInstance().createTask(mContext, entity, mSchedulers);
task =
TaskFactory.getInstance().createTask(mContext, entity, DownloadSchedulers.getInstance());
} else {
task = TaskFactory.getInstance()
- //.createTask(target.getClass().getName(), mContext, entity, mSchedulers);
.createTask(target, mContext, entity, DownloadSchedulers.getInstance());
}
mCachePool.putTask(task);
@@ -183,13 +171,10 @@ public class DownloadTaskQueue implements ITaskQueue {
Task task = mExecutePool.getTask(entity.getDownloadUrl());
if (task != null) {
Log.d(TAG, "从执行池删除任务,删除" + (mExecutePool.removeTask(task) ? "成功" : "失败"));
- } else {
- task = mCachePool.getTask(entity.getDownloadUrl());
}
+ task = mCachePool.getTask(entity.getDownloadUrl());
if (task != null) {
Log.d(TAG, "从缓存池删除任务,删除" + (mCachePool.removeTask(task) ? "成功" : "失败"));
- } else {
- Log.w(TAG, "没有找到下载链接为【" + entity.getDownloadUrl() + "】的任务");
}
}
@@ -197,29 +182,15 @@ public class DownloadTaskQueue implements ITaskQueue {
return mCachePool.pollTask();
}
- //@Override public void setScheduler(IDownloadSchedulers schedulers) {
- // mSchedulers = schedulers;
- //}
-
public static class Builder {
Context context;
- IDownloadSchedulers schedulers;
public Builder(Context context) {
this.context = context.getApplicationContext();
}
- //public Builder setDownloadSchedulers(IDownloadSchedulers schedulers) {
- // this.schedulers = schedulers;
- // return this;
- //}
-
public DownloadTaskQueue build() {
DownloadTaskQueue queue = new DownloadTaskQueue(context);
- //if (schedulers == null) {
- // schedulers = DownloadSchedulers.getInstance();
- //}
- //queue.setScheduler(schedulers);
return queue;
}
}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java
index 73163766..68576119 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java
@@ -73,11 +73,4 @@ public interface ITaskQueue extends IDownloader {
* @return 下载任务 or null
*/
public Task getNextTask();
-
- ///**
- // * 设置下载调度器
- // *
- // * @param schedulers 下载调度器{@link IDownloadSchedulers}
- // */
- //public void setScheduler(IDownloadSchedulers schedulers);
}
\ No newline at end of file
diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadSchedulers.java
index 5f5b8a4e..0e062e03 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadSchedulers.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/DownloadSchedulers.java
@@ -16,6 +16,7 @@
package com.arialyy.aria.core.scheduler;
+import android.os.CountDownTimer;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
@@ -37,44 +38,44 @@ public class DownloadSchedulers implements IDownloadSchedulers {
/**
* 任务预加载
*/
- public static final int PRE = 0;
+ public static final int PRE = 0;
/**
* 任务开始
*/
- public static final int START = 1;
+ public static final int START = 1;
/**
* 任务停止
*/
- public static final int STOP = 2;
+ public static final int STOP = 2;
/**
* 任务失败
*/
- public static final int FAIL = 3;
+ public static final int FAIL = 3;
/**
* 任务取消
*/
- public static final int CANCEL = 4;
+ public static final int CANCEL = 4;
/**
* 任务完成
*/
- public static final int COMPLETE = 5;
+ public static final int COMPLETE = 5;
/**
* 下载中
*/
- public static final int RUNNING = 6;
+ public static final int RUNNING = 6;
/**
* 恢复下载
*/
- public static final int RESUME = 7;
- private static final String TAG = "DownloadSchedulers";
- private static final Object LOCK = new Object();
+ public static final int RESUME = 7;
+ private static final String TAG = "DownloadSchedulers";
+ private static final Object LOCK = new Object();
private static volatile DownloadSchedulers INSTANCE = null;
/**
* 下载器任务监听
*/
Map mSchedulerListeners = new ConcurrentHashMap<>();
- DownloadManager mManager = DownloadManager.getInstance();
+ DownloadManager mManager = DownloadManager.getInstance();
ITaskQueue mQueue;
private DownloadSchedulers() {
@@ -120,16 +121,16 @@ public class DownloadSchedulers implements IDownloadSchedulers {
case STOP:
case CANCEL:
mQueue.removeTask(entity);
- mQueue.removeTask(entity);
- if (mQueue.size() != Configuration.getInstance().getDownloadNum()) {
+ if (mQueue.size() < Configuration.getInstance().getDownloadNum()) {
startNextTask(entity);
}
break;
case COMPLETE:
+ mQueue.removeTask(entity);
startNextTask(entity);
break;
case FAIL:
- handleFailTask(entity);
+ handleFailTask(task);
break;
}
return true;
@@ -154,10 +155,8 @@ public class DownloadSchedulers implements IDownloadSchedulers {
private void callback(int state, Task task, OnSchedulerListener listener) {
if (listener != null) {
- //Task task = mQueue.getTask(entity);
if (task == null) {
- //Log.e(TAG, "队列中没有下载链接【" + entity.getDownloadUrl() + "】的任务");
- Log.e(TAG, "传递的下载任务");
+ Log.e(TAG, "TASK 为null,回调失败");
return;
}
switch (state) {
@@ -192,12 +191,17 @@ public class DownloadSchedulers implements IDownloadSchedulers {
/**
* 处理下载任务下载失败的情形
*
- * @param entity 失败实体
+ * @param task 下载任务
*/
- @Override public void handleFailTask(final DownloadEntity entity) {
- new Thread(new Runnable() {
- @Override public void run() {
- final Configuration config = Configuration.getInstance();
+ @Override public void handleFailTask(final Task task) {
+ final Configuration config = Configuration.getInstance();
+ CountDownTimer timer = new CountDownTimer(config.getReTryInterval(), 1000) {
+ @Override public void onTick(long millisUntilFinished) {
+
+ }
+
+ @Override public void onFinish() {
+ DownloadEntity entity = task.getDownloadEntity();
if (entity.getFailNum() <= config.getReTryNum()) {
Task task = mQueue.getTask(entity);
mQueue.reTryStart(task);
@@ -207,10 +211,12 @@ public class DownloadSchedulers implements IDownloadSchedulers {
e.printStackTrace();
}
} else {
+ mQueue.removeTask(entity);
startNextTask(entity);
}
}
- }).start();
+ };
+ timer.start();
}
/**
@@ -219,7 +225,6 @@ public class DownloadSchedulers implements IDownloadSchedulers {
* @param entity 通过Handler传递的下载实体
*/
@Override public void startNextTask(DownloadEntity entity) {
- mQueue.removeTask(entity);
Task newTask = mQueue.getNextTask();
if (newTask == null) {
Log.w(TAG, "没有下一任务");
diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/IDownloadSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/IDownloadSchedulers.java
index 5b38ceb5..3e75061f 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/IDownloadSchedulers.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/IDownloadSchedulers.java
@@ -18,6 +18,7 @@ package com.arialyy.aria.core.scheduler;
import android.os.Handler;
import com.arialyy.aria.core.DownloadEntity;
+import com.arialyy.aria.core.task.Task;
/**
* Created by “AriaLyy@outlook.com” on 2016/11/2.
@@ -42,9 +43,9 @@ public interface IDownloadSchedulers extends Handler.Callback {
/**
* 处理下载任务下载失败的情形
*
- * @param entity 下载实体
+ * @param task 下载任务
*/
- public void handleFailTask(DownloadEntity entity);
+ public void handleFailTask(Task task);
/**
* 启动下一个任务,条件:任务停止,取消下载,任务完成
diff --git a/Aria/src/main/java/com/arialyy/aria/core/task/ConnectionHelp.java b/Aria/src/main/java/com/arialyy/aria/core/task/ConnectionHelp.java
new file mode 100644
index 00000000..38ee2867
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/task/ConnectionHelp.java
@@ -0,0 +1,75 @@
+/*
+ * 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.task;
+
+import com.arialyy.aria.util.CAConfiguration;
+import com.arialyy.aria.util.SSLContextUtil;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.net.URLConnection;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Created by lyy on 2017/1/18.
+ * 链接帮助类
+ */
+class ConnectionHelp {
+ /**
+ * 处理链接
+ *
+ * @throws IOException
+ */
+ static HttpURLConnection handleConnection(URL url) throws IOException {
+ HttpURLConnection conn;
+ URLConnection urlConn = url.openConnection();
+ if (urlConn instanceof HttpsURLConnection) {
+ conn = (HttpsURLConnection) urlConn;
+ SSLContext sslContext =
+ SSLContextUtil.getSSLContext(CAConfiguration.CA_ALIAS, CAConfiguration.CA_ALIAS);
+ if (sslContext == null) {
+ sslContext = SSLContextUtil.getDefaultSLLContext();
+ }
+ SSLSocketFactory ssf = sslContext.getSocketFactory();
+ ((HttpsURLConnection) conn).setSSLSocketFactory(ssf);
+ ((HttpsURLConnection) conn).setHostnameVerifier(SSLContextUtil.HOSTNAME_VERIFIER);
+ } else {
+ conn = (HttpURLConnection) urlConn;
+ }
+ return conn;
+ }
+
+ /**
+ * 设置头部参数
+ *
+ * @throws ProtocolException
+ */
+ static HttpURLConnection setConnectParam(HttpURLConnection conn) throws ProtocolException {
+ conn.setRequestMethod("GET");
+ conn.setRequestProperty("Charset", "UTF-8");
+ conn.setRequestProperty("User-Agent",
+ "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
+ conn.setRequestProperty("Accept",
+ "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
+ ////用于处理Disconnect 不起作用问题
+ //conn.setRequestProperty("Connection", "close");
+ conn.setRequestProperty("Connection", "Keep-Alive");
+ return conn;
+ }
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/task/DownloadStateConstance.java b/Aria/src/main/java/com/arialyy/aria/core/task/DownloadStateConstance.java
new file mode 100644
index 00000000..00da093b
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/task/DownloadStateConstance.java
@@ -0,0 +1,76 @@
+/*
+ * 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.task;
+
+/**
+ * Created by lyy on 2017/1/18.
+ * 下载状态常量
+ */
+final class DownloadStateConstance {
+ int CANCEL_NUM = 0;
+ int STOP_NUM = 0;
+ int FAIL_NUM = 0;
+ int CONNECT_TIME_OUT = 5000 * 4; //连接超时时间
+ int READ_TIME_OUT = 1000 * 20; //流读取的超时时间
+ int COMPLETE_THREAD_NUM = 0;
+ int THREAD_NUM = 3;
+ long CURRENT_LOCATION = 0;
+ boolean isDownloading = false;
+ boolean isCancel = false;
+ boolean isStop = false;
+
+ DownloadStateConstance(int num) {
+ THREAD_NUM = num;
+ }
+
+ void cleanState() {
+ isCancel = false;
+ isStop = false;
+ isDownloading = true;
+ CURRENT_LOCATION = 0;
+ CANCEL_NUM = 0;
+ STOP_NUM = 0;
+ FAIL_NUM = 0;
+ }
+
+ /**
+ * 所有子线程是否都已经停止下载
+ */
+ boolean isStop() {
+ return STOP_NUM == THREAD_NUM;
+ }
+
+ /**
+ * 所有子线程是否都已经下载失败
+ */
+ boolean isFail() {
+ return FAIL_NUM == THREAD_NUM;
+ }
+
+ /**
+ * 所有子线程是否都已经完成下载
+ */
+ boolean isComplete() {
+ return COMPLETE_THREAD_NUM == THREAD_NUM;
+ }
+
+ /**
+ * 所有子线程是否都已经取消下载
+ */
+ boolean isCancel() {
+ return CANCEL_NUM == THREAD_NUM;
+ }
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/task/DownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/task/DownloadUtil.java
index c69ee94a..6f14d883 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/task/DownloadUtil.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/task/DownloadUtil.java
@@ -20,14 +20,12 @@ import android.content.Context;
import android.util.Log;
import android.util.SparseArray;
import com.arialyy.aria.core.DownloadEntity;
+import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.CommonUtil;
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.ProtocolException;
import java.net.URL;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
@@ -37,9 +35,8 @@ import java.util.concurrent.Executors;
* Created by lyy on 2015/8/25.
* 下载工具类
*/
-final class DownloadUtil implements IDownloadUtil, Runnable {
- private static final String TAG = "DownloadUtil";
- private static final Object LOCK = new Object();
+public class DownloadUtil implements IDownloadUtil, Runnable {
+ private static final String TAG = "DownloadUtil";
/**
* 线程数
*/
@@ -48,27 +45,17 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
private IDownloadListener mListener;
private int mConnectTimeOut = 5000 * 4; //连接超时时间
private int mReadTimeOut = 5000 * 20; //流读取的超时时间
- /**
- * 已经完成下载任务的线程数量
- */
- private boolean isDownloading = false;
- private boolean isStop = false;
- private boolean isCancel = false;
private boolean isNewTask = true;
private boolean isSupportBreakpoint = true;
- private int mCompleteThreadNum = 0;
- private int mCancelNum = 0;
- private long mCurrentLocation = 0;
- private int mStopNum = 0;
- private int mFailNum = 0;
private Context mContext;
private DownloadEntity mDownloadEntity;
private ExecutorService mFixedThreadPool;
private File mDownloadFile; //下载的文件
private File mConfigFile;//下载信息配置文件
private SparseArray mTask = new SparseArray<>();
+ private DownloadStateConstance mConstance;
- DownloadUtil(Context context, DownloadEntity entity, IDownloadListener downloadListener) {
+ public DownloadUtil(Context context, DownloadEntity entity, IDownloadListener downloadListener) {
this(context, entity, downloadListener, 3);
}
@@ -79,6 +66,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
mListener = downloadListener;
THREAD_NUM = threadNum;
mFixedThreadPool = Executors.newFixedThreadPool(Integer.MAX_VALUE);
+ mConstance = new DownloadStateConstance(THREAD_NUM);
init();
}
@@ -122,19 +110,19 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
* 获取当前下载位置
*/
@Override public long getCurrentLocation() {
- return mCurrentLocation;
+ return mConstance.CURRENT_LOCATION;
}
@Override public boolean isDownloading() {
- return isDownloading;
+ return mConstance.isDownloading;
}
/**
* 取消下载
*/
@Override public void cancelDownload() {
- isCancel = true;
- isDownloading = false;
+ mConstance.isCancel = true;
+ mConstance.isDownloading = false;
mFixedThreadPool.shutdown();
for (int i = 0; i < THREAD_NUM; i++) {
SingleThreadTask task = (SingleThreadTask) mTask.get(i);
@@ -148,8 +136,8 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
* 停止下载
*/
@Override public void stopDownload() {
- isStop = true;
- isDownloading = false;
+ mConstance.isStop = true;
+ mConstance.isDownloading = false;
mFixedThreadPool.shutdown();
for (int i = 0; i < THREAD_NUM; i++) {
SingleThreadTask task = (SingleThreadTask) mTask.get(i);
@@ -189,13 +177,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
* 多线程断点续传下载文件,开始下载
*/
@Override public void startDownload() {
- isDownloading = true;
- mCurrentLocation = 0;
- isStop = false;
- isCancel = false;
- mCancelNum = 0;
- mStopNum = 0;
- mFailNum = 0;
+ mConstance.cleanState();
mListener.onPre();
new Thread(this).start();
}
@@ -206,25 +188,16 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
private void failDownload(String msg) {
Log.e(TAG, msg);
- isDownloading = false;
+ mConstance.isDownloading = false;
stopDownload();
mListener.onFail();
}
- private void setConnectParam(HttpURLConnection conn) throws ProtocolException {
- conn.setRequestMethod("GET");
- conn.setRequestProperty("Charset", "UTF-8");
- conn.setRequestProperty("User-Agent",
- "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
- conn.setRequestProperty("Accept",
- "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
- }
-
@Override public void run() {
try {
- URL url = new URL(mDownloadEntity.getDownloadUrl());
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- setConnectParam(conn);
+ URL url = new URL(mDownloadEntity.getDownloadUrl());
+ HttpURLConnection conn = ConnectionHelp.handleConnection(url);
+ conn = ConnectionHelp.setConnectParam(conn);
conn.setRequestProperty("Range", "bytes=" + 0 + "-");
conn.setConnectTimeout(mConnectTimeOut * 4);
conn.connect();
@@ -242,12 +215,16 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
handleBreakpoint(conn);
} else if (code == HttpURLConnection.HTTP_OK || len < 0) {
//在conn.setRequestProperty("Range", "bytes=" + 0 + "-");下,200为不支持断点状态
+ if (len < 0) {
+ failDownload("任务【" + mDownloadEntity.getDownloadUrl() + "】下载失败,文件长度小于0");
+ return;
+ }
isSupportBreakpoint = false;
mListener.supportBreakpoint(false);
Log.w(TAG, "该下载链接不支持断点下载");
handleBreakpoint(conn);
} else {
- failDownload("下载失败,返回码:" + code);
+ failDownload("任务【" + mDownloadEntity.getDownloadUrl() + "】下载失败,返回码:" + code);
}
} catch (IOException e) {
failDownload("下载失败【downloadUrl:"
@@ -263,16 +240,19 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
* 处理断点
*/
private void handleBreakpoint(HttpURLConnection conn) throws IOException {
+
//不支持断点只能单线程下载
if (!isSupportBreakpoint) {
ConfigEntity entity = new ConfigEntity();
- entity.fileSize = conn.getContentLength();
- entity.downloadUrl = mDownloadEntity.getDownloadUrl();
- entity.tempFile = mDownloadFile;
- entity.threadId = 0;
- entity.startLocation = 0;
- entity.endLocation = entity.fileSize;
- SingleThreadTask task = new SingleThreadTask(entity);
+ entity.FILE_SIZE = conn.getContentLength();
+ entity.DOWNLOAD_URL = mDownloadEntity.getDownloadUrl();
+ entity.TEMP_FILE = mDownloadFile;
+ entity.THREAD_ID = 0;
+ entity.START_LOCATION = 0;
+ entity.END_LOCATION = entity.FILE_SIZE;
+ entity.CONFIG_FILE_PATH = mConfigFile.getPath();
+ entity.isSupportBreakpoint = isSupportBreakpoint;
+ SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity);
mFixedThreadPool.execute(task);
mListener.onStart(0);
return;
@@ -280,7 +260,10 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
int fileLength = conn.getContentLength();
//必须建一个文件
CommonUtil.createFile(mDownloadFile.getPath());
- RandomAccessFile file = new RandomAccessFile(mDownloadFile.getPath(), "rwd");
+ //RandomAccessFile file = new RandomAccessFile(mDownloadFile.getPath(), "rwd");
+ ////设置文件长度
+ //file.setLength(fileLength);
+ BufferedRandomAccessFile file = new BufferedRandomAccessFile(mDownloadFile.getPath(), "rwd", 8192);
//设置文件长度
file.setLength(fileLength);
mListener.onPostPre(fileLength);
@@ -301,27 +284,27 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
}
}
}
- int blockSize = fileLength / THREAD_NUM;
- int[] recordL = new int[THREAD_NUM];
- int rl = 0;
+ int blockSize = fileLength / THREAD_NUM;
+ int[] recordL = new int[THREAD_NUM];
+ int rl = 0;
for (int i = 0; i < THREAD_NUM; i++) {
recordL[i] = -1;
}
for (int i = 0; i < THREAD_NUM; i++) {
- long startL = i * blockSize, endL = (i + 1) * blockSize;
- Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i);
+ long startL = i * blockSize, endL = (i + 1) * blockSize;
+ Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i);
if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
- mCurrentLocation += endL - startL;
+ mConstance.CURRENT_LOCATION += endL - startL;
Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++");
- mCompleteThreadNum++;
- mStopNum++;
- mCancelNum++;
- if (mCompleteThreadNum == THREAD_NUM) {
+ mConstance.COMPLETE_THREAD_NUM++;
+ mConstance.STOP_NUM++;
+ mConstance.CANCEL_NUM++;
+ if (mConstance.isComplete()) {
if (mConfigFile.exists()) {
mConfigFile.delete();
}
mListener.onComplete();
- isDownloading = false;
+ mConstance.isDownloading = false;
return;
}
continue;
@@ -331,7 +314,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
//如果有记录,则恢复下载
if (!isNewTask && record != null && Long.parseLong(record + "") > 0) {
Long r = Long.parseLong(record + "");
- mCurrentLocation += r - startL;
+ mConstance.CURRENT_LOCATION += r - startL;
Log.d(TAG, "++++++++++ 线程_" + i + "_恢复下载 ++++++++++");
mListener.onChildResume(r);
startL = r;
@@ -349,19 +332,21 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
endL = fileLength;
}
ConfigEntity entity = new ConfigEntity();
- entity.fileSize = fileLength;
- entity.downloadUrl = mDownloadEntity.getDownloadUrl();
- entity.tempFile = mDownloadFile;
- entity.threadId = i;
- entity.startLocation = startL;
- entity.endLocation = endL;
- SingleThreadTask task = new SingleThreadTask(entity);
+ entity.FILE_SIZE = fileLength;
+ entity.DOWNLOAD_URL = mDownloadEntity.getDownloadUrl();
+ entity.TEMP_FILE = mDownloadFile;
+ entity.THREAD_ID = i;
+ entity.START_LOCATION = startL;
+ entity.END_LOCATION = endL;
+ entity.CONFIG_FILE_PATH = mConfigFile.getPath();
+ entity.isSupportBreakpoint = isSupportBreakpoint;
+ SingleThreadTask task = new SingleThreadTask(mConstance, mListener, entity);
mTask.put(i, task);
}
- if (mCurrentLocation > 0) {
- mListener.onResume(mCurrentLocation);
+ if (mConstance.CURRENT_LOCATION > 0) {
+ mListener.onResume(mConstance.CURRENT_LOCATION);
} else {
- mListener.onStart(mCurrentLocation);
+ mListener.onStart(mConstance.CURRENT_LOCATION);
}
for (int l : recordL) {
if (l == -1) continue;
@@ -375,228 +360,15 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
/**
* 子线程下载信息类
*/
- private static class ConfigEntity {
+ final static class ConfigEntity {
//文件大小
- long fileSize;
- String downloadUrl;
- int threadId;
- long startLocation;
- long endLocation;
- File tempFile;
- }
-
- /**
- * 单个线程的下载任务
- */
- private class SingleThreadTask implements Runnable {
- private static final String TAG = "SingleThreadTask";
- private ConfigEntity configEntity;
- private String configFPath;
- private long currentLocation = 0;
-
- private SingleThreadTask(ConfigEntity downloadInfo) {
- this.configEntity = downloadInfo;
- if (isSupportBreakpoint) {
- configFPath = mContext.getFilesDir().getPath()
- + "/temp/"
- + configEntity.tempFile.getName()
- + ".properties";
- }
- }
-
- @Override public void run() {
- HttpURLConnection conn = null;
- InputStream is = null;
- try {
- URL url = new URL(configEntity.downloadUrl);
- conn = (HttpURLConnection) url.openConnection();
- if (isSupportBreakpoint) {
- Log.d(TAG, "线程_"
- + configEntity.threadId
- + "_正在下载【开始位置 : "
- + configEntity.startLocation
- + ",结束位置:"
- + configEntity.endLocation
- + "】");
- //在头里面请求下载开始位置和结束位置
- conn.setRequestProperty("Range",
- "bytes=" + configEntity.startLocation + "-" + configEntity.endLocation);
- } else {
- Log.w(TAG, "该下载不支持断点,即将重新下载");
- }
- setConnectParam(conn);
- conn.setConnectTimeout(mConnectTimeOut);
- conn.setReadTimeout(mReadTimeOut); //设置读取流的等待时间,必须设置该参数
- is = conn.getInputStream();
- //创建可设置位置的文件
- RandomAccessFile file = new RandomAccessFile(configEntity.tempFile, "rwd");
- //设置每条线程写入文件的位置
- file.seek(configEntity.startLocation);
- byte[] buffer = new byte[1024];
- int len;
- //当前子线程的下载位置
- currentLocation = configEntity.startLocation;
- while ((len = is.read(buffer)) != -1) {
- if (isCancel) {
- Log.d(TAG, "++++++++++ thread_" + configEntity.threadId + "_cancel ++++++++++");
- break;
- }
- if (isStop) {
- break;
- }
- //把下载数据数据写入文件
- file.write(buffer, 0, len);
- progress(len);
- }
- file.close();
- //close 为阻塞的,需要使用线程池来处理
- is.close();
- conn.disconnect();
-
- if (isCancel) {
- return;
- }
- //停止状态不需要删除记录文件
- if (isStop) {
- return;
- }
- //支持断点的处理
- if (isSupportBreakpoint) {
- Log.i(TAG, "线程【" + configEntity.threadId + "】下载完毕");
- writeConfig(configEntity.tempFile.getName() + "_state_" + configEntity.threadId, 1 + "");
- mListener.onChildComplete(configEntity.endLocation);
- mCompleteThreadNum++;
- if (mCompleteThreadNum == THREAD_NUM) {
- File configFile = new File(configFPath);
- if (configFile.exists()) {
- configFile.delete();
- }
- isDownloading = false;
- mListener.onComplete();
- }
- } else {
- Log.i(TAG, "下载任务完成");
- isDownloading = false;
- mListener.onComplete();
- }
- } catch (MalformedURLException e) {
- mFailNum++;
- failDownload(configEntity, currentLocation, "下载链接异常", e);
- } catch (IOException e) {
- mFailNum++;
- failDownload(configEntity, currentLocation, "下载失败【" + configEntity.downloadUrl + "】", e);
- } catch (Exception e) {
- mFailNum++;
- failDownload(configEntity, currentLocation, "获取流失败", e);
- }
- }
-
- /**
- * 停止下载
- */
- protected void stop() {
- synchronized (LOCK) {
- try {
- if (isSupportBreakpoint) {
- mStopNum++;
- String location = String.valueOf(currentLocation);
- Log.i(TAG,
- "thread_" + configEntity.threadId + "_stop, stop location ==> " + currentLocation);
- writeConfig(configEntity.tempFile.getName() + "_record_" + configEntity.threadId,
- location);
- if (mStopNum == THREAD_NUM) {
- Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++");
- isDownloading = false;
- mListener.onStop(mCurrentLocation);
- }
- } else {
- Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++");
- isDownloading = false;
- mListener.onStop(mCurrentLocation);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * 下载中
- */
- private void progress(long len) {
- synchronized (LOCK) {
- currentLocation += len;
- mCurrentLocation += len;
- mListener.onProgress(mCurrentLocation);
- }
- }
-
- /**
- * 取消下载
- */
- private void cancel() {
- synchronized (LOCK) {
- if (isSupportBreakpoint) {
- mCancelNum++;
- if (mCancelNum == THREAD_NUM) {
- File configFile = new File(configFPath);
- if (configFile.exists()) {
- configFile.delete();
- }
- if (configEntity.tempFile.exists()) {
- configEntity.tempFile.delete();
- }
- Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
- isDownloading = false;
- mListener.onCancel();
- }
- } else {
- Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
- isDownloading = false;
- mListener.onCancel();
- }
- }
- }
-
- /**
- * 下载失败
- */
- private void failDownload(ConfigEntity dEntity, long currentLocation, String msg,
- Exception ex) {
- synchronized (LOCK) {
- try {
- isDownloading = false;
- isStop = true;
- if (ex != null) {
- Log.e(TAG, CommonUtil.getPrintException(ex));
- }
- if (isSupportBreakpoint) {
- if (currentLocation != -1) {
- String location = String.valueOf(currentLocation);
- writeConfig(dEntity.tempFile.getName() + "_record_" + dEntity.threadId, location);
- }
- if (mFailNum == THREAD_NUM) {
- Log.d(TAG, "++++++++++++++++ onFail +++++++++++++++++");
- mListener.onFail();
- }
- } else {
- Log.d(TAG, "++++++++++++++++ onFail +++++++++++++++++");
- mListener.onFail();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * 将记录写入到配置文件
- */
- private void writeConfig(String key, String record) throws IOException {
- File configFile = new File(configFPath);
- Properties pro = CommonUtil.loadConfig(configFile);
- pro.setProperty(key, record);
- CommonUtil.saveConfig(configFile, pro);
- }
+ long FILE_SIZE;
+ String DOWNLOAD_URL;
+ int THREAD_ID;
+ long START_LOCATION;
+ long END_LOCATION;
+ File TEMP_FILE;
+ boolean isSupportBreakpoint = true;
+ String CONFIG_FILE_PATH;
}
}
\ No newline at end of file
diff --git a/Aria/src/main/java/com/arialyy/aria/core/task/SingleThreadTask.java b/Aria/src/main/java/com/arialyy/aria/core/task/SingleThreadTask.java
new file mode 100644
index 00000000..d09e1ed7
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/core/task/SingleThreadTask.java
@@ -0,0 +1,283 @@
+/*
+ * 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.task;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import com.arialyy.aria.util.BufferedRandomAccessFile;
+import com.arialyy.aria.util.CommonUtil;
+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;
+import java.util.Properties;
+
+/**
+ * Created by lyy on 2017/1/18.
+ * 下载线程
+ */
+final class SingleThreadTask implements Runnable {
+ private static final String TAG = "SingleThreadTask";
+ // TODO: 2017/2/22 不能使用1024 否则最大速度不能超过3m
+ private static final int BUF_SIZE = 8192;
+ private DownloadUtil.ConfigEntity mConfigEntity;
+ private String mConfigFPath;
+ private long mChildCurrentLocation = 0;
+ private static final Object LOCK = new Object();
+ private IDownloadListener mListener;
+ private DownloadStateConstance mConstance;
+
+ SingleThreadTask(DownloadStateConstance constance, IDownloadListener listener,
+ DownloadUtil.ConfigEntity downloadInfo) {
+ mConstance = constance;
+ mListener = listener;
+ this.mConfigEntity = downloadInfo;
+ if (mConfigEntity.isSupportBreakpoint) {
+ mConfigFPath = downloadInfo.CONFIG_FILE_PATH;
+ }
+ }
+
+ @Override public void run() {
+ HttpURLConnection conn = null;
+ InputStream is = null;
+ try {
+ URL url = new URL(mConfigEntity.DOWNLOAD_URL);
+ //conn = (HttpURLConnection) url.openConnection();
+ conn = ConnectionHelp.handleConnection(url);
+ if (mConfigEntity.isSupportBreakpoint) {
+ Log.d(TAG, "线程_"
+ + mConfigEntity.THREAD_ID
+ + "_正在下载【开始位置 : "
+ + mConfigEntity.START_LOCATION
+ + ",结束位置:"
+ + mConfigEntity.END_LOCATION
+ + "】");
+ //在头里面请求下载开始位置和结束位置
+ conn.setRequestProperty("Range",
+ "bytes=" + mConfigEntity.START_LOCATION + "-" + mConfigEntity.END_LOCATION);
+ } else {
+ Log.w(TAG, "该下载不支持断点,即将重新下载");
+ }
+ conn = ConnectionHelp.setConnectParam(conn);
+ conn.setConnectTimeout(mConstance.CONNECT_TIME_OUT);
+ conn.setReadTimeout(mConstance.READ_TIME_OUT); //设置读取流的等待时间,必须设置该参数
+ is = conn.getInputStream();
+ //创建可设置位置的文件
+ BufferedRandomAccessFile file =
+ new BufferedRandomAccessFile(mConfigEntity.TEMP_FILE, "rwd", BUF_SIZE);
+ //设置文件长度
+ file.seek(mConfigEntity.START_LOCATION);
+
+ byte[] buffer = new byte[BUF_SIZE];
+ int len;
+ //当前子线程的下载位置
+ mChildCurrentLocation = mConfigEntity.START_LOCATION;
+ while ((len = is.read(buffer)) != -1) {
+ if (mConstance.isCancel) {
+ break;
+ }
+ if (mConstance.isStop) {
+ Log.i(TAG, "stop");
+ break;
+ }
+ //把下载数据数据写入文件
+ file.write(buffer, 0, len);
+ progress(len);
+ }
+ file.close();
+ //close 为阻塞的,需要使用线程池来处理
+ is.close();
+ conn.disconnect();
+ if (mConstance.isCancel) {
+ return;
+ }
+ //停止状态不需要删除记录文件
+ if (mConstance.isStop) {
+ return;
+ }
+ //支持断点的处理
+ if (mConfigEntity.isSupportBreakpoint) {
+ Log.i(TAG, "线程【" + mConfigEntity.THREAD_ID + "】下载完毕");
+ writeConfig(mConfigEntity.TEMP_FILE.getName() + "_state_" + mConfigEntity.THREAD_ID,
+ 1 + "");
+ mListener.onChildComplete(mConfigEntity.END_LOCATION);
+ mConstance.COMPLETE_THREAD_NUM++;
+ if (mConstance.isComplete()) {
+ File configFile = new File(mConfigFPath);
+ if (configFile.exists()) {
+ configFile.delete();
+ }
+ mConstance.isDownloading = false;
+ mListener.onComplete();
+ }
+ } else {
+ Log.i(TAG, "下载任务完成");
+ mConstance.isDownloading = false;
+ mListener.onComplete();
+ }
+ } catch (MalformedURLException e) {
+ mConstance.FAIL_NUM++;
+ failDownload(mConfigEntity, mChildCurrentLocation, "下载链接异常", e);
+ } catch (IOException e) {
+ mConstance.FAIL_NUM++;
+ failDownload(mConfigEntity, mChildCurrentLocation, "下载失败【" + mConfigEntity.DOWNLOAD_URL + "】",
+ e);
+ } catch (Exception e) {
+ mConstance.FAIL_NUM++;
+ failDownload(mConfigEntity, mChildCurrentLocation, "获取流失败", e);
+ }
+ }
+
+ /**
+ * 停止下载
+ */
+ protected void stop() {
+ synchronized (LOCK) {
+ try {
+ if (mConfigEntity.isSupportBreakpoint) {
+ mConstance.STOP_NUM++;
+ String location = String.valueOf(mChildCurrentLocation);
+ Log.d(TAG, "thread_"
+ + mConfigEntity.THREAD_ID
+ + "_stop, stop location ==> "
+ + mChildCurrentLocation);
+ writeConfig(mConfigEntity.TEMP_FILE.getName() + "_record_" + mConfigEntity.THREAD_ID,
+ location);
+ if (mConstance.isStop()) {
+ Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++");
+ mConstance.isDownloading = false;
+ mListener.onStop(mConstance.CURRENT_LOCATION);
+ }
+ } else {
+ Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++");
+ mConstance.isDownloading = false;
+ mListener.onStop(mConstance.CURRENT_LOCATION);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * 下载中
+ */
+ private void progress(long len) {
+ synchronized (LOCK) {
+ mChildCurrentLocation += len;
+ mConstance.CURRENT_LOCATION += len;
+ mListener.onProgress(mConstance.CURRENT_LOCATION);
+ //mHandler.sendEmptyMessage(1);
+ //mHandler.post(t);
+ }
+ }
+
+ Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ mListener.onProgress(mConstance.CURRENT_LOCATION);
+ }
+ };
+
+ Thread t = new Thread(new Runnable() {
+ @Override public void run() {
+ mListener.onProgress(mConstance.CURRENT_LOCATION);
+ }
+ });
+
+ //Handler handler = new Handler(){
+ // @Override public void handleMessage(Message msg) {
+ // super.handleMessage(msg);
+ // mListener.onProgress(mConstance.CURRENT_LOCATION);
+ // }
+ //};
+
+ Thread thread = new Thread();
+
+ /**
+ * 取消下载
+ */
+ protected void cancel() {
+ synchronized (LOCK) {
+ if (mConfigEntity.isSupportBreakpoint) {
+ mConstance.CANCEL_NUM++;
+ Log.d(TAG, "++++++++++ thread_" + mConfigEntity.THREAD_ID + "_cancel ++++++++++");
+ if (mConstance.isCancel()) {
+ File configFile = new File(mConfigFPath);
+ if (configFile.exists()) {
+ configFile.delete();
+ }
+ if (mConfigEntity.TEMP_FILE.exists()) {
+ mConfigEntity.TEMP_FILE.delete();
+ }
+ Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
+ mConstance.isDownloading = false;
+ mListener.onCancel();
+ }
+ } else {
+ Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++");
+ mConstance.isDownloading = false;
+ mListener.onCancel();
+ }
+ }
+ }
+
+ /**
+ * 下载失败
+ */
+ private void failDownload(DownloadUtil.ConfigEntity dEntity, long currentLocation, String msg,
+ Exception ex) {
+ synchronized (LOCK) {
+ try {
+ mConstance.isDownloading = false;
+ mConstance.isStop = true;
+ if (ex != null) {
+ Log.e(TAG, CommonUtil.getPrintException(ex));
+ }
+ if (mConfigEntity.isSupportBreakpoint) {
+ if (currentLocation != -1) {
+ String location = String.valueOf(currentLocation);
+ writeConfig(dEntity.TEMP_FILE.getName() + "_record_" + dEntity.THREAD_ID, location);
+ }
+ if (mConstance.isFail()) {
+ Log.d(TAG, "++++++++++++++++ onFail +++++++++++++++++");
+ mListener.onFail();
+ }
+ } else {
+ Log.d(TAG, "++++++++++++++++ onFail +++++++++++++++++");
+ mListener.onFail();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * 将记录写入到配置文件
+ */
+ private void writeConfig(String key, String record) throws IOException {
+ File configFile = new File(mConfigFPath);
+ Properties pro = CommonUtil.loadConfig(configFile);
+ pro.setProperty(key, record);
+ CommonUtil.saveConfig(configFile, pro);
+ }
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/core/task/Task.java b/Aria/src/main/java/com/arialyy/aria/core/task/Task.java
index c66b5f67..016ab8ef 100644
--- a/Aria/src/main/java/com/arialyy/aria/core/task/Task.java
+++ b/Aria/src/main/java/com/arialyy/aria/core/task/Task.java
@@ -22,6 +22,7 @@ import android.os.Handler;
import android.util.Log;
import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.DownloadManager;
+import com.arialyy.aria.core.TaskEntity;
import com.arialyy.aria.core.scheduler.DownloadSchedulers;
import com.arialyy.aria.core.scheduler.IDownloadSchedulers;
import com.arialyy.aria.core.DownloadEntity;
@@ -196,20 +197,9 @@ public class Task {
return this;
}
- ///**
- // * 设置自定义下载工具
- // *
- // * @param downloadUtil {@link IDownloadUtil}
- // */
- //public Builder setDownloadUtil(IDownloadUtil downloadUtil) {
- // this.downloadUtil = downloadUtil;
- // return this;
- //}
-
public Task build() {
Task task = new Task(context, downloadEntity, outHandler);
task.setTargetName(targetName);
- //Log.w(TAG, "downloadEntity hashcode ==> " + downloadEntity.hashCode());
downloadEntity.save();
return task;
}
diff --git a/Aria/src/main/java/com/arialyy/aria/exception/FileException.java b/Aria/src/main/java/com/arialyy/aria/exception/FileException.java
new file mode 100644
index 00000000..f4fbb700
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/exception/FileException.java
@@ -0,0 +1,13 @@
+package com.arialyy.aria.exception;
+
+/**
+ * Created by Aria.Lao on 2017/1/18.
+ * Aria 文件异常
+ */
+public class FileException extends NullPointerException {
+ private static final String ARIA_FILE_EXCEPTION = "Aria Exception:";
+
+ public FileException(String detailMessage) {
+ super(ARIA_FILE_EXCEPTION + detailMessage);
+ }
+}
diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java b/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java
index 4f6fe2b1..10b64600 100644
--- a/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java
+++ b/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java
@@ -113,7 +113,6 @@ public class DbUtil {
params[i] = "'" + expression[i + 1] + "'";
}
sql = String.format(sql, params);
- Log.d(TAG, sql);
print(DEL_DATA, sql);
mDb.execSQL(sql);
close();
@@ -185,7 +184,6 @@ public class DbUtil {
params[i] = "'" + expression[i + 1] + "'";
}
sql = String.format(sql, params);
- Log.d(TAG, sql);
print(FIND_DATA, sql);
Cursor cursor = mDb.rawQuery(sql, null);
return cursor.getCount() > 0 ? newInstanceEntity(clazz, cursor) : null;
diff --git a/Aria/src/main/java/com/arialyy/aria/util/BufferedRandomAccessFile.java b/Aria/src/main/java/com/arialyy/aria/util/BufferedRandomAccessFile.java
new file mode 100644
index 00000000..a0f36785
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/util/BufferedRandomAccessFile.java
@@ -0,0 +1,328 @@
+/**
+ * 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 java.io.*;
+import java.util.Arrays;
+
+//import org.apache.log4j.Logger;
+
+/**
+ * A BufferedRandomAccessFile is like a
+ * RandomAccessFile, but it uses a private buffer so that most
+ * operations do not require a disk access.
+ *
+ *
+ * Note: The operations on this class are unmonitored. Also, the correct
+ * functioning of the RandomAccessFile methods that are not
+ * overridden here relies on the implementation of those methods in the
+ * superclass.
+ * Author : Avinash Lakshman ( alakshman@facebook.com) Prashant Malik ( pmalik@facebook.com )
+ */
+
+public final class BufferedRandomAccessFile extends RandomAccessFile {
+ //private static final Logger logger_ = Logger.getLogger(BufferedRandomAccessFile.class);
+ static final int LogBuffSz_ = 16; // 64K buffer
+ public static final int BuffSz_ = (1 << LogBuffSz_);
+ static final long BuffMask_ = ~(((long) BuffSz_) - 1L);
+
+ /*
+ * This implementation is based on the buffer implementation in Modula-3's
+ * "Rd", "Wr", "RdClass", and "WrClass" interfaces.
+ */
+ private boolean dirty_; // true iff unflushed bytes exist
+ private boolean closed_; // true iff the file is closed
+ private long curr_; // current position in file
+ private long lo_, hi_; // bounds on characters in "buff"
+ private byte[] buff_; // local buffer
+ private long maxHi_; // this.lo + this.buff.length
+ private boolean hitEOF_; // buffer contains last file block?
+ private long diskPos_; // disk position
+
+ /*
+ * To describe the above fields, we introduce the following abstractions for
+ * the file "f":
+ *
+ * len(f) the length of the file curr(f) the current position in the file
+ * c(f) the abstract contents of the file disk(f) the contents of f's
+ * backing disk file closed(f) true iff the file is closed
+ *
+ * "curr(f)" is an index in the closed interval [0, len(f)]. "c(f)" is a
+ * character sequence of length "len(f)". "c(f)" and "disk(f)" may differ if
+ * "c(f)" contains unflushed writes not reflected in "disk(f)". The flush
+ * operation has the effect of making "disk(f)" identical to "c(f)".
+ *
+ * A file is said to be *valid* if the following conditions hold:
+ *
+ * V1. The "closed" and "curr" fields are correct:
+ *
+ * f.closed == closed(f) f.curr == curr(f)
+ *
+ * V2. The current position is either contained in the buffer, or just past
+ * the buffer:
+ *
+ * f.lo <= f.curr <= f.hi
+ *
+ * V3. Any (possibly) unflushed characters are stored in "f.buff":
+ *
+ * (forall i in [f.lo, f.curr): c(f)[i] == f.buff[i - f.lo])
+ *
+ * V4. For all characters not covered by V3, c(f) and disk(f) agree:
+ *
+ * (forall i in [f.lo, len(f)): i not in [f.lo, f.curr) => c(f)[i] ==
+ * disk(f)[i])
+ *
+ * V5. "f.dirty" is true iff the buffer contains bytes that should be
+ * flushed to the file; by V3 and V4, only part of the buffer can be dirty.
+ *
+ * f.dirty == (exists i in [f.lo, f.curr): c(f)[i] != f.buff[i - f.lo])
+ *
+ * V6. this.maxHi == this.lo + this.buff.length
+ *
+ * Note that "f.buff" can be "null" in a valid file, since the range of
+ * characters in V3 is empty when "f.lo == f.curr".
+ *
+ * A file is said to be *ready* if the buffer contains the current position,
+ * i.e., when:
+ *
+ * R1. !f.closed && f.buff != null && f.lo <= f.curr && f.curr < f.hi
+ *
+ * When a file is ready, reading or writing a single byte can be performed
+ * by reading or writing the in-memory buffer without performing a disk
+ * operation.
+ */
+
+ /**
+ * Open a new BufferedRandomAccessFile on file
+ * in mode mode, which should be "r" for reading only, or
+ * "rw" for reading and writing.
+ */
+ public BufferedRandomAccessFile(File file, String mode) throws IOException {
+ super(file, mode);
+ this.init(0);
+ }
+
+ public BufferedRandomAccessFile(File file, String mode, int size) throws IOException {
+ super(file, mode);
+ this.init(size);
+ }
+
+ /**
+ * Open a new BufferedRandomAccessFile on the file named
+ * name in mode mode, which should be "r" for
+ * reading only, or "rw" for reading and writing.
+ */
+ public BufferedRandomAccessFile(String name, String mode) throws IOException {
+ super(name, mode);
+ this.init(0);
+ }
+
+ public BufferedRandomAccessFile(String name, String mode, int size) throws FileNotFoundException {
+ super(name, mode);
+ this.init(size);
+ }
+
+ private void init(int size) {
+ this.dirty_ = this.closed_ = false;
+ this.lo_ = this.curr_ = this.hi_ = 0;
+ this.buff_ = (size > BuffSz_) ? new byte[size] : new byte[BuffSz_];
+ this.maxHi_ = (long) BuffSz_;
+ this.hitEOF_ = false;
+ this.diskPos_ = 0L;
+ }
+
+ public void close() throws IOException {
+ this.flush();
+ this.closed_ = true;
+ super.close();
+ }
+
+ /**
+ * Flush any bytes in the file's buffer that have not yet been written to
+ * disk. If the file was created read-only, this method is a no-op.
+ */
+ public void flush() throws IOException {
+ this.flushBuffer();
+ }
+
+ /* Flush any dirty bytes in the buffer to disk. */
+ private void flushBuffer() throws IOException {
+ if (this.dirty_) {
+ if (this.diskPos_ != this.lo_) super.seek(this.lo_);
+ int len = (int) (this.curr_ - this.lo_);
+ super.write(this.buff_, 0, len);
+ this.diskPos_ = this.curr_;
+ this.dirty_ = false;
+ }
+ }
+
+ /*
+ * Read at most "this.buff.length" bytes into "this.buff", returning the
+ * number of bytes read. If the return result is less than
+ * "this.buff.length", then EOF was read.
+ */
+ private int fillBuffer() throws IOException {
+ int cnt = 0;
+ int rem = this.buff_.length;
+ while (rem > 0) {
+ int n = super.read(this.buff_, cnt, rem);
+ if (n < 0) break;
+ cnt += n;
+ rem -= n;
+ }
+ if ((cnt < 0) && (this.hitEOF_ = (cnt < this.buff_.length))) {
+ // make sure buffer that wasn't read is initialized with -1
+ Arrays.fill(this.buff_, cnt, this.buff_.length, (byte) 0xff);
+ }
+ this.diskPos_ += cnt;
+ return cnt;
+ }
+
+ /*
+ * This method positions this.curr at position pos.
+ * If pos does not fall in the current buffer, it flushes the
+ * current buffer and loads the correct one.
+ *
+ * On exit from this routine this.curr == this.hi iff pos
+ * is at or past the end-of-file, which can only happen if the file was
+ * opened in read-only mode.
+ */
+ public void seek(long pos) throws IOException {
+ if (pos >= this.hi_ || pos < this.lo_) {
+ // seeking outside of current buffer -- flush and read
+ this.flushBuffer();
+ this.lo_ = pos & BuffMask_; // start at BuffSz boundary
+ this.maxHi_ = this.lo_ + (long) this.buff_.length;
+ if (this.diskPos_ != this.lo_) {
+ super.seek(this.lo_);
+ this.diskPos_ = this.lo_;
+ }
+ int n = this.fillBuffer();
+ this.hi_ = this.lo_ + (long) n;
+ } else {
+ // seeking inside current buffer -- no read required
+ if (pos < this.curr_) {
+ // if seeking backwards, we must flush to maintain V4
+ this.flushBuffer();
+ }
+ }
+ this.curr_ = pos;
+ }
+
+ public long getFilePointer() {
+ return this.curr_;
+ }
+
+ public long length() throws IOException {
+ return Math.max(this.curr_, super.length());
+ }
+
+ public int read() throws IOException {
+ if (this.curr_ >= this.hi_) {
+ // test for EOF
+ // if (this.hi < this.maxHi) return -1;
+ if (this.hitEOF_) return -1;
+
+ // slow path -- read another buffer
+ this.seek(this.curr_);
+ if (this.curr_ == this.hi_) return -1;
+ }
+ byte res = this.buff_[(int) (this.curr_ - this.lo_)];
+ this.curr_++;
+ return ((int) res) & 0xFF; // convert byte -> int
+ }
+
+ public int read(byte[] b) throws IOException {
+ return this.read(b, 0, b.length);
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (this.curr_ >= this.hi_) {
+ // test for EOF
+ // if (this.hi < this.maxHi) return -1;
+ if (this.hitEOF_) return -1;
+
+ // slow path -- read another buffer
+ this.seek(this.curr_);
+ if (this.curr_ == this.hi_) return -1;
+ }
+ len = Math.min(len, (int) (this.hi_ - this.curr_));
+ int buffOff = (int) (this.curr_ - this.lo_);
+ System.arraycopy(this.buff_, buffOff, b, off, len);
+ this.curr_ += len;
+ return len;
+ }
+
+ public void write(int b) throws IOException {
+ if (this.curr_ >= this.hi_) {
+ if (this.hitEOF_ && this.hi_ < this.maxHi_) {
+ // at EOF -- bump "hi"
+ this.hi_++;
+ } else {
+ // slow path -- write current buffer; read next one
+ this.seek(this.curr_);
+ if (this.curr_ == this.hi_) {
+ // appending to EOF -- bump "hi"
+ this.hi_++;
+ }
+ }
+ }
+ this.buff_[(int) (this.curr_ - this.lo_)] = (byte) b;
+ this.curr_++;
+ this.dirty_ = true;
+ }
+
+ public void write(byte[] b) throws IOException {
+ this.write(b, 0, b.length);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ while (len > 0) {
+ int n = this.writeAtMost(b, off, len);
+ off += n;
+ len -= n;
+ this.dirty_ = true;
+ }
+ }
+
+ /*
+ * Write at most "len" bytes to "b" starting at position "off", and return
+ * the number of bytes written.
+ */
+ private int writeAtMost(byte[] b, int off, int len) throws IOException {
+ if (this.curr_ >= this.hi_) {
+ if (this.hitEOF_ && this.hi_ < this.maxHi_) {
+ // at EOF -- bump "hi"
+ this.hi_ = this.maxHi_;
+ } else {
+ // slow path -- write current buffer; read next one
+ this.seek(this.curr_);
+ if (this.curr_ == this.hi_) {
+ // appending to EOF -- bump "hi"
+ this.hi_ = this.maxHi_;
+ }
+ }
+ }
+ len = Math.min(len, (int) (this.hi_ - this.curr_));
+ int buffOff = (int) (this.curr_ - this.lo_);
+ System.arraycopy(b, off, this.buff_, buffOff, len);
+ this.curr_ += len;
+ return len;
+ }
+}
\ No newline at end of file
diff --git a/Aria/src/main/java/com/arialyy/aria/util/CAConfiguration.java b/Aria/src/main/java/com/arialyy/aria/util/CAConfiguration.java
new file mode 100644
index 00000000..ac755396
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/util/CAConfiguration.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.arialyy.aria.util;
+
+/**
+ * Created by Aria.Lao on 2017/1/11.
+ */
+
+public class CAConfiguration {
+ public static String CA_PATH, CA_ALIAS;
+}
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 7cffe259..fd07b7c1 100644
--- a/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java
+++ b/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java
@@ -19,6 +19,7 @@ package com.arialyy.aria.util;
import android.text.TextUtils;
import android.util.Log;
import com.arialyy.aria.core.DownloadEntity;
+import com.arialyy.aria.exception.FileException;
import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -30,6 +31,10 @@ import java.util.regex.Pattern;
public class CheckUtil {
private static final String TAG = "CheckUtil";
+ public static void checkNull(Object obj) {
+ if (obj == null) throw new IllegalArgumentException("不能传入空对象");
+ }
+
/**
* 检查sql的expression是否合法
*/
@@ -80,9 +85,9 @@ public class CheckUtil {
return false;
} else if (TextUtils.isEmpty(entity.getFileName())) {
//Log.w(TAG, "文件名不能为空");
- throw new IllegalArgumentException("文件名不能为null");
+ throw new FileException("文件名不能为null");
} else if (TextUtils.isEmpty(entity.getDownloadPath())) {
- throw new IllegalArgumentException("文件保存路径不能为null");
+ throw new FileException("文件保存路径不能为null");
}
String fileName = entity.getFileName();
if (fileName.contains(" ")) {
diff --git a/Aria/src/main/java/com/arialyy/aria/util/SSLContextUtil.java b/Aria/src/main/java/com/arialyy/aria/util/SSLContextUtil.java
new file mode 100644
index 00000000..af40e0de
--- /dev/null
+++ b/Aria/src/main/java/com/arialyy/aria/util/SSLContextUtil.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.arialyy.aria.util;
+
+import android.text.TextUtils;
+import com.arialyy.aria.core.DownloadManager;
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Created by Aria.Lao on 2017/1/11.
+ */
+
+public class SSLContextUtil {
+
+ /**
+ * 颁发服务器证书的 CA 未知
+ *
+ * @param caAlias CA证书别名
+ * @param caPath 保存在assets目录下的CA证书完整路径
+ */
+ public static SSLContext getSSLContext(String caAlias, String caPath) {
+ if (TextUtils.isEmpty(caAlias) || TextUtils.isEmpty(caPath)) {
+ return null;
+ }
+ // Load CAs from an InputStream
+ // (could be from a resource or ByteArrayInputStream or ...)
+ CertificateFactory cf = null;
+ try {
+ cf = CertificateFactory.getInstance("X.509");
+ InputStream caInput = DownloadManager.APP.getAssets().open(caPath);
+ Certificate ca;
+ ca = cf.generateCertificate(caInput);
+ System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
+
+ // Create a KeyStore containing our trusted CAs
+ String keyStoreType = KeyStore.getDefaultType();
+ KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+ keyStore.load(null, null);
+ keyStore.setCertificateEntry(caAlias, ca);
+
+ // Create a TrustManager that trusts the CAs in our KeyStore
+ String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
+ tmf.init(keyStore);
+ KeyManagerFactory kmf =
+ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(keyStore, null);
+
+ // Create an SSLContext that uses our TrustManager
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
+ return context;
+ } catch (CertificateException | NoSuchAlgorithmException | IOException | KeyStoreException | KeyManagementException | UnrecoverableKeyException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 服务器证书不是由 CA 签署的,而是自签署时,获取默认的SSL
+ */
+ public static SSLContext getDefaultSLLContext() {
+ SSLContext sslContext = null;
+ try {
+ sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, new TrustManager[] { trustManagers }, new SecureRandom());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return sslContext;
+ }
+
+ /**
+ * 创建自己的 TrustManager,这次直接信任服务器证书。这种方法具有前面所述的将应用与证书直接关联的所有弊端,但可以安全地操作。
+ */
+ private static TrustManager trustManagers = new X509TrustManager() {
+
+ @Override public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ }
+
+ @Override public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+
+ }
+
+ @Override public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+ };
+
+ public static final HostnameVerifier HOSTNAME_VERIFIER = new HostnameVerifier() {
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ };
+}
diff --git a/README.md b/README.md
index c63f85e8..fbf16549 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@
- 支持多线程、多任务下载
- 支持任务自动切换
- 支持下载速度直接获取
+ - 支持https地址下载
[Aria怎样使用?](#使用)
@@ -16,11 +17,9 @@
## 下载
[![Download](https://api.bintray.com/packages/arialyy/maven/Aria/images/download.svg)](https://bintray.com/arialyy/maven/Aria/_latestVersion)
```java
-compile 'com.arialyy.aria:Aria:2.3.8'
+compile 'com.arialyy.aria:Aria:2.4.2'
```
-
-
## 示例
![多任务下载](https://github.com/AriaLyy/DownloadUtil/blob/v_2.0/img/download_img.gif)
![Dialog使用](https://github.com/AriaLyy/DownloadUtil/blob/v_2.0/img/dialog_use.gif "")
@@ -52,6 +51,7 @@ compile 'com.arialyy.aria:Aria:2.3.8'
}
```
### 四、通过下载链接,你还能使用Aria执行很多操作,如:
+Aria支持https下载,如果你希望使用自己的ca证书,那么你需要进行[Aria https证书配置](#https证书配置)
- 添加任务(不进行下载)
```java
@@ -118,8 +118,25 @@ compile 'com.arialyy.aria:Aria:2.3.8'
Aria.get(this).openBroadcast(true);
```
+### https证书配置
+ + 将你的证书导入`assets`目录
+ + 调用以下代码配置ca证书相关信息
+
+ ```java
+ /**
+ * 设置CA证书信息
+ *
+ * @param caAlias ca证书别名
+ * @param caPath assets 文件夹下的ca证书完整路径
+ */
+ Aria.get(this).setCAInfo("caAlias","caPath");
+ ```
+
***
## 开发日志
+ + v_2.4.2 修复失败重试无效的bug
+ + v_2.4.1 修复下载慢的问题,修复application、service 不能使用的问题
+ + v_2.4.0 支持https链接下载
+ v_2.3.8 修复数据错乱的bug、添加fragment支持
+ v_2.3.6 添加dialog、popupWindow支持
+ v_2.3.3
@@ -128,7 +145,6 @@ compile 'com.arialyy.aria:Aria:2.3.8'
- 修复一个内存泄露的bug
+ v_2.3.1 重命名为Aria,下载流程简化
+ v_2.1.1 增加,选择最大下载任务数接口
- + v_2.1.0 修复大量bug
License
-------
diff --git a/app/src/main/java/com/arialyy/simple/MainActivity.java b/app/src/main/java/com/arialyy/simple/MainActivity.java
index 33a10ee8..0df80bbc 100644
--- a/app/src/main/java/com/arialyy/simple/MainActivity.java
+++ b/app/src/main/java/com/arialyy/simple/MainActivity.java
@@ -33,6 +33,7 @@ import com.arialyy.simple.databinding.ActivityMainBinding;
import com.arialyy.simple.dialog_task.DownloadDialog;
import com.arialyy.simple.fragment_task.FragmentActivity;
import com.arialyy.simple.multi_task.MultiTaskActivity;
+import com.arialyy.simple.notification.SimpleNotification;
import com.arialyy.simple.pop_task.DownloadPopupWindow;
import com.arialyy.simple.single_task.SingleTaskActivity;
@@ -104,6 +105,10 @@ public class MainActivity extends BaseActivity {
case R.id.fragment_task:
startActivity(new Intent(this, FragmentActivity.class));
break;
+ case R.id.notification:
+ SimpleNotification notification = new SimpleNotification(this);
+ notification.start();
+ break;
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/arialyy/simple/multi_task/FileListAdapter.java b/app/src/main/java/com/arialyy/simple/multi_task/FileListAdapter.java
index 97e9b773..c3e87256 100644
--- a/app/src/main/java/com/arialyy/simple/multi_task/FileListAdapter.java
+++ b/app/src/main/java/com/arialyy/simple/multi_task/FileListAdapter.java
@@ -67,7 +67,6 @@ final class FileListAdapter extends AbsRVAdapter keys = mBtStates.keySet();
for (String key : keys) {
if (key.equals(downloadUrl)) {
- Log.d(TAG, "able ==> " + able);
mBtStates.put(downloadUrl, able);
notifyItemChanged(indexItem(downloadUrl));
return;
@@ -80,7 +79,7 @@ final class FileListAdapter extends AbsRVAdapter " + index);
+ //Log.d(TAG, "index ==> " + index);
return index;
}
}
diff --git a/app/src/main/java/com/arialyy/simple/notification/SimpleNotification.java b/app/src/main/java/com/arialyy/simple/notification/SimpleNotification.java
new file mode 100644
index 00000000..3d11fd69
--- /dev/null
+++ b/app/src/main/java/com/arialyy/simple/notification/SimpleNotification.java
@@ -0,0 +1,98 @@
+package com.arialyy.simple.notification;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Environment;
+import android.support.v4.app.NotificationCompat;
+import com.arialyy.aria.core.Aria;
+import com.arialyy.aria.core.task.Task;
+import com.arialyy.frame.util.show.L;
+import com.arialyy.frame.util.show.T;
+import com.arialyy.simple.R;
+
+/**
+ * Created by Aria.Lao on 2017/1/18.
+ */
+
+public class SimpleNotification {
+ private static final String DOWNLOAD_URL =
+ "http://static.gaoshouyou.com/d/6e/e5/ff6ecaaf45e532e6d07747af82357472.apk";
+
+ private NotificationManager mManager;
+ private Context mContext;
+ private NotificationCompat.Builder mBuilder;
+ private static final int mNotifiyId = 0;
+
+ public SimpleNotification(Context context) {
+ mContext = context;
+ init();
+ }
+
+ private void init() {
+ mManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mBuilder = new NotificationCompat.Builder(mContext);
+ mBuilder.setContentTitle("Aria Download Test")
+ .setContentText("进度条")
+ .setProgress(100, 0, false)
+ .setSmallIcon(R.mipmap.ic_launcher);
+ mManager.notify(mNotifiyId, mBuilder.build());
+ Aria.whit(mContext).addSchedulerListener(new DownloadCallback(mBuilder, mManager));
+ }
+
+ public void start() {
+ Aria.whit(mContext)
+ .load(DOWNLOAD_URL)
+ .setDownloadName("notification_test.apk")
+ .setDownloadPath(
+ Environment.getExternalStorageDirectory() + "/Download/notification_test.apk")
+ .start();
+ }
+
+ public void stop() {
+ Aria.whit(mContext).load(DOWNLOAD_URL).stop();
+ }
+
+ private static class DownloadCallback extends Aria.SimpleSchedulerListener {
+ NotificationCompat.Builder mBuilder;
+ NotificationManager mManager;
+
+ private DownloadCallback(NotificationCompat.Builder builder, NotificationManager manager) {
+ mBuilder = builder;
+ mManager = manager;
+ }
+
+ @Override public void onTaskStart(Task task) {
+ super.onTaskStart(task);
+ }
+
+ @Override public void onTaskPre(Task task) {
+ super.onTaskPre(task);
+ }
+
+ @Override public void onTaskStop(Task task) {
+ super.onTaskStop(task);
+ }
+
+ @Override public void onTaskRunning(Task task) {
+ super.onTaskRunning(task);
+ long len = task.getFileSize();
+ int p = (int) (task.getCurrentProgress() * 100 / len);
+ if (mBuilder != null) {
+ mBuilder.setProgress(100, p, false);
+ mManager.notify(mNotifiyId, mBuilder.build());
+ }
+ }
+
+ @Override public void onTaskComplete(Task task) {
+ super.onTaskComplete(task);
+ if (mBuilder != null) {
+ mBuilder.setProgress(100, 100, false);
+ mManager.notify(mNotifiyId, mBuilder.build());
+ }
+ }
+
+ @Override public void onTaskCancel(Task task) {
+ super.onTaskCancel(task);
+ }
+ }
+}
diff --git a/app/src/main/java/com/arialyy/simple/single_task/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/single_task/SingleTaskActivity.java
index b66ae0b4..4d0bdff2 100644
--- a/app/src/main/java/com/arialyy/simple/single_task/SingleTaskActivity.java
+++ b/app/src/main/java/com/arialyy/simple/single_task/SingleTaskActivity.java
@@ -33,6 +33,8 @@ import butterknife.Bind;
import com.arialyy.aria.core.AMTarget;
import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.DownloadEntity;
+import com.arialyy.aria.core.task.DownloadUtil;
+import com.arialyy.aria.core.task.IDownloadListener;
import com.arialyy.aria.core.task.Task;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.frame.util.show.L;
@@ -49,10 +51,11 @@ public class SingleTaskActivity extends BaseActivity {
public static final int DOWNLOAD_RESUME = 0x05;
public static final int DOWNLOAD_COMPLETE = 0x06;
public static final int DOWNLOAD_RUNNING = 0x07;
+
private static final String DOWNLOAD_URL =
- "http://static.gaoshouyou.com/d/3a/93/573ae1db9493a801c24bf66128b11e39.apk";
- //private static final String DOWNLOAD_URL =
- // "http://www.yangqiang.im/wp-content/uploads/2016/10/%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F.png";
+ //"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/21/e8/61218d78d0e8b79df68dbc18dd484c97.apk";
@Bind(R.id.progressBar) HorizontalProgressBarWithNumber mPb;
@Bind(R.id.start) Button mStart;
@Bind(R.id.stop) Button mStop;
@@ -89,7 +92,7 @@ public class SingleTaskActivity extends BaseActivity {
case DOWNLOAD_PRE:
mSize.setText(CommonUtil.formatFileSize((Long) msg.obj));
setBtState(false);
- mStart.setText("暂停");
+ //mStart.setText("暂停");
break;
case DOWNLOAD_FAILE:
Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show();
@@ -114,15 +117,11 @@ public class SingleTaskActivity extends BaseActivity {
setBtState(false);
break;
case DOWNLOAD_COMPLETE:
+ mPb.setProgress(100);
Toast.makeText(SingleTaskActivity.this, "下载完成", Toast.LENGTH_SHORT).show();
mStart.setText("重新开始?");
mCancel.setEnabled(false);
setBtState(true);
-
- //String path = Environment.getExternalStorageDirectory().getPath() + "/test.jpg";
- //Bitmap bm = BitmapFactory.decodeFile(path);
- //mImg.setImageBitmap(bm);
-
break;
}
}
@@ -200,9 +199,68 @@ public class SingleTaskActivity extends BaseActivity {
.load(DOWNLOAD_URL)
.setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/test.apk")
.setDownloadName("test.apk")
- //.setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/test.jpg")
- //.setDownloadName("test.jpg")
.start();
+ //DownloadEntity entity = new DownloadEntity();
+ //entity.setDownloadUrl(DOWNLOAD_URL);
+ //entity.setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/test.apk");
+ //entity.setFileName("test.apk");
+ //DownloadUtil util = new DownloadUtil(this, entity, new IDownloadListener() {
+ // long fileSize = 1;
+ // @Override public void supportBreakpoint(boolean support) {
+ //
+ // }
+ //
+ // @Override public void onCancel() {
+ //
+ // }
+ //
+ // @Override public void onFail() {
+ //
+ // }
+ //
+ // @Override public void onPre() {
+ //
+ // }
+ //
+ // @Override public void onPostPre(long fileSize) {
+ // this.fileSize = fileSize;
+ // }
+ //
+ // @Override public void onProgress(long currentLocation) {
+ // long current = currentLocation;
+ // long len = fileSize;
+ // if (len == 0) {
+ // mPb.setProgress(0);
+ // } else {
+ // mPb.setProgress((int) ((current * 100) / len));
+ // }
+ // }
+ //
+ // @Override public void onChildComplete(long finishLocation) {
+ //
+ // }
+ //
+ // @Override public void onStart(long startLocation) {
+ //
+ // }
+ //
+ // @Override public void onChildResume(long resumeLocation) {
+ //
+ // }
+ //
+ // @Override public void onResume(long resumeLocation) {
+ //
+ // }
+ //
+ // @Override public void onStop(long stopLocation) {
+ //
+ // }
+ //
+ // @Override public void onComplete() {
+ //
+ // }
+ //});
+ //util.startDownload();
}
private void stop() {
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 464bb3a5..08ccd012 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -56,5 +56,14 @@
style="?buttonBarButtonStyle"
/>
+
+