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

来源:互联网 发布:java自带的观察者模式 编辑:程序博客网 时间:2024/05/21 10:35

一、断点下载原理及步骤

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

原理:
     在下载中断的时候,通过数据库,记录中断的文件信息。待恢复上传的时候,从数据库中读取中断的文件信息。通过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();        }

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

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

原创粉丝点击