Android之多线程断点下载
本文主要包含多线程下载的一些简单demo,包括三部分
- java实现
 - android实现
 - XUtils开源库实现
 
注意下载添加网络权限与SD卡读写权限
java实现多线程下载
public class MutileThreadDownload {
    /**
     * 线程的数量
     */
    private static int threadCount = 3;
    /**
     * 每个下载区块的大小
     */
    private static long blocksize;
    /**
     * 正在运行的线程的数量
     */
    private static int runningThreadCount;
    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        // 服务器文件的路径
        String path = "http://192.168.1.100:8080/ff.exe";
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(5000);
        int code = conn.getResponseCode();
        if (code == 200) {
            long size = conn.getContentLength();// 得到服务端返回的文件的大小
            System.out.println("服务器文件的大小:" + size);
            blocksize = size / threadCount;
            // 1.首先在本地创建一个大小跟服务器一模一样的空白文件。
            File file = new File("temp.exe");
            RandomAccessFile raf = new RandomAccessFile(file, "rw");
            raf.setLength(size);
            // 2.开启若干个子线程分别去下载对应的资源。
            runningThreadCount = threadCount;
            for (int i = 1; i <= threadCount; i++) {
                long startIndex = (i - 1) * blocksize;
                long endIndex = i * blocksize - 1;
                if (i == threadCount) {
                    // 最后一个线程
                    endIndex = size - 1;
                }
                System.out.println("开启线程:" + i + "下载的位置:" + startIndex + "~"
                        + endIndex);
                new DownloadThread(path, i, startIndex, endIndex).start();
            }
        }
        conn.disconnect();
    }
    private static class DownloadThread extends Thread {
        private int threadId;
        private long startIndex;
        private long endIndex;
        private String path;
        public DownloadThread(String path, int threadId, long startIndex,
                long endIndex) {
            this.path = path;
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }
        @Override
        public void run() {
            try {
                // 当前线程下载的总大小
                int total = 0;
                File positionFile = new File(threadId + ".txt");
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setRequestMethod("GET");
                // 接着从上一次的位置继续下载数据
                if (positionFile.exists() && positionFile.length() > 0) {// 判断是否有记录
                    FileInputStream fis = new FileInputStream(positionFile);
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(fis));
                    // 获取当前线程上次下载的总大小是多少
                    String lasttotalstr = br.readLine();
                    int lastTotal = Integer.valueOf(lasttotalstr);
                    System.out.println("上次线程" + threadId + "下载的总大小:"
                            + lastTotal);
                    startIndex += lastTotal;
                    total += lastTotal;// 加上上次下载的总大小。
                    fis.close();
                }
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
                        + endIndex);
                conn.setConnectTimeout(5000);
                int code = conn.getResponseCode();
                System.out.println("code=" + code);
                InputStream is = conn.getInputStream();
                File file = new File("temp.exe");
                RandomAccessFile raf = new RandomAccessFile(file, "rw");
                // 指定文件开始写的位置。
                raf.seek(startIndex);
                System.out.println("第" + threadId + "个线程:写文件的开始位置:"
                        + String.valueOf(startIndex));
                int len = 0;
                byte[] buffer = new byte[512];
                while ((len = is.read(buffer)) != -1) {
                    RandomAccessFile rf = new RandomAccessFile(positionFile,
                            "rwd");
                    raf.write(buffer, 0, len);
                    total += len;
                    rf.write(String.valueOf(total).getBytes());
                    rf.close();
                }
                is.close();
                raf.close();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 只有所有的线程都下载完毕后 才可以删除记录文件。
                synchronized (MutileThreadDownload.class) {
                    System.out.println("线程" + threadId + "下载完毕了");
                    runningThreadCount--;
                    if (runningThreadCount < 1) {
                        System.out.println("所有的线程都工作完毕了。删除临时记录的文件");
                        for (int i = 1; i <= threadCount; i++) {
                            File f = new File(i + ".txt");
                            System.out.println(f.delete());
                        }
                    }
                }
            }
        }
    }
}
安卓实现
public class MainActivity extends Activity {
    protected static final int DOWNLOAD_ERROR = 1;
    private static final int THREAD_ERROR = 2;
    public static final int DWONLOAD_FINISH = 3;
    private EditText et_path;
    private EditText et_count;
    /**
     * 存放进度条的布局
     */
    private LinearLayout ll_container;
    /**
     * 进度条的集合
     */
    private List<ProgressBar> pbs;
    /**
     * android下的消息处理器,在主线程创建,才可以更新ui
     */
    private Handler handler = new Handler(){
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case DOWNLOAD_ERROR:
                Toast.makeText(getApplicationContext(), "下载失败", 0).show();
                break;
            case THREAD_ERROR:
                Toast.makeText(getApplicationContext(), "下载失败,请重试", 0).show();
                break;
            case DWONLOAD_FINISH:
                Toast.makeText(getApplicationContext(), "下载完成", 0).show();
                break;
            }
        };
    };
    /**
     * 线程的数量
     */
    private int threadCount = 3;
    /**
     * 每个下载区块的大小
     */
    private long blocksize;
    /**
     * 正在运行的线程的数量
     */
    private  int runningThreadCount;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_path = (EditText) findViewById(R.id.et_path);
        et_count = (EditText) findViewById(R.id.et_count);
        ll_container = (LinearLayout) findViewById(R.id.ll_container);
    }
    /**
     * 下载按钮的点击事件
     * @param view
     */
    public void downLoad(View view){
        //下载文件的路径
        final String path = et_path.getText().toString().trim();
        if(TextUtils.isEmpty(path)){
            Toast.makeText(this, "对不起下载路径不能为空", 0).show();
            return;
        }
        String count = et_count.getText().toString().trim();
        if(TextUtils.isEmpty(path)){
            Toast.makeText(this, "对不起,线程数量不能为空", 0).show();
            return;
        }
        threadCount = Integer.parseInt(count);
        //清空掉旧的进度条
        ll_container.removeAllViews();
        //在界面里面添加count个进度条
        pbs = new ArrayList<ProgressBar>();
        for(int j=0;j<threadCount;j++){
            ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb, null);
            ll_container.addView(pb);
            pbs.add(pb);
        }
        Toast.makeText(this, "开始下载", 0).show();
        new Thread(){
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        long size = conn.getContentLength();// 得到服务端返回的文件的大小
                        System.out.println("服务器文件的大小:" + size);
                        blocksize = size / threadCount;
                        // 1.首先在本地创建一个大小跟服务器一模一样的空白文件。
                        File file = new File(Environment.getExternalStorageDirectory(),getFileName(path));
                        RandomAccessFile raf = new RandomAccessFile(file, "rw");
                        raf.setLength(size);
                        // 2.开启若干个子线程分别去下载对应的资源。
                        runningThreadCount = threadCount;
                        for (int i = 1; i <= threadCount; i++) {
                            long startIndex = (i - 1) * blocksize;
                            long endIndex = i * blocksize - 1;
                            if (i == threadCount) {
                                // 最后一个线程
                                endIndex = size - 1;
                            }
                            System.out.println("开启线程:" + i + "下载的位置:" + startIndex + "~"
                                    + endIndex);
                            int threadSize = (int) (endIndex - startIndex);
                            pbs.get(i-1).setMax(threadSize);
                            new DownloadThread(path, i, startIndex, endIndex).start();
                        }
                    }
                    conn.disconnect();
                } catch (Exception e) {
                    e.printStackTrace();
                    Message msg = Message.obtain();
                    msg.what = DOWNLOAD_ERROR;
                    handler.sendMessage(msg);
                }
            };
        }.start();
    }
    private class DownloadThread extends Thread {
        private int threadId;
        private long startIndex;
        private long endIndex;
        private String path;
        public DownloadThread(String path, int threadId, long startIndex,
                long endIndex) {
            this.path = path;
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }
        @Override
        public void run() {
            try {
                // 当前线程下载的总大小
                int total = 0;
                File positionFile = new File(Environment.getExternalStorageDirectory(),getFileName(path)+threadId + ".txt");
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setRequestMethod("GET");
                // 接着从上一次的位置继续下载数据
                if (positionFile.exists() && positionFile.length() > 0) {// 判断是否有记录
                    FileInputStream fis = new FileInputStream(positionFile);
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(fis));
                    // 获取当前线程上次下载的总大小是多少
                    String lasttotalstr = br.readLine();
                    int lastTotal = Integer.valueOf(lasttotalstr);
                    System.out.println("上次线程" + threadId + "下载的总大小:"
                            + lastTotal);
                    startIndex += lastTotal;
                    total += lastTotal;// 加上上次下载的总大小。
                    fis.close();
                    //存数据库。
                    //_id path threadid total
                }
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
                        + endIndex);
                conn.setConnectTimeout(5000);
                int code = conn.getResponseCode();
                System.out.println("code=" + code);
                InputStream is = conn.getInputStream();
                File file = new File(Environment.getExternalStorageDirectory(),getFileName(path));
                RandomAccessFile raf = new RandomAccessFile(file, "rw");
                // 指定文件开始写的位置。
                raf.seek(startIndex);
                System.out.println("第" + threadId + "个线程:写文件的开始位置:"
                        + String.valueOf(startIndex));
                int len = 0;
                byte[] buffer = new byte[1024];
                while ((len = is.read(buffer)) != -1) {
                    RandomAccessFile rf = new RandomAccessFile(positionFile,
                            "rwd");
                    raf.write(buffer, 0, len);
                    total += len;
                    rf.write(String.valueOf(total).getBytes());
                    rf.close();
                    pbs.get(threadId-1).setProgress(total);
                }
                is.close();
                raf.close();
            } catch (Exception e) {
                e.printStackTrace();
                Message msg = Message.obtain();
                msg.what = THREAD_ERROR;
                handler.sendMessage(msg);
            } finally {
                // 只有所有的线程都下载完毕后 才可以删除记录文件。
                synchronized (MainActivity.class) {
                    System.out.println("线程" + threadId + "下载完毕了");
                    runningThreadCount--;
                    if (runningThreadCount < 1) {
                        System.out.println("所有的线程都工作完毕了。删除临时记录的文件");
                        for (int i = 1; i <= threadCount; i++) {
                            File f = new File(Environment.getExternalStorageDirectory(),getFileName(path)+ i + ".txt");
                            System.out.println(f.delete());
                        }
                        Message msg = Message.obtain();
                        msg.what = DWONLOAD_FINISH;
                        handler.sendMessage(msg);
                    }
                }
            }
        }
    }
    //http://192.168.1.100:8080/aa.exe
    private String getFileName(String path){
        int start = path.lastIndexOf("/")+1;
        return path.substring(start);
    }
}
利用XUtils开源框架实现,需要XUtils的jar包
public class MainActivity extends Activity {
    private EditText et_path;
    private TextView tv_info;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_path = (EditText) findViewById(R.id.et_path);
        tv_info = (TextView) findViewById(R.id.tv_info);
    }
    public void download(View view){
        String path = et_path.getText().toString().trim();
        if(TextUtils.isEmpty(path)){
            Toast.makeText(this, "请输入下载的路径", 0).show();
            return;
        }else{
            HttpUtils http = new HttpUtils();
            HttpHandler handler = http.download(path,
                    "/sdcard/xxx.zip",
                    true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。
                    true, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。
                    new RequestCallBack<File>() {
                        @Override
                        public void onStart() {
                            tv_info.setText("conn...");
                        }
                        @Override
                        public void onLoading(long total, long current, boolean isUploading) {
                            tv_info.setText(current + "/" + total);
                        }
                        @Override
                        public void onSuccess(ResponseInfo<File> responseInfo) {
                            tv_info.setText("downloaded:" + responseInfo.result.getPath());
                        }
                        @Override
                        public void onFailure(HttpException error, String msg) {
                            tv_info.setText(msg);
                        }
                });
        }
    }
}
完成
Android之多线程断点下载的更多相关文章
- Android网络多线程断点续传下载
		
