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

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"    ><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/editText"android:text="http://gongxue.cn/yingyinkuaiche/UploadFiles_9323/201008/2010082909434077.mp3"/><LinearLayoutandroid:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/downButton"android:text="Download"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/pauseButton"android:enabled="false"android:text="Pause"/></LinearLayout><ProgressBarandroid:layout_width="match_parent"android:layout_height="18dp"style="?android:attr/progressBarStyleHorizontal"android:id="@+id/progressBar"/><TextViewandroid: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"/>    <!-- SDCard写数据的权限 -->    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>    </manifest> 

activity程序:

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 {@Overridepublic 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();}}}}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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();}}@Overridepublic void run() {try {servcie.download(new DownloadListener() {@Overridepublic 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();}}}}


工具类:

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);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE fileDownloading(_id integer primary key autoincrement,downPath varchar(100),threadId INTEGER,downLength INTEGER)");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stub}}


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;}@Overridepublic 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;}}}


运行效果:


源码下载地址

http://blog.csdn.net/shimiso/article/details/8448544 android 多线程断点续传下载 三

转载请标明出处http://blog.csdn.net/shimiso

技术交流群:66756039

更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. 一句话锁定MySQL数据占用元凶
  3. Android(安卓)之最新最全的Intent传递数据方法
  4. andorid中网络图片下载、保存以及在相册中显示
  5. Thread、Runable和Timer、TimerTask简述
  6. android Activity runOnUiThread() 方法使用
  7. ubuntu下搭建Android(安卓)SDK开发环境
  8. Android(java)学习笔记76:多线程-定时器概述和使用
  9. Android腾讯微薄客户端开发九:博主详情界面篇(广播,听众,收听)

随机推荐

  1. Android——Activity四种启动模式
  2. Android View的介绍和使用
  3. android加密解密完美教程
  4. android api 中文 (74)—— AdapterView.Ad
  5. Android 之 下拉框(Spinner)的简单使用
  6. MTK Android Driver:led
  7. Android,LIstView中的OnItemClick点击无
  8. Android UI开发第十七篇——Android Frag
  9. Android N 指纹框架
  10. android 系统中静音后使得音量减键不能解