自己动手实现一个Android断点下载

一、断点下载原理及步骤

     对于断点下载,就是下载的过程中,都会出现一些异常情况,导致下载中断。虽说可以重新下载,但是这对于数据比较大的来说,这是很麻烦很蛋疼的事。

原理:
     在下载中断的时候,通过数据库,记录中断的文件信息。待恢复上传的时候,从数据库中读取中断的文件信息。通过RandomAccessFile这个类可以让文件继续从中断的位置上传。

步骤:

  1. 获取下载链接,首先根据下载链接到数据库查找一下是否有重复的下载任务,有的话获取数据继续下载,没有的话,新建任务,获取文件对象,传给下载服务。
  2. 新建一个下载服务,方便应用退出时,能继续在后台下载。
  3. 创建一个数据库,用来存储程序出现异常中断时,能够及时保存下载信息,方便下次读取继续下载。
  4. 创建一个线程,用来获取待下载文件的长度和执行下载线程。
  5. 在不断写文件的过程中,通过广播刷新下载进度。

二、代码实现

package com.example.river.download;

import java.io.Serializable;

/**
 * Created by Administrator on 2017/10/18.
 */

public class FileInfo implements Serializable{
  private String fileName;
  private String url;
  //文件的大小
  private int len;

  //文件结束位置
  private int finished;

  private boolean isDownloading;
  public FileInfo(){

  }
public FileInfo(String fileName,String url){
    this.fileName = fileName;
    this.url = url;
}

  public boolean isDownloading() {
    return isDownloading;
  }

  public void setDownloading(boolean downloading) {
    isDownloading = downloading;
  }


  public String getUrl() {
    return url;
  }

  public void setUrl(String url) {
    this.url = url;
  }

  public int getLen() {
    return len;
  }

  public void setLen(int len) {
    this.len = len;
  }


  public int getFinished() {
    return finished;
  }

  public void setFinished(int finished) {
    this.finished = finished;
  }

  public String getFileName() {
    return fileName;
  }

  public void setFileName(String fileName) {
    this.fileName = fileName;
  }
}

start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, DownloadService.class);
                if (fileInfo.isDownloading()) {
                    fileInfo.setDownloading(false);
                    start.setText("继续");
                } else {
                    fileInfo.setDownloading(true);
                    start.setText("暂停");
                }

                intent.setAction("start");
                intent.putExtra("fileInfo", fileInfo);
                startService(intent);
            }
        });
        restart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                start.setText("暂停");
                fileInfo.setDownloading(true);
                Intent intent = new Intent(MainActivity.this, DownloadService.class);
                fileInfo.setFinished(0);
                intent.setAction("restart");
                intent.putExtra("fileInfo", fileInfo);
                startService(intent);
            }
        });
        receiver = new ProgressBroadcast();
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.intent.action.ProgressBroadcast");
//注册receiver
        registerReceiver(receiver, filter);




广播更新进度
  public class ProgressBroadcast extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            int progress = intent.getIntExtra("finished", 0);
            mProgressBar.setProgress(progress);
        }
    }
检查数据库
private FileInfo checkDB(){
         DBHelper dbHelper = new DBHelper(MainActivity.this);
        SQLiteDatabase db = dbHelper.getReadableDatabase();
         fileInfo= dbHelper.queryData(db,"http://www.21yey.com/clientdownload/android/family.apk");
            if (fileInfo.getFinished() > 0) {
                mProgressBar.setProgress(fileInfo.getFinished() * 100 / fileInfo.getLen());
                start.setText("继续");
            }else {
                fileInfo = new FileInfo("family.apk", "http://www.21yey.com/clientdownload/android/family.apk");

            }
        return fileInfo;

    }
