自己动手实现一个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();
        }

运行效果:
这里写图片描述

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值