commit 20d6a79e20952f838afd3da9885d9148bc6263f4 Author: yuyu lao Date: Fri Feb 19 22:42:25 2016 +0800 第一次提交 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2ce7571b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/build +gradle.properties +downloadutil.iml \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..688dda3f --- /dev/null +++ b/build.gradle @@ -0,0 +1,51 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.jfrog.bintray' +android { + compileSdkVersion 23 + buildToolsVersion "23.0.2" + + defaultConfig { + minSdkVersion 9 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + testCompile 'junit:junit:4.12' + compile 'com.android.support:appcompat-v7:23.1.1' +} + +bintray { + user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') + key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') + configurations = ['archives'] + pkg { + repo = 'generic' + name = 'gradle-bintray-plugin-example' + userOrg = user + licenses = ['Apache-2.0'] + vcsUrl = 'https://github.com/bintray/gradle-bintray-plugin.git' + labels = ['gear', 'gore', 'gorilla'] + publicDownloadNumbers = true + attributes= ['a': ['ay1', 'ay2'], 'b': ['bee'], c: 'cee'] //Optional package-level attributes + version { + name = '1.0-Final-Modules' + desc = 'Gradle Bintray Plugin 1.0 final' + vcsTag = '1.3.0' + attributes = ['gradle-plugin': 'com.use.less:com.use.less.gradle:gradle-useless-plugin'] + } + } +} + +task wrapper(type: Wrapper) { + gradleVersion = '2.4' +} diff --git a/jcenter.gradle b/jcenter.gradle new file mode 100644 index 00000000..6fa33347 --- /dev/null +++ b/jcenter.gradle @@ -0,0 +1,158 @@ +group = PROJ_GROUP +version = PROJ_VERSION +project.archivesBaseName = PROJ_ARTIFACTID +apply plugin: 'com.jfrog.bintray' +apply plugin: "com.jfrog.artifactory" +apply plugin: 'maven-publish' + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + options.encoding = "utf-8" + source = android.sourceSets.main.java.srcDirs + classpath += configurations.compile + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +javadoc { + options { + encoding "UTF-8" + charSet 'UTF-8' + author true + version true + links "http://docs.oracle.com/javase/7/docs/api" + title PROJ_ARTIFACTID + } +} + +//添加以下信息避免JAVADOC打包时引用其它类库而出现问题,比如出现以下错误 +// xxxx.java:20: 错误: 找不到符号 +// public static T create(JsonElement json, Class classOfModel) { +// ^ +// 符号: 类 JsonElement +// 位置: 类 xxxx +android.libraryVariants.all { variant -> + println variant.javaCompile.classpath.files + if (variant.name == 'release') { //我们只需 release 的 javadoc + task("generate${variant.name.capitalize()}Javadoc", type: Javadoc) { + // title = '' + // description = '' + source = variant.javaCompile.source + classpath = files(variant.javaCompile.classpath.files, project.android.getBootClasspath()) + options { + encoding "utf-8" + links "http://docs.oracle.com/javase/7/docs/api/" + linksOffline "http://d.android.com/reference", "${android.sdkDirectory}/docs/reference" + } + exclude '**/BuildConfig.java' + exclude '**/R.java' + } + task("javadoc${variant.name.capitalize()}Jar", type: Jar, dependsOn: "generate${variant.name.capitalize()}Javadoc") { + classifier = 'javadoc' + from tasks.getByName("generate${variant.name.capitalize()}Javadoc").destinationDir + } + artifacts { + archives tasks.getByName("javadoc${variant.name.capitalize()}Jar") + } + } +} + +artifacts { + archives javadocJar + archives sourcesJar +} + + +def pomConfig = { + scm { + connection PROJ_VCSURL + developerConnection PROJ_VCSURL + url PROJ_WEBSITEURL + } + licenses { + license { + name "The Apache Software License, Version 2.0" + url "http://www.apache.org/licenses/LICENSE-2.0.txt" + distribution "repo" + } + } + developers { + developer { + id DEVELOPER_ID + name DEVELOPER_NAME + email DEVELOPER_EMAIL + } + } +} + +publishing { + publications { + mavenJava(MavenPublication) { + artifactId PROJ_ARTIFACTID + artifact javadocJar + artifact sourcesJar + + pom { + packaging 'aar' + } + pom.withXml { + def root = asNode() + root.appendNode('description', PROJ_DESCRIPTION) + root.children().last() + pomConfig + } + } + } +} + +afterEvaluate { + publishing.publications.mavenJava.artifact(bundleRelease) +} + +bintray { + user = hasProperty("bintrayUser") ? getProperty("bintrayUser") : getProperty("BINTRAY_USER") + key = hasProperty("bintrayKey") ? getProperty("bintrayKey") : getProperty("BINTRAY_KEY") + +// configurations = ['archives'] + publications = ['mavenJava'] + publish = true + + pkg { + repo = 'maven' + name = PROJ_NAME + desc = PROJ_DESCRIPTION + websiteUrl = PROJ_WEBSITEURL + issueTrackerUrl = PROJ_ISSUETRACKERURL + vcsUrl = PROJ_VCSURL + licenses = ['Apache-2.0'] + publicDownloadNumbers = true + } +} + +artifactory { + contextUrl = 'http://oss.jfrog.org/artifactory' + resolve { + repository { + repoKey = 'libs-release' + } + } + publish { + repository { + repoKey = 'oss-snapshot-local' //The Artifactory repository key to publish to + username = bintray.user + password = bintray.key + maven = true + } + defaults { + publications('mavenJava') + publishArtifacts = true + } + } +} \ No newline at end of file diff --git a/proguard-rules.pro b/proguard-rules.pro new file mode 100644 index 00000000..80105608 --- /dev/null +++ b/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in D:\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/src/androidTest/java/com/arialyy/downloadutil/ApplicationTest.java b/src/androidTest/java/com/arialyy/downloadutil/ApplicationTest.java new file mode 100644 index 00000000..e2c01efd --- /dev/null +++ b/src/androidTest/java/com/arialyy/downloadutil/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.arialyy.downloadutil; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml new file mode 100644 index 00000000..838fc034 --- /dev/null +++ b/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/src/main/java/com/arialyy/downloadutil/DownLoadUtil.java b/src/main/java/com/arialyy/downloadutil/DownLoadUtil.java new file mode 100644 index 00000000..45674bb8 --- /dev/null +++ b/src/main/java/com/arialyy/downloadutil/DownLoadUtil.java @@ -0,0 +1,360 @@ +package com.arialyy.downloadutil; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.util.Log; +import android.util.SparseArray; + +import com.arialyy.downloadutil.inf.IDownloadListener; + +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 2015/8/25. + * 下载工具类 + */ +public class DownLoadUtil { + private static final String TAG = "DownLoadUtil"; + //下载监听 + private IDownloadListener mListener; + /** + * 线程数 + */ + private static final int THREAD_NUM = 3; + /** + * 已经完成下载任务的线程数量 + */ + private int mCompleteThreadNum = 0; + private long mCurrentLocation; + private boolean isDownloading = false; + private boolean isStop = false; + private boolean isCancel = false; + private static final int TIME_OUT = 5000; //超时时间 + boolean newTask = true; + private int mCancelNum = 0; + private int mStopNum = 0; + + public DownLoadUtil() { + } + + /** + * 获取当前下载位置 + * + * @return + */ + public long getCurrentLocation() { + return mCurrentLocation; + } + + public boolean isDownloading() { + return isDownloading; + } + + /** + * 取消下载 + */ + public void cancelDownload() { + isCancel = true; + } + + /** + * 停止下载 + */ + public void stopDownload() { + isStop = true; + } + + /** + * 多线程断点续传下载文件,暂停和继续 + * + * @param context 必须添加该参数,不能使用全局变量的context + * @param downloadUrl 下载路径 + * @param filePath 保存路径 + * @param downloadListener 下载进度监听 {@link DownloadListener} + */ + public void download(final Context context, @NonNull final String downloadUrl, @NonNull final String filePath, + @NonNull final DownloadListener downloadListener) { + isDownloading = true; + mCurrentLocation = 0; + isStop = false; + isCancel = false; + mCancelNum = 0; + mStopNum = 0; + final File dFile = new File(filePath); + //读取已完成的线程数 + final File configFile = new File(context.getFilesDir().getPath() + "/temp/" + dFile.getName() + ".properties"); + try { + if (!configFile.exists()) { //记录文件被删除,则重新下载 + newTask = true; + Util.createFile(configFile.getPath()); + } else { + newTask = false; + } + } catch (Exception e) { + e.printStackTrace(); + mListener.onFail(); + return; + } + newTask = !dFile.exists(); + new Thread(new Runnable() { + @Override + public void run() { + try { + mListener = downloadListener; + URL url = new URL(downloadUrl); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Charset", "UTF-8"); + conn.setConnectTimeout(TIME_OUT); + 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, */*"); + conn.connect(); + int len = conn.getContentLength(); + if (len < 0) { //网络被劫持时会出现这个问题 + mListener.onFail(); + return; + } + int code = conn.getResponseCode(); + if (code == 200) { + int fileLength = conn.getContentLength(); + //必须建一个文件 + Util.createFile(filePath); + RandomAccessFile file = new RandomAccessFile(filePath, "rwd"); + //设置文件长度 + file.setLength(fileLength); + mListener.onPreDownload(conn); + //分配每条线程的下载区间 + Properties pro = null; + pro = Util.loadConfig(configFile); + int blockSize = fileLength / THREAD_NUM; + SparseArray tasks = new SparseArray<>(); + for (int i = 0; i < THREAD_NUM; i++) { + long startL = i * blockSize, endL = (i + 1) * blockSize; + Object state = pro.getProperty(dFile.getName() + "_state_" + i); + if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成 + mCurrentLocation += endL - startL; + Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++"); + mCompleteThreadNum++; + if (mCompleteThreadNum == THREAD_NUM) { + if (configFile.exists()) { + configFile.delete(); + } + mListener.onComplete(); + isDownloading = false; + System.gc(); + return; + } + continue; + } + //分配下载位置 + Object record = pro.getProperty(dFile.getName() + "_record_" + i); + if (!newTask && record != null && Long.parseLong(record + "") > 0) { //如果有记录,则恢复下载 + Long r = Long.parseLong(record + ""); + mCurrentLocation += r - startL; + Log.d(TAG, "++++++++++ 线程_" + i + "_恢复下载 ++++++++++"); + mListener.onChildResume(r); + startL = r; + } + if (i == (THREAD_NUM - 1)) { + endL = fileLength;//如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度 + } + DownloadEntity entity = new DownloadEntity(context, fileLength, downloadUrl, dFile, i, startL, endL); + DownLoadTask task = new DownLoadTask(entity); + tasks.put(i, new Thread(task)); + } + if (mCurrentLocation > 0) { + mListener.onResume(mCurrentLocation); + } else { + mListener.onStart(mCurrentLocation); + } + for (int i = 0, count = tasks.size(); i < count; i++) { + Thread task = tasks.get(i); + if (task != null) { + task.start(); + } + } + } else { + Log.e(TAG, "下载失败,返回码:" + code); + isDownloading = false; + System.gc(); + mListener.onFail(); + } + } catch (IOException e) { + Log.e(TAG, "下载失败【downloadUrl:" + downloadUrl + "】\n【filePath:" + filePath + "】" + Util.getPrintException(e)); + isDownloading = false; + mListener.onFail(); + } + } + }).start(); + } + + /** + * 多线程下载任务类,不能使用AsyncTask来进行多线程下载,因为AsyncTask是串行执行的,这种方式下载速度太慢了 + */ + private class DownLoadTask implements Runnable { + private static final String TAG = "DownLoadTask"; + private DownloadEntity dEntity; + private String configFPath; + + public DownLoadTask(DownloadEntity downloadInfo) { + this.dEntity = downloadInfo; + configFPath = dEntity.context.getFilesDir().getPath() + "/temp/" + dEntity.tempFile.getName() + ".properties"; + } + + @Override + public void run() { + try { + Log.d(TAG, "线程_" + dEntity.threadId + "_正在下载【" + "开始位置 : " + dEntity.startLocation + ",结束位置:" + dEntity.endLocation + "】"); + URL url = new URL(dEntity.downloadUrl); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + //在头里面请求下载开始位置和结束位置 + conn.setRequestProperty("Range", "bytes=" + dEntity.startLocation + "-" + dEntity.endLocation); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Charset", "UTF-8"); + conn.setConnectTimeout(TIME_OUT); + 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, */*"); + conn.setReadTimeout(2000); //设置读取流的等待时间,必须设置该参数 + InputStream is = conn.getInputStream(); + //创建可设置位置的文件 + RandomAccessFile file = new RandomAccessFile(dEntity.tempFile, "rwd"); + //设置每条线程写入文件的位置 + file.seek(dEntity.startLocation); + byte[] buffer = new byte[1024]; + int len; + //当前子线程的下载位置 + long currentLocation = dEntity.startLocation; + while ((len = is.read(buffer)) != -1) { + if (isCancel) { + Log.d(TAG, "++++++++++ thread_" + dEntity.threadId + "_cancel ++++++++++"); + break; + } + + if (isStop) { + break; + } + + //把下载数据数据写入文件 + file.write(buffer, 0, len); + synchronized (DownLoadUtil.this) { + mCurrentLocation += len; + mListener.onProgress(mCurrentLocation); + } + currentLocation += len; + } + file.close(); + is.close(); + + if (isCancel) { + synchronized (DownLoadUtil.this) { + mCancelNum++; + if (mCancelNum == THREAD_NUM) { + File configFile = new File(configFPath); + if (configFile.exists()) { + configFile.delete(); + } + + if (dEntity.tempFile.exists()) { + dEntity.tempFile.delete(); + } + Log.d(TAG, "++++++++++++++++ onCancel +++++++++++++++++"); + isDownloading = false; + mListener.onCancel(); + System.gc(); + } + } + return; + } + + //停止状态不需要删除记录文件 + if (isStop) { + synchronized (DownLoadUtil.this) { + mStopNum++; + String location = String.valueOf(currentLocation); + Log.i(TAG, "thread_" + dEntity.threadId + "_stop, stop location ==> " + currentLocation); + writeConfig(dEntity.tempFile.getName() + "_record_" + dEntity.threadId, location); + if (mStopNum == THREAD_NUM) { + Log.d(TAG, "++++++++++++++++ onStop +++++++++++++++++"); + isDownloading = false; + mListener.onStop(mCurrentLocation); + System.gc(); + } + } + return; + } + + Log.i(TAG, "线程【" + dEntity.threadId + "】下载完毕"); + writeConfig(dEntity.tempFile.getName() + "_state_" + dEntity.threadId, 1 + ""); + mListener.onChildComplete(dEntity.endLocation); + mCompleteThreadNum++; + if (mCompleteThreadNum == THREAD_NUM) { + File configFile = new File(configFPath); + if (configFile.exists()) { + configFile.delete(); + } + mListener.onComplete(); + isDownloading = false; + System.gc(); + } + } catch (MalformedURLException e) { + e.printStackTrace(); + isDownloading = false; + mListener.onFail(); + } catch (IOException e) { + Log.e(TAG, "下载失败【" + dEntity.downloadUrl + "】" + Util.getPrintException(e)); + isDownloading = false; + mListener.onFail(); + } catch (Exception e) { + Log.e(TAG, "获取流失败" + Util.getPrintException(e)); + isDownloading = false; + mListener.onFail(); + } + } + + /** + * 将记录写入到配置文件 + * + * @param record + */ + private void writeConfig(String key, String record) throws IOException { + File configFile = new File(configFPath); + Properties pro = Util.loadConfig(configFile); + pro.setProperty(key, record); + Util.saveConfig(configFile, pro); + } + } + + + /** + * 子线程下载信息类 + */ + private class DownloadEntity { + //文件大小 + long fileSize; + String downloadUrl; + int threadId; + long startLocation; + long endLocation; + File tempFile; + Context context; + + public DownloadEntity(Context context, long fileSize, String downloadUrl, File file, int threadId, long startLocation, long endLocation) { + this.fileSize = fileSize; + this.downloadUrl = downloadUrl; + this.tempFile = file; + this.threadId = threadId; + this.startLocation = startLocation; + this.endLocation = endLocation; + this.context = context; + } + } + + +} diff --git a/src/main/java/com/arialyy/downloadutil/DownloadListener.java b/src/main/java/com/arialyy/downloadutil/DownloadListener.java new file mode 100644 index 00000000..66e4229f --- /dev/null +++ b/src/main/java/com/arialyy/downloadutil/DownloadListener.java @@ -0,0 +1,61 @@ +package com.arialyy.downloadutil; + +import com.arialyy.downloadutil.inf.IDownloadListener; + +import java.net.HttpURLConnection; + +/** + * 下载监听 + */ +public class DownloadListener implements IDownloadListener { + + @Override + public void onResume(long resumeLocation) { + + } + + @Override + public void onCancel() { + + } + + @Override + public void onFail() { + + } + + @Override + public void onPreDownload(HttpURLConnection connection) { + + } + + @Override + public void onProgress(long currentLocation) { + + } + + @Override + public void onChildComplete(long finishLocation) { + + } + + @Override + public void onStart(long startLocation) { + + } + + @Override + public void onChildResume(long resumeLocation) { + + } + + @Override + public void onStop(long stopLocation) { + + } + + @Override + public void onComplete() { + + } +} \ No newline at end of file diff --git a/src/main/java/com/arialyy/downloadutil/Util.java b/src/main/java/com/arialyy/downloadutil/Util.java new file mode 100644 index 00000000..aebab92b --- /dev/null +++ b/src/main/java/com/arialyy/downloadutil/Util.java @@ -0,0 +1,175 @@ +package com.arialyy.downloadutil; + +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.Properties; + +/** + * Created by lyy on 2016/1/22. + */ +public class Util { + private static final String TAG = "util"; + + /** + * 格式化文件大小 + * + * @param size file.length() 获取文件大小 + * @return + */ + public static String formatFileSize(double size) { + double kiloByte = size / 1024; + if (kiloByte < 1) { + return size + "Byte(s)"; + } + + double megaByte = kiloByte / 1024; + if (megaByte < 1) { + BigDecimal result1 = new BigDecimal(Double.toString(kiloByte)); + return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB"; + } + + double gigaByte = megaByte / 1024; + if (gigaByte < 1) { + BigDecimal result2 = new BigDecimal(Double.toString(megaByte)); + return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB"; + } + + double teraBytes = gigaByte / 1024; + if (teraBytes < 1) { + BigDecimal result3 = new BigDecimal(Double.toString(gigaByte)); + return result3.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "GB"; + } + BigDecimal result4 = new BigDecimal(teraBytes); + return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB"; + } + /** + * 创建目录 当目录不存在的时候创建文件,否则返回false + * + * @param path + * @return + */ + public static boolean createDir(String path) { + File file = new File(path); + if (!file.exists()) { + if (!file.mkdirs()) { + Log.d(TAG, "创建失败,请检查路径和是否配置文件权限!"); + return false; + } + return true; + } + return false; + } + + /** + * 创建文件 当文件不存在的时候就创建一个文件,否则直接返回文件 + * + * @param path + * @return + */ + public static File createFile(String path) { + File file = new File(path); + if (!file.getParentFile().exists()) { + Log.d(TAG, "目标文件所在路径不存在,准备创建……"); + if (!createDir(file.getParent())) { + Log.d(TAG, "创建目录文件所在的目录失败!文件路径【" + path + "】"); + } + } + // 创建目标文件 + try { + if (!file.exists()) { + if (file.createNewFile()) { + Log.d(TAG, "创建文件成功:" + file.getAbsolutePath()); + } + return file; + } else { + return file; + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + /** + * 设置打印的异常格式 + */ + public static String getPrintException(Throwable ex) { + StringBuilder err = new StringBuilder(); + err.append("ExceptionDetailed:\n"); + err.append("====================Exception Info====================\n"); + err.append(ex.toString()); + err.append("\n"); + StackTraceElement[] stack = ex.getStackTrace(); + for (StackTraceElement stackTraceElement : stack) { + err.append(stackTraceElement.toString()).append("\n"); + } + Throwable cause = ex.getCause(); + if (cause != null) { + err.append("【Caused by】: "); + err.append(cause.toString()); + err.append("\n"); + StackTraceElement[] stackTrace = cause.getStackTrace(); + for (StackTraceElement stackTraceElement : stackTrace) { + err.append(stackTraceElement.toString()).append("\n"); + } + } + err.append("==================================================="); + return err.toString(); + } + /** + * 读取下载配置文件 + * + * @param file + * @return + */ + public static Properties loadConfig(File file) { + Properties properties = new Properties(); + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + properties.load(fis); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (fis != null) { + fis.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return properties; + } + + /** + * 保存配置文件 + * + * @param file + * @param properties + */ + public static void saveConfig(File file, Properties properties) { + FileOutputStream fos = null; + try { + fos = new FileOutputStream(file, false); + properties.store(fos, null); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (fos != null) { + fos.flush(); + fos.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/com/arialyy/downloadutil/inf/IDownloadListener.java b/src/main/java/com/arialyy/downloadutil/inf/IDownloadListener.java new file mode 100644 index 00000000..4999a55f --- /dev/null +++ b/src/main/java/com/arialyy/downloadutil/inf/IDownloadListener.java @@ -0,0 +1,59 @@ +package com.arialyy.downloadutil.inf; + +import java.net.HttpURLConnection; + +/** + * 下载监听 + */ +public interface IDownloadListener { + /** + * 取消下载 + */ + public void onCancel(); + + /** + * 下载失败 + */ + public void onFail(); + + /** + * 下载预处理,可通过HttpURLConnection获取文件长度 + */ + public void onPreDownload(HttpURLConnection connection); + + /** + * 下载监听 + */ + public void onProgress(long currentLocation); + + /** + * 单一线程的结束位置 + */ + public void onChildComplete(long finishLocation); + + /** + * 开始 + */ + public void onStart(long startLocation); + + /** + * 子程恢复下载的位置 + */ + public void onChildResume(long resumeLocation); + + /** + * 恢复位置 + */ + public void onResume(long resumeLocation); + + /** + * 停止 + */ + public void onStop(long stopLocation); + + /** + * 下载完成 + */ + public void onComplete(); + +} \ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml new file mode 100644 index 00000000..650de897 --- /dev/null +++ b/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + DownloadUtil + diff --git a/src/test/java/com/arialyy/downloadutil/ExampleUnitTest.java b/src/test/java/com/arialyy/downloadutil/ExampleUnitTest.java new file mode 100644 index 00000000..753847a5 --- /dev/null +++ b/src/test/java/com/arialyy/downloadutil/ExampleUnitTest.java @@ -0,0 +1,15 @@ +package com.arialyy.downloadutil; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * To work on unit tests, switch the Test Artifact in the Build Variants view. + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file