多线程断点下载

本文参考:
http://www.cnblogs.com/hanyonglu/archive/2012/02/20/2358801.html
http://blog.csdn.net/lyy1104/article/details/27227001

本示例介绍在Android平台下通过HTTP协议实现断点续传下载。
主要特点:
1.多线程下载
2.支持断点。

  1. 首先获得下载文件的长度,然后设置本地文件的长度。
   int length=httpURLConnection.getContentLength();
  1. 创建本地文件+随机访问对象
  File file =new File(adFile,fileName);
  RandomAccessFile accessFile=new RandomAccessFile(file,"rwd");
  accessFile.setLength(fileLength);
  1. 创建多个线程+每个线程的下载任务
  for(int threadId=1;threadId<threadNum;threadId++){
        int startIndex=(threadId-1)*thread*size;
        int endIndex=threadId*threadsize-1;
        if(threadId==threadNum){
           endIndex=fileLength-1;
        }
  }

4.保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。

 RandomAccessFile threadfile = 
           new RandomAccessFile("QQWubiSetup.exe ","rwd");
 threadfile.seek(2097152);//从文件的什么位置开始写入数据

示例代码:

package com.hefei.ppa.mytest.download;

import android.app.Activity;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.hefei.ppa.mytest.R;

/**
 * Created by Administrator on 2017/1/20.
 */

public class DownloadActivity extends Activity {
    // 线程开启的数量
    private int threadNum = 3;
    private int threadRunning = 3;

    private EditText et_url;
    private ProgressBar progressBar;
    private TextView tv_pb;
    private Button bt_download;

