ASP源码.NET源码PHP源码JSP源码JAVA源码DELPHI源码PB源码VC源码VB源码Android源码
当前位置:首页 >> 低调看直播体育app软件下载 >> Android开发 >> Android多线程断点续传下载

Android多线程断点续传下载

来源:网络整理     时间:2016-07-03     关键词:

本篇文章主要介绍了" Android多线程断点续传下载",主要涉及到方面的内容,对于Android开发感兴趣的同学可以参考一下: 最近项目里用到了断点续传下载,所以在博客里做个记录,便于以后回顾。背景知识依赖于Http/206响应首先你需要知道文件大小以及远程服务器是否支持HTTP 206...

最近项目里用到了断点续传下载,所以在博客里做个记录,便于以后回顾。

背景知识

依赖于Http/206响应

首先你需要知道文件大小以及远程服务器是否支持HTTP 206请求.使用curl命令可以查看任意资源的HTTP头,使用下面的curl命令可以发送一个HEAD请求

$ curl -I http://avatar.csdn.net/D/0/9/1_wz249863091.jpg
这张图是我在CSDN博客的头像

 Android多线程断点续传下载

这里只看2个属性

Accept-Ranges: bytes - 该响应头表明服务器支持Range请求,以及服务器所支持的单位是字节(这也是唯一可用的单位).我们还能知道:服务器支持断点续传,以及支持同时下载文件的多个部分,也就是说下载工具可以利用范围请求加速下载该文件.Accept-Ranges: none 响应头表示服务器不支持范围请求.

Content-Length: 30220 - Content-Length响应头表明了响应实体的大小,也就是真实的图片文件的大小是30220字节 (30K).

正常情况下,我们不指定range属性的时候,默认值都是0-length,也就是全量下载,返回的code就是200
如果我们指定了range,那么返回的code就是206

代码实现

根据上面的背景知识,可以写一个工具类,便于整个项目使用

package com.example.tony.myapplication;

