diff --git a/.idea/modules.xml b/.idea/modules.xml index b0a774eb..d68bc4ee 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,8 +2,8 @@ - - + + 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/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 b9dcb150..d100d1a3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -217,6 +217,10 @@ import java.util.Set; key = clsName; } handlePopupWindowLift((PopupWindow) obj); + } else if (obj instanceof Service) { + key = clsName; + } else if (obj instanceof Application) { + key = clsName; } } else { key = clsName; 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/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 80714b08..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,61 +20,42 @@ import android.content.Context; import android.util.Log; import android.util.SparseArray; import com.arialyy.aria.core.DownloadEntity; -import com.arialyy.aria.util.CAConfiguration; +import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.CommonUtil; -import com.arialyy.aria.util.SSLContextUtil; 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.net.URLConnection; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; /** * Created by lyy on 2015/8/25. * 下载工具类 */ -final class DownloadUtil implements IDownloadUtil, Runnable { +public class DownloadUtil implements IDownloadUtil, Runnable { private static final String TAG = "DownloadUtil"; - private static final Object LOCK = new Object(); /** * 线程数 */ - private final int THREAD_NUM; + private final int THREAD_NUM; //下载监听 - 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 IDownloadListener mListener; + private int mConnectTimeOut = 5000 * 4; //连接超时时间 + private int mReadTimeOut = 5000 * 20; //流读取的超时时间 + 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 Context mContext; + private DownloadEntity mDownloadEntity; private ExecutorService mFixedThreadPool; - private File mDownloadFile; //下载的文件 - private File mConfigFile;//下载信息配置文件 + 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); } @@ -85,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(); } @@ -128,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); @@ -154,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); @@ -195,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(); } @@ -212,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 = handleConnection(url); - setConnectParam(conn); + HttpURLConnection conn = ConnectionHelp.handleConnection(url); + conn = ConnectionHelp.setConnectParam(conn); conn.setRequestProperty("Range", "bytes=" + 0 + "-"); conn.setConnectTimeout(mConnectTimeOut * 4); conn.connect(); @@ -269,44 +236,23 @@ final class DownloadUtil implements IDownloadUtil, Runnable { } } - /** - * 处理链接 - * - * @throws IOException - */ - private 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; - } - /** * 处理断点 */ 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; @@ -314,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); @@ -345,17 +294,17 @@ final class DownloadUtil implements IDownloadUtil, Runnable { 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; @@ -365,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; @@ -383,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; @@ -409,229 +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(); - conn = handleConnection(url); - 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..317b9da1 --- /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", 8192); + //设置文件长度 + 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/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/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/README.md b/README.md index c9204f59..39c7847b 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ ## 下载 [![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.4.0' +compile 'com.arialyy.aria:Aria:2.4.1' ``` ## 示例 @@ -134,6 +134,7 @@ Aria支持https下载,如果你希望使用自己的ca证书,那么你需要 *** ## 开发日志 + + v_2.4.1 修复下载慢的问题,修复application、service 不能使用的问题 + v_2.4.0 支持https链接下载 + v_2.3.8 修复数据错乱的bug、添加fragment支持 + v_2.3.6 添加dialog、popupWindow支持 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/single_task/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/single_task/SingleTaskActivity.java index 82d62924..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; @@ -52,7 +54,8 @@ public class SingleTaskActivity extends BaseActivity { private static final String DOWNLOAD_URL = //"http://kotlinlang.org/docs/kotlin-docs.pdf"; - "https://atom-installer.github.com/v1.13.0/AtomSetup.exe?s=1484074138&ext=.exe"; + //"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; @@ -197,6 +200,67 @@ public class SingleTaskActivity extends BaseActivity { .setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/test.apk") .setDownloadName("test.apk") .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() {