    private int currentProgress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download);
        // 获取控件对象
        et_url = (EditText) findViewById(R.id.et_url);
        progressBar = (ProgressBar) findViewById(R.id.pb_down);
        tv_pb = (TextView) findViewById(R.id.tv_pb);
        bt_download= (Button) findViewById(R.id.bt_download);

        File sdDir = Environment.getExternalStorageDirectory();
        File pbFile = new File(sdDir,"pb.txt");
        InputStream is = null;
        try {
            //判断文件是否存在
            if (pbFile.exists()) {
                is = new FileInputStream(pbFile);
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if (is != null) {
            String value = StreamTools.streamToStr(is);
            String[] arr = value.split(";");
            progressBar.setMax(Integer.valueOf(arr[0]));//最大值
            currentProgress = Integer.valueOf(arr[1]);//当前值
            progressBar.setProgress(currentProgress);
            tv_pb.setText("当前的进度是:"+arr[2]);//显示百分比
        }

        bt_download.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                downLoadFile(v);
            }
        });
    }

    // 下载文件(得到服务器端文件的大小)
    public void downLoadFile(View v) {

        // 获取下载路径
        final String spec = et_url.getText().toString();
        if (TextUtils.isEmpty(spec)) {
            Toast.makeText(this, "下载的地址不能为空", Toast.LENGTH_LONG).show();
        } else {
            new Thread() {
                public void run() {
                    // HttpURLConnection
                    try {
                        // 根据下载的地址构建URL对象
                        URL url = new URL(spec);
                        // 通过URL对象的openConnection()方法打开连接,返回一个连接对象
                        HttpURLConnection httpURLConnection = (HttpURLConnection) url
                                .openConnection();
                        // 设置请求的头
                        httpURLConnection.setRequestMethod("GET");
                        httpURLConnection.setReadTimeout(5000);
                        httpURLConnection.setConnectTimeout(5000);
                        // 判断是否响应成功
                        if (httpURLConnection.getResponseCode() == 200) {
                            // 获取下载文件的长度
                            int fileLength = httpURLConnection
                                    .getContentLength();
                            //设置进度条的最大值
                            progressBar.setMax(fileLength);
                            //判断sd卡是否管用
                            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                                // 保存文件
                                // 外部存储设备的路径
                                File sdFile = Environment
                                        .getExternalStorageDirectory();
                                //获取文件的名称
                                String fileName = spec.substring(spec.lastIndexOf("/")+1);
                                //创建保存的文件
                                File file = new File(sdFile, fileName);
                                //创建可以随机访问对象
                                RandomAccessFile accessFile = new RandomAccessFile(
                                        file, "rwd");
                                // 保存文件的大小
                                accessFile.setLength(fileLength);
                                // 关闭
                                accessFile.close();
                                // 计算出每个线程的下载大小
                                int threadSize = fileLength / threadNum;
                                // 计算出每个线程的开始位置,结束位置
                                for (int threadId = 1; threadId <= 3; threadId++) {
                                    int startIndex = (threadId - 1) * threadSize;
                                    int endIndex = threadId * threadSize - 1;
                                    if (threadId == threadNum) {// 最后一个线程
                                        endIndex = fileLength - 1;
                                    }

                                    System.out.println("当前线程:" + threadId
                                            + " 开始位置:" + startIndex + " 结束位置:"
                                            + endIndex + " 线程大小:" + threadSize);
                                    // 开启线程下载
                                    new DownLoadThread(threadId, startIndex,
                                            endIndex, spec).start();
                                }
                            }else {
                                DownloadActivity.this.runOnUiThread(new Runnable() {
                                    public void run() {
                                        Toast.makeText(DownloadActivity.this, "SD卡不管用", Toast.LENGTH_LONG).show();
                                    }
                                });
                            }
                        }else {
                            //在主线程中运行
                            DownloadActivity.this.runOnUiThread(new Runnable() {
                                public void run() {
                                    Toast.makeText(DownloadActivity.this, "服务器端返回错误", Toast.LENGTH_LONG).show();
                                }
                            });
                        }

                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                };

            }.start();
        }
    }

    class DownLoadThread extends Thread {

        private int threadId;
        private int startIndex;
        private int endIndex;
        private String path;

        /**
         * 构造函数
         *
         * @param threadId
         *            线程的序号
         * @param startIndex
         *            线程开始位置
         * @param endIndex
         * @param path
         */
        public DownLoadThread(int threadId, int startIndex, int endIndex,
                              String path) {
            super();
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.path = path;
        }

        @Override
        public void run() {
            try {
                File sdFile = Environment.getExternalStorageDirectory();
                //获取每个线程下载的记录文件
                File recordFile = new File(sdFile, threadId + ".txt");
                if (recordFile.exists()) {
                    // 读取文件的内容
                    InputStream is = new FileInputStream(recordFile);
                    // 利用工具类转换
                    String value = StreamTools.streamToStr(is);
                    // 获取记录的位置
                    int recordIndex = Integer.parseInt(value);
                    // 将记录的位置赋给开始位置
                    startIndex = recordIndex;
                }

                // 通过path路径构建URL对象
                URL url = new URL(path);
                // 通过URL对象的openConnection()方法打开连接,返回一个连接对象
                HttpURLConnection httpURLConnection = (HttpURLConnection) url
                        .openConnection();
                // 设置请求的头
                httpURLConnection.setRequestMethod("GET");
                httpURLConnection.setReadTimeout(5000);
                // 设置下载文件的开始位置结束位置
                httpURLConnection.setRequestProperty("Range", "bytes="
                        + startIndex + "-" + endIndex);
                // 获取的状态码
                int code = httpURLConnection.getResponseCode();
                // 判断是否成功
                if (code == 206) {
                    // 获取每个线程返回的流对象
                    InputStream is = httpURLConnection.getInputStream();
                    //获取文件的名称
                    String fileName = path.substring(path.lastIndexOf("/")+1);
                    // 根据路径创建文件
                    File file = new File(sdFile, fileName);
                    // 根据文件创建RandomAccessFile对象
                    RandomAccessFile raf = new RandomAccessFile(file, "rwd");
                    raf.seek(startIndex);
                    // 定义读取的长度
                    int len = 0;
                    // 定义缓冲区
                    byte b[] = new byte[1024 * 1024];
                    int total = 0;
                    // 循环读取
                    while ((len = is.read(b)) != -1) {
                        RandomAccessFile threadFile = new RandomAccessFile(
                                new File(sdFile, threadId + ".txt"), "rwd");
                        threadFile.writeBytes((startIndex + total) + "");
                        threadFile.close();
                        raf.write(b, 0, len);
                        // 已经下载的大小
                        total += len;
                        //解决同步问题
                        synchronized (DownloadActivity.this) {
                            currentProgress += len;
                            progressBar.setProgress(currentProgress);
                            //计算百分比的操作 l表示long型
                            final String percent = currentProgress*100l/progressBar.getMax()+"%";
                            DownloadActivity.this.runOnUiThread(new Runnable() {
                                public void run() {
                                    tv_pb.setText("当前的进度是:"+percent);
                                }
                            });
                            //创建保存当前进度和百分比的操作
                            RandomAccessFile pbFile = new RandomAccessFile(
                                    new File(sdFile, "pb.txt"), "rwd");
                            pbFile.writeBytes(progressBar.getMax()+";"+currentProgress+";"+percent);
                            pbFile.close();
                        }
                    }
                    raf.close();
                    is.close();
                    runOnUiThread(new Runnable() {
                        public void run() {
                            Toast.makeText(DownloadActivity.this, "当前线程--" + threadId + "--下载完毕", Toast.LENGTH_LONG).show();
                        }
                    });
                    deleteRecordFiles();
                } else {
                    runOnUiThread(new Runnable() {
                        public void run() {
                            Toast.makeText(DownloadActivity.this, "服务器端下载错误", Toast.LENGTH_LONG).show();
                        }
                    });
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }

    // synchronized避免线程同步
    public synchronized void deleteRecordFiles() {
        File sdFile = Environment.getExternalStorageDirectory();
        threadRunning--;
        if (threadRunning == 0) {
            for (int i = 1; i <= 3; i++) {
                File recordFile = new File(sdFile, i + ".txt");
                if (recordFile.exists()) {
                    // 删除文件
                    recordFile.delete();
                }
                File pbFile = new File(sdFile,"pb.txt");
                if (pbFile.exists()) {
                    pbFile.delete();
                }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值