http://blog.csdn.net/shimiso/article/details/6763986
多线程断点续传下载,这在实际项目中很常用.
main.xml:
-
<?xml version="1.0" encoding="utf-8"?>
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-
android:orientation="vertical"
-
android:layout_width="fill_parent"
-
android:layout_height="fill_parent"
-
>
-
<EditText
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
android:id="@+id/editText"
-
android:text="http://gongxue.cn/yingyinkuaiche/UploadFiles_9323/201008/2010082909434077.mp3"
-
/>
-
<LinearLayout
-
android:orientation="horizontal"
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
>
-
<Button
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:id="@+id/downButton"
-
android:text="Download"
-
/>
-
<Button
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:id="@+id/pauseButton"
-
android:enabled="false"
-
android:text="Pause"
-
/>
-
</LinearLayout>
-
-
<ProgressBar
-
android:layout_width="match_parent"
-
android:layout_height="18dp"
-
style="?android:attr/progressBarStyleHorizontal"
-
android:id="@+id/progressBar"
-
/>
-
<TextView
-
android:layout_width="fill_parent"
-
android:layout_height="wrap_content"
-
android:id="@+id/textView"
-
android:gravity="center"
-
/>
-
</LinearLayout>
String.xml:
-
<?xml version="1.0" encoding="utf-8"?>
-
<resources>
-
<string name="hello">Hello World, Main!</string>
-
<string name="app_name">多线程断点续传下载</string>
-
</resources>
AndroidManifest.xml:
-
<?xml version="1.0" encoding="utf-8"?>
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-
package="sms.multithreaddownload"
-
android:versionCode="1"
-
android:versionName="1.0">
-
<application android:icon="@drawable/icon" android:label="@string/app_name">
-
<activity android:name=".Main"
-
android:label="@string/app_name">
-
<intent-filter>
-
<action android:name="android.intent.action.MAIN" />
-
<category android:name="android.intent.category.LAUNCHER" />
-
</intent-filter>
-
</activity>
-
<uses-library android:name="android.test.runner" />
-
</application>
-
<uses-sdk android:minSdkVersion="8" />
-
<instrumentation
-
android:targetPackage="sms.multithreaddownload"
-
android:name="android.test.InstrumentationTestRunner" />
-
-
<uses-permission android:name="android.permission.INTERNET"/>
-
-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
-
-
</manifest>
activity程序:
工具类:
-
package sms.multithreaddownload.bean;
-
-
import android.content.Context;
-
import android.database.sqlite.SQLiteDatabase;
-
import android.database.sqlite.SQLiteOpenHelper;
-
-
public class DBHelper extends SQLiteOpenHelper {
-
-
public DBHelper(Context context) {
-
super(context, "MultiDownLoad.db", null, 1);
-
}
-
-
@Override
-
public void onCreate(SQLiteDatabase db) {
-
db.execSQL("CREATE TABLE fileDownloading(_id integer primary key autoincrement,downPath varchar(100),threadId INTEGER,downLength INTEGER)");
-
}
-
-
@Override
-
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-
-
-
}
-
-
}
-
package sms.multithreaddownload.bean;
-
-
public interface DownloadListener {
-
public void onDownload(int downloaded_size);
-
}
-
package sms.multithreaddownload.bean;
-
-
import java.io.File;
-
import java.io.InputStream;
-
import java.io.RandomAccessFile;
-
import java.net.HttpURLConnection;
-
import java.net.URL;
-
-
import sms.multithreaddownload.service.DownloadService;
-
-
import android.util.Log;
-
-
public final class MultiThreadDownload implements Runnable {
-
public int id;
-
private RandomAccessFile savedFile;
-
private String path;
-
-
public int currentDownloadSize = 0;
-
-
public boolean finished;
-
-
private final DownloadService downloadService;
-
-
public int start;
-
-
private int end;
-
-
public MultiThreadDownload(int id, File savedFile, int block, String path, Integer downlength, DownloadService downloadService) throws Exception {
-
this.id = id;
-
this.path = path;
-
if (downlength != null) this.currentDownloadSize = downlength;
-
this.savedFile = new RandomAccessFile(savedFile, "rwd");
-
this.downloadService = downloadService;
-
start = id * block + currentDownloadSize;
-
end = (id + 1) * block;
-
}
-
-
@Override
-
public void run() {
-
try {
-
HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
-
conn.setConnectTimeout(5000);
-
conn.setRequestMethod("GET");
-
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
-
-
InputStream in = conn.getInputStream();
-
byte[] buffer = new byte[1024];
-
int len = 0;
-
savedFile.seek(start);
-
while (!downloadService.isPause && (len = in.read(buffer)) != -1) {
-
savedFile.write(buffer, 0, len);
-
currentDownloadSize += len;
-
}
-
savedFile.close();
-
in.close();
-
conn.disconnect();
-
if (!downloadService.isPause) Log.i(DownloadService.TAG, "Thread " + (this.id + 1) + "finished");
-
finished = true;
-
} catch (Exception e) {
-
e.printStackTrace();
-
throw new RuntimeException("File downloading error!");
-
}
-
}
-
}
service类:
-
package sms.multithreaddownload.service;
-
-
import java.io.File;
-
import java.io.RandomAccessFile;
-
import java.net.HttpURLConnection;
-
import java.net.URL;
-
import java.util.HashMap;
-
import java.util.List;
-
import java.util.Map;
-
import java.util.Map.Entry;
-
import java.util.UUID;
-
import java.util.concurrent.ConcurrentHashMap;
-
import java.util.regex.Matcher;
-
import java.util.regex.Pattern;
-
-
import sms.multithreaddownload.bean.DBHelper;
-
import sms.multithreaddownload.bean.DownloadListener;
-
import sms.multithreaddownload.bean.MultiThreadDownload;
-
-
import android.content.Context;
-
import android.database.Cursor;
-
import android.database.sqlite.SQLiteDatabase;
-
-
-
public class DownloadService {
-
public static final String TAG = "tag";
-
-
private DBHelper dbHelper;
-
-
public int fileSize;
-
-
private int block;
-
-
private File savedFile;
-
-
private String path;
-
-
public boolean isPause;
-
-
private MultiThreadDownload[] threads;
-
-
private Map<Integer, Integer> downloadedLength = new ConcurrentHashMap<Integer, Integer>();
-
-
public DownloadService(String target, File destination, int thread_size, Context context) throws Exception {
-
dbHelper = new DBHelper(context);
-
this.threads = new MultiThreadDownload[thread_size];
-
this.path = target;
-
URL url = new URL(target);
-
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
-
conn.setConnectTimeout(5000);
-
conn.setRequestMethod("GET");
-
-
if (conn.getResponseCode() != 200) {
-
throw new RuntimeException("server no response!");
-
}
-
-
fileSize = conn.getContentLength();
-
if (fileSize <= 0) {
-
throw new RuntimeException("file is incorrect!");
-
}
-
String fileName = getFileName(conn);
-
if (!destination.exists()) destination.mkdirs();
-
-
this.savedFile = new File(destination, fileName);
-
RandomAccessFile doOut = new RandomAccessFile(savedFile, "rwd");
-
doOut.setLength(fileSize);
-
doOut.close();
-
conn.disconnect();
-
-
-
this.block = fileSize % thread_size == 0 ? fileSize / thread_size : fileSize / thread_size + 1;
-
-
downloadedLength = this.getDownloadedLength(path);
-
}
-
-
private Map<Integer, Integer> getDownloadedLength(String path) {
-
SQLiteDatabase db = dbHelper.getReadableDatabase();
-
String sql = "SELECT threadId,downLength FROM fileDownloading WHERE downPath=?";
-
Cursor cursor = db.rawQuery(sql, new String[] { path });
-
Map<Integer, Integer> data = new HashMap<Integer, Integer>();
-
while (cursor.moveToNext()) {
-
data.put(cursor.getInt(0), cursor.getInt(1));
-
}
-
db.close();
-
return data;
-
}
-
-
private String getFileName(HttpURLConnection conn) {
-
String fileName = path.substring(path.lastIndexOf("/") + 1, path.length());
-
if (fileName == null || "".equals(fileName.trim())) {
-
String content_disposition = null;
-
for (Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
-
if ("content-disposition".equalsIgnoreCase(entry.getKey())) {
-
content_disposition = entry.getValue().toString();
-
}
-
}
-
try {
-
Matcher matcher = Pattern.compile(".*filename=(.*)").matcher(content_disposition);
-
if (matcher.find()) fileName = matcher.group(1);
-
} catch (Exception e) {
-
fileName = UUID.randomUUID().toString() + ".tmp";
-
}
-
}
-
return fileName;
-
}
-
-
public void download(DownloadListener listener) throws Exception {
-
this.deleteDownloading();
-
for (int i = 0; i < threads.length; i++) {
-
threads[i] = new MultiThreadDownload(i, savedFile, block, path, downloadedLength.get(i), this);
-
new Thread(threads[i]).start();
-
}
-
this.saveDownloading(threads);
-
-
while (!isFinish(threads)) {
-
Thread.sleep(900);
-
if (listener != null) listener.onDownload(getDownloadedSize(threads));
-
this.updateDownloading(threads);
-
}
-
if (!this.isPause) this.deleteDownloading();
-
}
-
-
private void saveDownloading(MultiThreadDownload[] threads) {
-
SQLiteDatabase db = dbHelper.getWritableDatabase();
-
try {
-
db.beginTransaction();
-
for (MultiThreadDownload thread : threads) {
-
String sql = "INSERT INTO fileDownloading(downPath,threadId,downLength) values(?,?,?)";
-
db.execSQL(sql, new Object[] { path, thread.id, 0 });
-
}
-
db.setTransactionSuccessful();
-
} finally {
-
db.endTransaction();
-
db.close();
-
}
-
}
-
-
private void deleteDownloading() {
-
SQLiteDatabase db = dbHelper.getWritableDatabase();
-
String sql = "DELETE FROM fileDownloading WHERE downPath=?";
-
db.execSQL(sql, new Object[] { path });
-
db.close();
-
}
-
-
private void updateDownloading(MultiThreadDownload[] threads) {
-
SQLiteDatabase db = dbHelper.getWritableDatabase();
-
try {
-
db.beginTransaction();
-
for (MultiThreadDownload thread : threads) {
-
String sql = "UPDATE fileDownloading SET downLength=? WHERE threadId=? AND downPath=?";
-
db.execSQL(sql, new String[] { thread.currentDownloadSize + "", thread.id + "", path });
-
}
-
db.setTransactionSuccessful();
-
} finally {
-
db.endTransaction();
-
db.close();
-
}
-
}
-
-
private int getDownloadedSize(MultiThreadDownload[] threads) {
-
int sum = 0;
-
for (int len = threads.length, i = 0; i < len; i++) {
-
sum += threads[i].currentDownloadSize;
-
}
-
return sum;
-
}
-
-
private boolean isFinish(MultiThreadDownload[] threads) {
-
try {
-
for (int len = threads.length, i = 0; i < len; i++) {
-
if (!threads[i].finished) {
-
return false;
-
}
-
}
-
return true;
-
} catch (Exception e) {
-
return false;
-
}
-
}
-
}
运行效果:

源码下载地址