import android.os.Environment;
import android.text.TextUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * 断点续传工具类
 *
 * @author Tony.W
 */publicclassDownloadUtil {privatestaticfinal String RANGE = "Range";
    privatestaticfinal String BYTE = "bytes=";
    privatestaticfinal String TO = "-";
    privatestaticfinal String RWS = "rws";
    privatestaticfinal String SLASH = "/";
    privatestaticfinal String UTF_8 = "utf-8";

    //Http协议--部分下载的状态码privatestaticfinalint HTTP_PARTIAL_CODE = 206;
    //Htpp协议--下载成功的状态码privatestaticfinalint HTPP_SUCCESS_CODE = 200;
    //Http超时时间privatestaticfinalint TIME_OUT = 3000;
    //默认下载线程数privatestaticfinalint DEFALUT_THREAD_COUNT = 3;

    /**
     * 断点续传下载
     *
     * @param path 资源路径
     * @param threadCount 下载线程数
     * */publicstaticvoiddown(String path, int threadCount){
        URL url = null;
        HttpURLConnection conn = null;
        RandomAccessFile raf = null;
        try {
            url = new URL(path);
            conn = (HttpURLConnection) url.openConnection();
            if (conn.getResponseCode() == HTPP_SUCCESS_CODE){
                //文件长度int fileLen = conn.getContentLength();
                //缓存文件名字
                String cacheFileName = path.substring(path.lastIndexOf(SLASH) + 1);
                File file = new File(Environment.getExternalStorageDirectory(), cacheFileName);
                raf = new RandomAccessFile(file, RWS);
                raf.setLength(fileLen);
                int partialLen = fileLen / threadCount;
                for(int i = 0;inew DownloadThread(url, i, partialLen, file);
                }
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(raf != null){
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 断点续传下载,默认开启3个线程下载
     *
     * @param path 资源路径
     * */publicstaticvoiddown(String path){
        down(path, DEFALUT_THREAD_COUNT);
    }

    privatestaticfinalclassDownloadThreadextendsThread{//下载资源的urlprivate URL url;
        //下载的线程Idprivateint threadId;
        //每个线程需要下载的长度privateint particalLen;
        //缓存文件地址private File file;
        //是否暂停下载privateboolean isPause = false;

        /**
         * 下载线程
         * */publicDownloadThread(URL url, int threadId, int particalLen, File file){
            this.file = file;
            this.url = url;
            this.particalLen = particalLen;
            this.threadId = threadId;
        }

        /**
         * 从文件读取数据
         * */publicstatic String readFromFile(File file) throws IOException {
            if (!file.exists() || file.isDirectory()) {
                returnnull;
            }
            BufferedReader br = new BufferedReader(new FileReader(file));
            String temp = null;
            StringBuffer sb = new StringBuffer();
            temp = br.readLine();
            while (temp != null) {
                sb.append(temp);
                temp = br.readLine();
            }
            return sb.toString();
        }

        /**
         * 向文件写入数据
         * */publicstaticvoidwriteToFile(File file, String data) throws IOException {
            if (!file.exists()) {
                file.createNewFile();
            }
            //重新写入,不是追加写入模式
            FileOutputStream out = new FileOutputStream(file, false);
            out.write(data.getBytes(UTF_8));
            out.close();
        }

        @Overridepublicvoidrun() {
            downTask();
        }

        privatevoiddownTask(){
            //缓存文件名字
            String cacheFileName = Environment.getExternalStorageDirectory() + url.getPath().substring(url.getPath().lastIndexOf(SLASH) + 1 + threadId);
            File cacheFile = new File(cacheFileName);
            //读取缓存文件,判断是否之前已经有下载部分float hasDownLen = 0;
            //下载开始位置int start = 0 + threadId * particalLen;
            //下载结束为止int end = (0 + threadId + 1) * particalLen -1;

            String data = null;
            try {
                data = readFromFile(cacheFile);
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (!TextUtils.isEmpty(data)) {
                hasDownLen = Float.valueOf(data);
                start += hasDownLen;
            }

            HttpURLConnection conn = null;
            try {
                conn = (HttpURLConnection) url.openConnection();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(conn == null){
                return;
            }

            conn.setReadTimeout(TIME_OUT);
            //指定下载部分,如果超过服务器部分,以服务器为准
            conn.setRequestProperty(RANGE, BYTE + start + TO + end);
            RandomAccessFile raf = null;
            InputStream in = null;
            try {
                if (conn.getResponseCode() == HTTP_PARTIAL_CODE) {
                    raf = new RandomAccessFile(file, RWS);
                    raf.seek(start);
                    in = conn.getInputStream();
                    //分段下载,每段大小为1024个字节byte[] buf = newbyte[1024];
                    int len = 0;
                    //如果读到最后,就跳出循环while ((len = in.read(buf)) != -1) {
                        //将下载的数据存到文件
                        raf.write(buf);
                        //记录下载长度
                        hasDownLen += len;
                        writeToFile(cacheFile, String.valueOf(hasDownLen));
                    }
                    //如果完成该部分下载,删除缓存文件
                    cacheFile.delete();
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (raf != null) {
                        raf.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

这里只实现了多线程断点续传功能,如果要结合UI,可以加入一个Handler。
但是需要主要,由于都是static的静态方法,如果处理Handler的时候需要格外小心,不要造成内存泄漏

另外可以观察DDMS,在下载的时候,会产生N+1个文件,N就是你开启线程的数量,当下载全部完成后,只会留下一个数据文件,其余临时缓存文件都会被删除

附件已经上传,有需要的朋友可以下载
http://download.csdn.net/detail/wz249863091/9566461
如果有什么问题,请留言或者私信,第一时间会做出合理修改

').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('
  • ').text(i)); }; $numbering.fadeIn(1700); }); });

    以上就介绍了 Android多线程断点续传下载,包括了方面的内容,希望对Android开发有兴趣的朋友有所帮助。

    本文网址链接:http://www.codes51.com/article/detail_2254643.html

    相关图片

    相关文章