Android多线程断点下载简单实现

HTTP多线程断点下载

原理:获取目标文件的大小,在本地创建一个相同大小的文件,并计算每个线程需要下载的起始位置及大小,然后分配至每个线程独立下载,全部下载完毕则自动合并.

实现步骤

  1. 查看并计算目标文件的大小

    URL url = new URL(mPath);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("GET");
    int code = connection.getResponseCode();
    if (code == 200) {
        int length = connection.getContentLength();
        System.out.println("文件总长度为:" + length);
  2. 设置目标文件在本地的映射

    RandomAccessFile raf = new RandomAccessFile(
            Environment.getExternalStorageDirectory().getAbsolutePath()
                    +"/"+getDownlaodName(mPath), "rw");
    raf.setLength(length);
  3. 开启子线程并分配下载任务

    int blokeSize = length / mTotalCount;
    System.out.println("每一块大小为:" + blokeSize);
    runningThreadCount = mTotalCount;
    for (int threadid = 0; threadid < mTotalCount; threadid++) {
        int startPosition = threadid * blokeSize;
        int endPosition = (threadid + 1) * blokeSize - 1;
        //最后一个线程应该下载至末尾
        if (threadid == mTotalCount - 1) {
            endPosition = length - 1;
        }
        System.out.println("线程编号:" + threadid + ""
                + ",下载范围:" + startPosition + "~~" + endPosition);
        //开启下载子线程
        new DownloadThread(threadid, startPosition, endPosition).start();
  4. 子线程中的具体逻辑

    public void run() {
        System.out.println("线程"+threadid+"开始运行了");
        try{
            //读存有历史下载进度的文件,判断是否已经下载过
            File finfo=new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                    +"/"+mTotalCount + getDownlaodName(mPath)+threadid+".txt");
            if (finfo.exists()&&finfo.length()>0) {
                //获取文件输入流
                FileInputStream fis=new FileInputStream(finfo);
                //读取缓冲
                BufferedReader br=new BufferedReader(new InputStreamReader(fis));
                //得到文件内容
                String lasrposition=br.readLine();
                //转化为int值
                int intlastposition=Integer.parseInt(lasrposition);
                lastLoadSize=intlastposition-startPosition;
                startPosition=intlastposition;
                fis.close();
            }
            URL url=new URL(mPath);
            HttpURLConnection conn=(HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            System.out.println("线程实际下载:"+threadid+",范围:"+startPosition
                    +"~~"+endPosition);
            //设置http请求头部参数
            conn.setRequestProperty("Range", "bytes="+startPosition+"-"+endPosition);
            int code=conn.getResponseCode();
            //206代表请求部分数据成功
            if (code==206) {
                //拿到链接返回的输入流
                InputStream is=conn.getInputStream();
                //指向要写的文件(abc.exe)
                RandomAccessFile raf=new RandomAccessFile(
                        Environment.getExternalStorageDirectory().getAbsolutePath()
                                +"/"+getDownlaodName(mPath), "rw");
                //指定文件开始写的位置
                raf.seek(startPosition);
                //下载缓冲区,越大下载越快,对硬盘损耗越小,但是越容易丢失数据
                byte[] buffer=new byte[1024*1024];
                int len=-1;
                int total=0;//当前线程本次下载数据总量
                while ((len=is.read(buffer))!=-1) {
                    raf.write(buffer,0,len);
                    total+=len;
                    //将每次更新的数据同步到底层硬盘
                    RandomAccessFile inforaf=new RandomAccessFile(
                            Environment.getExternalStorageDirectory().getAbsolutePath()
                            +"/"+mTotalCount +getDownlaodName(mPath)+threadid+".txt","rwd");
                    //保存当前线程下载到什么位置
                    inforaf.write(String.valueOf(startPosition+total).getBytes());
                    inforaf.close();
                }
                is.close();
                raf.close();
                System.out.println("线程"+threadid+"下载完毕");
    
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            //同步代码块,保证同时间仅有一个线程执行此区块代码
            synchronized (MainActivity.class) {
                runningThreadCount--;
                if (runningThreadCount<=0) {
                    System.out.println("多线程下载完毕");
                    for (int i = 0; i <mTotalCount ; i++) {
                        File f=new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                                        +"/"+mTotalCount +getDownlaodName(mPath)+i+".txt");
                        System.out.println(f.delete());
                        endTime=System.currentTimeMillis();
                    }
                    System.out.println("结束时间:"+endTime);
                    System.out.println("总共花费:"+(endTime-startTime)/1000+"秒");
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), "总共花费:"
                                    +(endTime-startTime)/1000+"秒",Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            }
        }
    }

    备注:如果不使用断点下载,只需要将判断和存储历史下载信息的逻辑删除即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值