本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多 ...
 - andoid 多线程断点下载
		
本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多 ...
 - Android(java)学习笔记216:多线程断点下载的原理(Android实现)
		
之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载: 1.新建一个Android工程: (1)其中我们先实现布局 ...
 - 我的Android进阶之旅------>Android基于HTTP协议的多线程断点下载器的实现
		
一.首先写这篇文章之前,要了解实现该Android多线程断点下载器的几个知识点 1.多线程下载的原理,如下图所示 注意:由于Android移动设备和PC机的处理器还是不能相比,所以开辟的子线程建议不要 ...
 - Android(java)学习笔记159:多线程断点下载的原理(Android实现)
		
之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载: 1. 新建一个Android工程: (1)其中我们先实现布 ...
 - 实现android支持多线程断点续传下载器功能
		
多线程断点下载流程图: 多线程断点续传下载原理介绍: 在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度手机端下载数据时难免会出现无信号断线.电量不足等情况,所以需要断点续传功能根据下 ...
 - 33、多线程断点下载的实现&界面的更新
		
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...
 - java多线程断点下载原理(代码实例演示)
		
原文:http://www.open-open.com/lib/view/open1423214229232.html 其实多线程断点下载原理,很简单的,那么我们就来先了解下,如何实现多线程的断点下载 ...
 - iOS开发网络篇—大文件的多线程断点下载
		
