您的位置:首页 > 移动开发 > Android开发

android 多线程断点续传下载 二

2016-06-28 16:41 405 查看
转载自:http://blog.csdn.net/shimiso/article/details/6763986


android 多线程断点续传下载 二

在上一集中,我们简单介绍了如何创建多任务下载,但那种还不能拿来实用,这一集我们重点通过代码为大家展示如何创建多线程断点续传下载,这在实际项目中很常用.

main.xml:

[html] view
plain copy

<?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:

[html] view
plain copy

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="hello">Hello World, Main!</string>

<string name="app_name">多线程断点续传下载</string>

</resources>

AndroidManifest.xml:

[html] view
plain copy

<?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"/>

<!-- SDCard写数据的权限 -->

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

</manifest>

activity程序:

[java] view
plain copy

package sms.multithreaddownload;

import java.io.File;

import sms.multithreaddownload.bean.DownloadListener;

import sms.multithreaddownload.service.DownloadService;

import android.app.Activity;

import android.os.Bundle;

import android.os.Environment;

import android.os.Handler;

import android.os.Message;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.EditText;

import android.widget.ProgressBar;

import android.widget.TextView;

import android.widget.Toast;

public class Main extends Activity {

private EditText path;

private TextView progress;

private ProgressBar progressBar;

private Handler handler = new UIHandler();

private DownloadService servcie;

private Button downButton;

private Button pauseButton;

private final class UIHandler extends Handler {

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case 1:

int downloaded_size = msg.getData().getInt("size");

progressBar.setProgress(downloaded_size);

int result = (int) ((float) downloaded_size / progressBar.getMax() * 100);

progress.setText(result + "%");

if (progressBar.getMax() == progressBar.getProgress()) {

Toast.makeText(getApplicationContext(), "下载完成", Toast.LENGTH_LONG).show();

}

}

}

}

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

path = (EditText) this.findViewById(R.id.editText);

progress = (TextView) this.findViewById(R.id.textView);

progressBar = (ProgressBar) this.findViewById(R.id.progressBar);

downButton = (Button) this.findViewById(R.id.downButton);

pauseButton = (Button) this.findViewById(R.id.pauseButton);

downButton.setOnClickListener(new DownloadButton());

pauseButton.setOnClickListener(new PauseButton());

}

private final class DownloadButton implements View.OnClickListener {

@Override

public void onClick(View v) {

DownloadTask task;

try {

task = new DownloadTask(path.getText().toString());

servcie.isPause = false;

v.setEnabled(false);

pauseButton.setEnabled(true);

new Thread(task).start();

} catch (Exception e) {

e.printStackTrace();

}

}

}

public class PauseButton implements OnClickListener {

@Override

public void onClick(View v) {

servcie.isPause = true;

v.setEnabled(false);

downButton.setEnabled(true);

}

}

public void pause(View v) {

}

private final class DownloadTask implements Runnable {

public DownloadTask(String target) throws Exception {

if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

File destination = Environment.getExternalStorageDirectory();

servcie = new DownloadService(target, destination, 3, getApplicationContext());

progressBar.setMax(servcie.fileSize);

} else {

Toast.makeText(getApplicationContext(), "SD卡不存在或写保护!", Toast.LENGTH_LONG).show();

}

}

@Override

public void run() {

try {

servcie.download(new DownloadListener() {

@Override

public void onDownload(int downloaded_size) {

Message message = new Message();

message.what = 1;

message.getData().putInt("size", downloaded_size);

handler.sendMessage(message);

}

});

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

工具类:

[java] view
plain copy

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) {

// TODO Auto-generated method stub

}

}

[java] view
plain copy

package sms.multithreaddownload.bean;

public interface DownloadListener {

public void onDownload(int downloaded_size);

}

[java] view
plain copy

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类:

[java] view
plain copy

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;

}

}

}

运行效果:



源码下载地址

http://blog.csdn.net/shimiso/article/details/8448544 Android 多线程断点续传下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: