天天看点

android 多线程断点续传下载 二

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

main.xml:

[html] view

plaincopy

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

        <button  

            android:layout_width="wrap_content"  

            android:layout_height="wrap_content"  

            android:id="@+id/downbutton"  

            android:text="download"  

        />  

            android:id="@+id/pausebutton"  

            android:enabled="false"  

            android:text="pause"  

    </linearlayout>  

    <progressbar  

        android:layout_height="18dp"  

        style="?android:attr/progressbarstylehorizontal"  

        android:id="@+id/progressbar"  

    <textview  

        android:layout_width="fill_parent"  

        android:id="@+id/textview"  

        android:gravity="center"  

</linearlayout>  

string.xml:

<resources>  

    <string name="hello">hello world, main!</string>  

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

</resources>  

androidmanifest.xml:

<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

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 {  

        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 {  

            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();  

        public void run() {  

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

                });  

}  

工具类:

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

    public void oncreate(sqlitedatabase db) {  

        db.execsql("create table filedownloading(_id integer primary key autoincrement,downpath varchar(100),threadid integer,downlength integer)");  

    public void onupgrade(sqlitedatabase db, int oldversion, int newversion) {  

        // todo auto-generated method stub  

public interface downloadlistener {  

    public void ondownload(int downloaded_size);  

import java.io.inputstream;  

import java.io.randomaccessfile;  

import java.net.httpurlconnection;  

import java.net.url;  

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;  

    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.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.multithreaddownload;  

import android.database.cursor;  

public class downloadservice {  

    public static final string tag = "tag";  

    /* 用于查询数据库 */  

    private dbhelper dbhelper;  

    /* 要下载的文件大小 */  

    public int filesize;  

    /* 每条线程需要下载的数据量 */  

    private int block;  

    /* 保存文件地目录 */  

    private file savedfile;  

    /* 下载地址 */  

    /* 是否停止下载 */  

    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();  

                }  

                matcher matcher = pattern.compile(".*filename=(.*)").matcher(content_disposition);  

                if (matcher.find()) filename = matcher.group(1);  

                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();  

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

        string sql = "delete from filedownloading where downpath=?";  

        db.execsql(sql, new object[] { path });  

    private void updatedownloading(multithreaddownload[] threads) {  

                string sql = "update filedownloading set downlength=? where threadid=? and downpath=?";  

                db.execsql(sql, new string[] { thread.currentdownloadsize + "", thread.id + "", path });  

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

            for (int len = threads.length, i = 0; i < len; i++) {  

                if (!threads[i].finished) {  

                    return false;  

            return true;  

            return false;  

运行效果:

android 多线程断点续传下载 二

源码下载地址