http://www.cnblogs.com/wendingding/p/3947550.html iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时 ...
 
随机推荐
- DLUTOJ 1142 高中的公式
			
传送门 Time Limit: 1 Sec Memory Limit: 128 MB Description 据说...高中学习了好多公式.所以...萌学长不知道该用什么公式来解决下面这个问题.对于 ...
 - bootstrap学习总结-06 按钮
			
一按钮的基本样式 Bootstrap提供一组标准的按钮配色和大小调整方案,只需要简单的应用的按钮类即可.BootStrap3提供了按钮的标准样式如图. <!DOCTYPE html> &l ...
 - Apple的App Analytics统计平台你必须知道的Q&A整理与翻译
			
Apple的App Analytics统计平台你必须知道的Q&A整理与翻译 Apple最近在iTunesConnect里最新发布了App Analytics统计平台,提供了现有友盟统计平台和自 ...
 - POJ3233Matrix Power Series(十大矩阵问题之三 + 二分+矩阵快速幂)
			
http://poj.org/problem?id=3233 Matrix Power Series Time Limit: 3000MS Memory Limit: 131072K Total ...
 - php返回json数据简单实例
			
<?php include './include/conn.php'; //数据库链接文件 $sql_notice = mysql_query('SELECT * FROM gg_notice ...
 - TFS2008解除独占式锁定文件命令(转载)
			
使用场景:如果项目团队成员A对项目某个文件以独占式方式签出,恰好那天该成员A没有来上班而成员需要对此文件进入修改并check in,这时需要先把A对该文件的锁定解除.没有IDE可以使用,只能使用下面的 ...
 - spring mvc实现登录+异常
			
登录页面login.jsp在webroot下 <body> <form action="user/login" method="post"&g ...
 - upc.2219: A^X mod P(打表 && 超越快速幂(in some ways))
			
2219: A^X mod P Time Limit: 5 Sec Memory Limit: 128 MB Submit: 417 Solved: 68 [Submit][Status][Web ...
 - Java中的异常
			
一.什么是异常 异常就是在程序的运行过程中所发生的不正常的事件,如所需文件找不到,网络连接不通或中断,算术运算出错(如被0除),数组下标越界,装载了一个不存在的类,对null的操作,类型转换异常等等. ...
 - JSONKit 简单使用
			
http://blog.csdn.net/l_ch_g/article/details/8477187 例子上写的比较浅显易懂, 不过我还是稍微总结一下: 导入JSONKit.h之后 字符串转NSDi ...