commit
						20d6a79e20
					
				| @ -0,0 +1,3 @@ | ||||
| /build | ||||
| gradle.properties | ||||
| downloadutil.iml | ||||
| @ -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' | ||||
| } | ||||
| @ -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> T create(JsonElement json, Class<T> 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 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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 *; | ||||
| #} | ||||
| @ -0,0 +1,13 @@ | ||||
| package com.arialyy.downloadutil; | ||||
| 
 | ||||
| import android.app.Application; | ||||
| import android.test.ApplicationTestCase; | ||||
| 
 | ||||
| /** | ||||
|  * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> | ||||
|  */ | ||||
| public class ApplicationTest extends ApplicationTestCase<Application> { | ||||
|     public ApplicationTest() { | ||||
|         super(Application.class); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     package="com.arialyy.downloadutil"> | ||||
| 
 | ||||
|     <application | ||||
|         android:allowBackup="true" | ||||
|         android:label="@string/app_name" | ||||
|         android:supportsRtl="true"> | ||||
| 
 | ||||
|     </application> | ||||
| 
 | ||||
| </manifest> | ||||
| @ -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<Thread> 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; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -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() { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -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(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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(); | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <resources> | ||||
|     <string name="app_name">DownloadUtil</string> | ||||
| </resources> | ||||
| @ -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); | ||||
|     } | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue