Android断点续传详解和实例

来源:互联网 发布:怎么咨询网络服务商 编辑:程序博客网 时间:2024/05/22 16:49

前言

作为一个拖延者晚期的人,真的是计划跟行动力很难直接挂上钩,最近又有了新的打算,所以博客还是需要好好整理一下

在写越来越多的代码,不,准确说是改别人的代码的时候,发现作为程序员,创作性精神真的是不能少,我们现在能用代码构建的一切,都是建立在前人提前制定好规则的基础上,所以现在的大神也是在理解前人的思想的基础上,加以自己的想法去融会贯通,才能做出来好的产品,我自己现在的弊端就是着急,太着急,从android的应用到破解到游戏,看别人的思想,却有点迷失了.所以,打算沉下心来,好好去弄明白一类问题,来日方长,我还有时间去慢慢学习.废话了太多,也算是自我检醒,希望看到这篇博客的童鞋勿怪.

这次主要是运用的RandomAccessFile类来做 断点续传这件事,我一贯喜欢代码简洁清晰,所以尽可能用最少的代码解决问题


断点续传原理及使用

断点续传想必大家都不陌生,其中最常见的是视频的断点续传,在其他的文件下载中,我们有时候为了保证下载文件的完整性也会用到RandomAccessFile类。

RandomAccessFile主要是记录开始的位置,从而进行插入操作,以下摘自百度百科,其实是原官方的翻译

RandomAccessFile是不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至都没有用InputStream和OutputStream已经准备好的功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。

附:RandomAccessFile类的api的地址

RandomAccessFile类分析

  • 构造方法
    • RandomAccessFile(File file, String mode)
    • RandomAccessFile(String name, String mode)
  • 常用方法
    • 流关闭
      • close()
    • 获取文件的长度
      • length()
    • 设置文件的长度
      • setLength(long newLength)
    • 设置文件指针的开始偏移位置
      • seek(long pos)
    • 读操作
      • read()
      • read(byte[] b)
      • read(byte[] b, int off, int len)
    • 写操作
      • write(byte[] b)
      • write(byte[] b, int off, int len)

代码实例

在写实例之前,说一下现在要实现的功能以及实现的基本思路

  • 功能
    • http下载文件,允许开始下载,暂停下载,恢复下载和取消下载
  • 实现思路
    • 需要先获取该文件的大小,创建一个空的文件,然后向该文件中依次写入数据

实现类 HttpDownLoadFile

(已测试过,功能正常,测试机型:Meizu M1 Metal Android 5.1)