后台下载服务,以便应用退出可以继续下载

 public class DownloadService extends IntentService{
    public static final String ACTION_START = "start";
    public static final String ACTION_RESTART = "restart";
    public static final String ACTION_UPDATE = "update";

    public DownloadService() {
        super("download");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        TaskManager task =  TaskManager.getInstance();
        FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
        if(intent.getAction().equals(ACTION_START)){
            if(fileInfo.isDownloading()){
                task.start(DownloadService.this,fileInfo);
            }else {
                task.stop();
            }
        }
        if(intent.getAction().equals(ACTION_RESTART)){
            task.restart(DownloadService.this,fileInfo);
        }

    }


}
任务调度

public class TaskManager {
    private Map<String,FileInfo>  map = new HashMap<>();
    private boolean isPause;
    public static class TaskHolder{
        private static final TaskManager instance = new TaskManager();
    }
    public static TaskManager getInstance(){
        return TaskHolder.instance;
    }


    //恢复任务
    public void start(Context context,FileInfo fileInfo){
        if(map.get(fileInfo.getUrl())==null){
            map.put(fileInfo.getUrl(),fileInfo);
        }
        DownloadTask task = new DownloadTask(map.get(fileInfo.getUrl()),context);
        isPause = false;
        task.start();
    }

    public void stop(){
        isPause =true;
    }
    public void restart(Context context,FileInfo fileInfo){
        try {
            map.clear();
            File file = new File(DownloadTask.FILE_PATH,fileInfo.getFileName());
            if (file.exists()){
                file.delete();
            }
            Thread.sleep(100);
        }catch (Exception e){
            return;
        }
        start(context,fileInfo);
    }
    public boolean isPause() {
        return isPause;
    }

    public void setPause(boolean pause) {
        isPause = pause;
    }

}

获取待下载的文件长度
   int length = -1;
        try {
            HttpURLConnection conn = null;
            URL url = new URL(info.getUrl());
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);

            if(conn.getResponseCode() ==200){
                length =conn.getContentLength();
            }
            if(length<0){
                return;
            }
            File dir = new File(DownloadTask.FILE_PATH);
            if(!dir.exists()){
                dir.mkdir();
            }
            info.setLen(length);
            conn.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
执行下载任务

 HttpURLConnection connection = null;
        RandomAccessFile raf = null;


        try {
            URL urls = new URL(info.getUrl());
            connection = (HttpURLConnection) urls.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(3000);
            //获取上次下载位置
            int start = info.getFinished();
            connection.setRequestProperty("Range","bytes="+start+"-"+length);
            //设置文件写入位置
            File file = new File(FILE_PATH,info.getFileName());
            raf= new RandomAccessFile(file,"rwd");
            raf.seek(start);
            finished += info.getFinished();
            if(connection.getResponseCode() == 206){
                InputStream is =connection.getInputStream();
                byte[] bytes = new byte[1024*4];
                int len;
                while ((len = is.read(bytes))!=-1){
                    raf.write(bytes,0,len);
                    finished+=len;
                    info.setFinished(finished);
                    if(TaskManager.getInstance().isPause()){
                        info.setDownloading(false);
                        dbHelper.insert(db,info);
                        db.close();
                        return;
                    }
                //实时更新下载进度
                        Intent intent = new Intent(DownloadService.ACTION_UPDATE);
                        intent.putExtra("finished", finished * 100 / length);
                        intent.setAction("android.intent.action.ProgressBroadcast");
                        context.sendBroadcast(intent);

                    
                }
                info.setDownloading(false);
                dbHelper.insert(db,info);
                db.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

运行效果:


这里写图片描述

三、总结
     我简单的实现单任务单线程的下载(适合文件较小)。后续会实现单任务多线程下载、多任务多线程下载(文件较大的)和上传,敬请关注。虽然代码简陋,但简洁思路清晰比较好理解。我的目的不是实现一个高性能可扩展的下载器,而是展示具体如何实现下载的一个流程。当然,我实现的代码都是比较基础的,比较好理解。

扫描领红包


1513521727541.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容