├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── MyApplication2.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bbk │ │ └── lling │ │ └── myapplication │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── bbk │ │ └── lling │ │ └── myapplication │ │ └── AsyncTaskDemoActivity.java │ └── res │ ├── layout │ └── activity_async_task_demo.xml │ ├── menu │ └── menu_async_task_demo.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── example.gif ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── multitaskdownload ├── .gitignore ├── build.gradle ├── multitaskdownload.iml ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bbk │ │ └── lling │ │ └── multitaskdownload │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── bbk │ │ └── lling │ │ └── multitaskdownload │ │ ├── activitys │ │ └── MainActivity.java │ │ ├── adapters │ │ └── AppContentAdapter.java │ │ ├── beans │ │ ├── AppContent.java │ │ └── DownloadInfo.java │ │ ├── db │ │ ├── DBHelper.java │ │ ├── DownloadFileDAO.java │ │ └── DownloadInfoDAO.java │ │ ├── downloador │ │ ├── Constants.java │ │ ├── DownloadTask.java │ │ └── Downloador.java │ │ ├── services │ │ └── DownloadService.java │ │ ├── utils │ │ └── DownloadUtils.java │ │ └── view │ │ └── DownloadPercentView.java │ └── res │ ├── drawable-xhdpi │ ├── ic_finished.png │ ├── ic_no_download.png │ ├── ic_pause.png │ └── ic_wait.png │ ├── layout │ ├── activity_main.xml │ └── listitem_download.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── attrs.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── multithreaddownload ├── .gitignore ├── build.gradle ├── multithreaddownload.iml ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bbk │ │ └── lling │ │ └── multithreaddownload │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── bbk │ │ └── lling │ │ └── multithreaddownload │ │ ├── MainActivity.java │ │ └── MainActivity2.java │ └── res │ ├── layout │ ├── activity_main.xml │ └── main_activity2.xml │ ├── menu │ ├── menu_main.xml │ └── menu_main_activity2.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | My Application -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 1.7 27 | 28 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MyApplication2.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MultiTaskAndThreadDownload 2 | 使用AsyncTask实现的多任务多线程下载的Demo,支持断点续传。 3 | 4 | # Demo演示 5 | ![image](https://raw.githubusercontent.com/liuling07/MultiTaskAndThreadDownload/master/example.gif) 6 | 7 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 21 5 | buildToolsVersion "20.0.0" 6 | 7 | defaultConfig { 8 | applicationId "com.bbk.lling.myapplication" 9 | minSdkVersion 11 10 | targetSdkVersion 21 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:21.0.2' 25 | } 26 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in F:\liuling\adt-bundle-windows-x86_64-20140702\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/bbk/lling/myapplication/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.bbk.lling.myapplication; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/bbk/lling/myapplication/AsyncTaskDemoActivity.java: -------------------------------------------------------------------------------- 1 | package com.bbk.lling.myapplication; 2 | 3 | import android.app.Activity; 4 | import android.os.AsyncTask; 5 | import android.os.Bundle; 6 | import android.os.Environment; 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.widget.ProgressBar; 10 | import android.widget.Toast; 11 | 12 | import org.apache.http.Header; 13 | import org.apache.http.HttpResponse; 14 | import org.apache.http.client.HttpClient; 15 | import org.apache.http.client.methods.HttpGet; 16 | import org.apache.http.impl.client.DefaultHttpClient; 17 | import org.apache.http.message.BasicHeader; 18 | 19 | import java.io.File; 20 | import java.io.FileOutputStream; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.io.OutputStream; 24 | import java.io.RandomAccessFile; 25 | import java.net.MalformedURLException; 26 | 27 | public class AsyncTaskDemoActivity extends Activity { 28 | 29 | private ProgressBar progressBar; 30 | //下载路径 31 | private String downloadPath = Environment.getExternalStorageDirectory() + 32 | File.separator + "download"; 33 | private DownloadAsyncTask downloadTask; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_async_task_demo); 39 | progressBar = (ProgressBar) findViewById(R.id.progressbar); 40 | //开始下载 41 | findViewById(R.id.begin).setOnClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View v) { 44 | /** 45 | * 一个AsyncTask只能被执行一次,否则会抛异常 46 | * java.lang.IllegalStateException: Cannot execute task: the task is already running. 47 | * 如果要重新开始任务的话要重新创建AsyncTask对象 48 | */ 49 | if(downloadTask != null && !downloadTask.isCancelled()) { 50 | return; 51 | } 52 | downloadTask = new DownloadAsyncTask("http://bbk-lewen.u.qiniudn.com/3d5b1a2c-4986-4e4a-a626-b504a36e600a.flv"); 53 | downloadTask.execute(); 54 | } 55 | }); 56 | 57 | //暂停下载 58 | findViewById(R.id.end).setOnClickListener(new View.OnClickListener() { 59 | @Override 60 | public void onClick(View v) { 61 | if(downloadTask != null && downloadTask.getStatus() == AsyncTask.Status.RUNNING) { 62 | downloadTask.cancel(true); 63 | } 64 | } 65 | }); 66 | 67 | } 68 | 69 | /** 70 | * 下载的AsyncTask 71 | */ 72 | private class DownloadAsyncTask extends AsyncTask { 73 | private static final String TAG = "DownloadAsyncTask"; 74 | private String mUrl; 75 | 76 | public DownloadAsyncTask(String url) { 77 | this.mUrl = url; 78 | } 79 | 80 | @Override 81 | protected Long doInBackground(String... params) { 82 | Log.i(TAG, "downloading"); 83 | if(mUrl == null) { 84 | return null; 85 | } 86 | HttpClient client = new DefaultHttpClient(); 87 | HttpGet request = new HttpGet(mUrl); 88 | HttpResponse response = null; 89 | InputStream is = null; 90 | RandomAccessFile fos = null; 91 | OutputStream output = null; 92 | 93 | try { 94 | //创建存储文件夹 95 | File dir = new File(downloadPath); 96 | if(!dir.exists()) { 97 | dir.mkdir(); 98 | } 99 | //本地文件 100 | File file = new File(downloadPath + File.separator + mUrl.substring(mUrl.lastIndexOf("/") + 1)); 101 | if(!file.exists()){ 102 | //创建文件输出流 103 | output = new FileOutputStream(file); 104 | //获取下载输入流 105 | response = client.execute(request); 106 | is = response.getEntity().getContent(); 107 | //写入本地 108 | file.createNewFile(); 109 | byte buffer [] = new byte[1024]; 110 | int inputSize = -1; 111 | //获取文件总大小,用于计算进度 112 | long total = response.getEntity().getContentLength(); 113 | int count = 0; //已下载大小 114 | while((inputSize = is.read(buffer)) != -1) { 115 | output.write(buffer, 0, inputSize); 116 | count += inputSize; 117 | //更新进度 118 | this.publishProgress((int) ((count / (float) total) * 100)); 119 | //一旦任务被取消则退出循环,否则一直执行,直到结束 120 | if(isCancelled()) { 121 | output.flush(); 122 | return null; 123 | } 124 | } 125 | output.flush(); 126 | } else { 127 | long readedSize = file.length(); //文件大小,即已下载大小 128 | //设置下载的数据位置XX字节到XX字节 129 | Header header_size = new BasicHeader("Range", "bytes=" + readedSize + "-"); 130 | request.addHeader(header_size); 131 | //执行请求获取下载输入流 132 | response = client.execute(request); 133 | is = response.getEntity().getContent(); 134 | //文件总大小=已下载大小+未下载大小 135 | long total = readedSize + response.getEntity().getContentLength(); 136 | 137 | //创建文件输出流 138 | fos = new RandomAccessFile(file, "rw"); 139 | //从文件的size以后的位置开始写入,其实也不用,直接往后写就可以。有时候多线程下载需要用 140 | fos.seek(readedSize); 141 | //这里用RandomAccessFile和FileOutputStream都可以,只是使用FileOutputStream的时候要传入第二哥参数true,表示从后面填充 142 | // output = new FileOutputStream(file, true); 143 | 144 | byte buffer [] = new byte[1024]; 145 | int inputSize = -1; 146 | int count = (int)readedSize; 147 | while((inputSize = is.read(buffer)) != -1) { 148 | fos.write(buffer, 0, inputSize); 149 | // output.write(buffer, 0, inputSize); 150 | count += inputSize; 151 | this.publishProgress((int) ((count / (float) total) * 100)); 152 | if(isCancelled()) { 153 | // output.flush(); 154 | return null; 155 | } 156 | } 157 | // output.flush(); 158 | } 159 | } catch (MalformedURLException e) { 160 | Log.e(TAG, e.getMessage()); 161 | } catch (IOException e) { 162 | Log.e(TAG, e.getMessage()); 163 | } finally{ 164 | try{ 165 | if(is != null) { 166 | is.close(); 167 | } 168 | if(output != null) { 169 | output.close(); 170 | } 171 | if(fos != null) { 172 | fos.close(); 173 | } 174 | } catch(Exception e) { 175 | e.printStackTrace(); 176 | } 177 | } 178 | return null; 179 | } 180 | 181 | @Override 182 | protected void onPreExecute() { 183 | Log.i(TAG, "download begin "); 184 | Toast.makeText(AsyncTaskDemoActivity.this, "开始下载", Toast.LENGTH_SHORT).show(); 185 | super.onPreExecute(); 186 | } 187 | 188 | @Override 189 | protected void onProgressUpdate(Integer... values) { 190 | super.onProgressUpdate(values); 191 | Log.i(TAG, "downloading " + values[0]); 192 | //更新界面进度条 193 | progressBar.setProgress(values[0]); 194 | } 195 | 196 | @Override 197 | protected void onPostExecute(Long aLong) { 198 | Log.i(TAG, "download success " + aLong); 199 | Toast.makeText(AsyncTaskDemoActivity.this, "下载结束", Toast.LENGTH_SHORT).show(); 200 | super.onPostExecute(aLong); 201 | } 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_async_task_demo.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 |