import android.content.Context;import android.content.SharedPreferences;import android.os.Handler;import android.os.Message;import java.io.InputStream;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;/** * 多线程下载文件 */public class HttpDownLoadFile {    private static final String SP_NAME = "http_download_file";    private static final String CURRENT_LENGTH = "current_length";    //以下为线程状态    private static final String DOWNLOAD_INIT = "1";//初始    private static final String DOWNLOAD_ING = "2";//运行    private static final String DOWNLOAD_PAUSE = "3";//暂停    private String loadUrl;//网络获取的url    private String filePath;//下载到本地的path    private int fileLength;//文件总大小    //使用volatile防止多线程不安全    private volatile int currentLength;//当前总共下载的大小    private Thread thread;    private String stateDownload = DOWNLOAD_INIT;//当前线程状态    private OnHttpDownLoadListener httpDownLoadListener;    private final int SUCCESS = 0x00000101;    private final int FAILURE = 0x00000102;    private SharedPreferences sp;    public void setOnHttpDownLoadListener(OnHttpDownLoadListener httpDownLoadListener) {        this.httpDownLoadListener = httpDownLoadListener;    }    public interface OnHttpDownLoadListener {        //返回当前下载进度的百分比        void onProgress(int progress);        void onComplete();        void onFailure();    }    public HttpDownLoadFile(Context mContext, String loadUrl, String filePath) {        this(mContext, loadUrl, filePath, null);    }    public HttpDownLoadFile(Context mContext, String loadUrl, String filePath, OnHttpDownLoadListener mDownLoadListener) {        this.loadUrl = loadUrl;        this.filePath = filePath;        this.httpDownLoadListener = mDownLoadListener;        this.sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);    }    /**     * 开始下载     */    public void onStart() {        //在线程中运行,防止anr        new Thread(new Runnable() {            @Override            public void run() {                try {                    //建立连接请求                    URL url = new URL(loadUrl);                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();                    conn.setRequestMethod("GET");                    int code = conn.getResponseCode();//获取返回码                    LogUtil.e("","code>>>"+code);                    if (code == 200) {//请求成功,根据文件大小开始分多线程下载                        LogUtil.e("","开始执行下载");                        fileLength = conn.getContentLength();                        RandomAccessFile raf = new RandomAccessFile(filePath, "rwd");                        raf.setLength(fileLength);                        //获取上次的下载位置                        currentLength = sp.getInt(CURRENT_LENGTH, 0);                        //开始位置,获取上次取消下载的进度                        int startPosition = sp.getInt(SP_NAME ,  0);                        //结束位置                        int endPosition =  fileLength - 1;                        thread = new DownThread(startPosition, endPosition);                        thread.start();                        raf.close();                    } else {                        handler.sendEmptyMessage(FAILURE);                    }                } catch (Exception e) {                    e.printStackTrace();                    LogUtil.e("","code>>> e.printStackTrace()");                    handler.sendEmptyMessage(FAILURE);                }            }        }).start();    }    /**     * 取消下载     */    public void onCancle() {        if (thread != null) {            //若线程处于等待状态,则while循环处于阻塞状态,无法跳出循环,必须先唤醒线程,才能执行取消任务            if (stateDownload.equals(DOWNLOAD_PAUSE))                onStart();                ((DownThread) thread).cancel();        }    }    /**     * 暂停下载     */    public void onPause() {        if (thread != null)            stateDownload = DOWNLOAD_PAUSE;    }    /**     * 继续下载     */    public void onResume() {        if (thread != null)            synchronized (DOWNLOAD_PAUSE) {                stateDownload = DOWNLOAD_ING;                DOWNLOAD_PAUSE.notifyAll();            }    }    /**     * 结束下载     */    public void onDestroy() {        if (thread != null)            thread = null;    }    private class DownThread extends Thread {        private boolean isGoOn = true;//是否继续下载        private int startPosition;//开始下载点        private int endPosition;//结束下载点        private int currPosition;//当前线程的下载进度        private DownThread(int startPosition, int endPosition) {            this.startPosition = startPosition;            currPosition = startPosition;            this.endPosition = endPosition;        }        @Override        public void run() {            InputStream is = null;            RandomAccessFile raf = null;            try {                URL url = new URL(loadUrl);                HttpURLConnection conn = (HttpURLConnection) url.openConnection();                conn.setRequestMethod("GET");                conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);                //若请求头加上Range这个参数,则返回状态码为206,而不是200                if (conn.getResponseCode() == 206) {                     is = conn.getInputStream();                     raf = new RandomAccessFile(filePath, "rwd");                    raf.seek(startPosition);//跳到指定位置开始写数据                    int len;                    byte[] buffer = new byte[1024];                    while ((len = is.read(buffer)) != -1) {                        //是否继续下载                        if (!isGoOn)                            break;                        //回调当前进度                        if (httpDownLoadListener != null) {                            currentLength += len;                            int progress = (int) ((float) currentLength / (float) fileLength * 100);                            handler.sendEmptyMessage(progress);                        }                        raf.write(buffer, 0, len);                        //写完后将当前指针后移,为取消下载时保存当前进度做准备                        currPosition += len;                        synchronized (DOWNLOAD_PAUSE) {                            if (stateDownload.equals(DOWNLOAD_PAUSE)) {                                DOWNLOAD_PAUSE.wait();                            }                        }                    }                    //若取消下载,则直接返回                    if (!isGoOn) {                        if (currPosition < endPosition) {                            sp.edit().putInt(SP_NAME, currPosition).apply();                            sp.edit().putInt(CURRENT_LENGTH, currentLength).apply();                        }                        return;                    }                    handler.sendEmptyMessage(SUCCESS);                    onDestroy();                } else {                    handler.sendEmptyMessage(FAILURE);                }            } catch (Exception e) {                e.printStackTrace();                handler.sendEmptyMessage(FAILURE);            }finally {                try{                    sp.edit().clear().apply();                    if(null != is)                        is.close();                    if(null != raf)                        raf.close();                }catch (Exception e){                    e.printStackTrace();                }            }        }        public void cancel() {            isGoOn = false;        }    }    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            if (httpDownLoadListener != null) {                if (msg.what == SUCCESS) {                    httpDownLoadListener.onComplete();                } else if (msg.what == FAILURE) {                    httpDownLoadListener.onFailure();                } else {                    httpDownLoadListener.onProgress(msg.what);                }            }        }    };}

使用方法应该不用我多写了,相信大家都看得懂,坑已填,好困,碎觉
有什么不对的地方,欢迎指正,谢谢

原创粉丝点击