Android 断点续传原理以及实现_Android

Android 断点续传原理以及实现

0.  前言

在Android开发中,断点续传听起来挺容易,在下载一个文件时点击暂停任务暂停,点击开始会继续下载文件。但是真正实现起来知识点还是蛮多的,因此今天有时间实现了一下,并进行记录。

1.  断点续传原理

在本地下载过程中要使用数据库实时存储到底存储到文件的哪个位置了,这样点击开始继续传递时,才能通过HTTP的GET请求中的setRequestProperty()方法可以告诉服务器,数据从哪里开始,到哪里结束。同时在本地的文件写入时,RandomAccessFile的seek()方法也支持在文件中的任意位置进行写入操作。同时通过广播将子线程的进度告诉Activity的ProcessBar。

2.  Activity的按钮响应

当点击开始按钮时,将url写在了FileInfo类的对象info中并通过Intent从Activity传递到了Service中。这里使用setAction()来区分是开始按钮还是暂停按钮。

public class FileInfo implements Serializable{
  private String url; //URL
  private int length; //长度或结束位置
  private int start; //开始位置
  private int now;//当前进度
//构造方法,set/get略
}
//开始按钮逻辑,停止逻辑大致相同
strat.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
    Intent intent = new Intent(MainActivity.this,DownLoadService.class);
    intent.setAction(DownLoadService.ACTION_START);
    intent.putExtra("fileUrl",info);
    startService(intent);
  }
}); 

3.  在Service中的子线程中获取文件大小

在Service中的onStartCommand()中,将FileInfo对象从Intent中取出,如果是开始命令,则开启一个线程,根据该url去获得要下载文件的大小,将该大小写入对象并通过Handler传回Service,同时在本地创建一个相同大小的本地文件。暂停命令最后会讲到。

public void run() {
      HttpURLConnection urlConnection = null;
      RandomAccessFile randomFile = null;
      try {
        URL url = new URL(fileInfo.getUrl());
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setConnectTimeout(3000);
        urlConnection.setRequestMethod("GET");
        int length = -1;
        if (urlConnection.getResponseCode() == HttpStatus.SC_OK) {
          //获得文件长度
          length = urlConnection.getContentLength();
        }
        if (length <= 0) {
          return;
        }
        //创建相同大小的本地文件
        File dir = new File(DOWNLOAD_PATH);
        if (!dir.exists()) {
          dir.mkdir();
        }
        File file = new File(dir, FILE_NAME);
        randomFile = new RandomAccessFile(file, "rwd");
        randomFile.setLength(length);
        //长度给fileInfo对象
        fileInfo.setLength(length);
        //通过Handler将对象传递给Service
        mHandle.obtainMessage(0, fileInfo).sendToTarget();
      } catch (Exception e) {
        e.printStackTrace();
      } finally { //流的回收逻辑略
      }
    }
  } 

4.  数据库操作封装

在Service的handleMessage()方法中拿到有length属性的FileInfo对象,并使用自定义的DownLoadUtil类进行具体的文件下载逻辑。这里传入上下文,因为数据库处理操作需要用到。

downLoadUtil = new DownLoadUtil(DownLoadService.this,info);

downLoadUtil.download();

这里有一个数据库操作的接口ThreadDAO,内部有增删改查等逻辑,用于记录下载任务的信息。自定义一个ThreadDAOImpl类将这里的逻辑实现,内部数据库创建关于继承SQLiteOpenHelper的自定义类的逻辑就不贴了,比较简单,该类会在ThreadDAOImpl类的构造方法中创建实例。完成底层数据库操作的封装。

public interface ThreadDAO {
  //插入一条数据
  public void insert(FileInfo info);
  //根据URL删除一条数据
  public void delete(String url);
  //根据URL更新一条进度
  public void update(String url,int finished);
  //根据URL找到一条数据
  public List<FileInfo> get(String url);
  //是否存在
  public boolean isExits(String url);
} 

5.  具体的文件下载逻辑

public class DownLoadUtil {
  //构造方法略
  public void download(){
    List<FileInfo> lists = threadDAO.get(fileInfo.getUrl());
    FileInfo info = null;
    if(lists.size() == 0){
      //第一次下载,创建子线程下载
      new MyThread(fileInfo).start();
    }else{
      //中间开始的
      info = lists.get(0);
      new MyThread(info).start();
    }
  } 

  class MyThread extends Thread{
    private FileInfo info = null;
    public MyThread(FileInfo threadInfo) {
      this.info = threadInfo;
    }
    @Override
    public void run() {
      //向数据库添加线程信息
      if(!threadDAO.isExits(info.getUrl())){
        threadDAO.insert(info);
      }
      HttpURLConnection urlConnection = null;
      RandomAccessFile randomFile =null;
      InputStream inputStream = null;
      try {
        URL url = new URL(info.getUrl());
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setConnectTimeout(3000);
        urlConnection.setRequestMethod("GET");
        //设置下载位置
        int start = info.getStart() + info.getNow();
        urlConnection.setRequestProperty("Range","bytes=" + start + "-" + info.getLength()); 

        //设置文件写入位置
        File file = new File(DOWNLOAD_PATH,FILE_NAME);
        randomFile = new RandomAccessFile(file, "rwd");
        randomFile.seek(start); 

        //向Activity发广播
        Intent intent = new Intent(ACTION_UPDATE);
        finished += info.getNow(); 

        if (urlConnection.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) {
          //获得文件流
          inputStream = urlConnection.getInputStream();
          byte[] buffer = new byte[512];
          int len = -1;
          long time = System.currentTimeMillis();
          while ((len = inputStream.read(buffer))!= -1){
            //写入文件
            randomFile.write(buffer,0,len);
            //把进度发送给Activity
            finished += len;
            //看时间间隔,时间间隔大于500ms再发
            if(System.currentTimeMillis() - time >500){
              time = System.currentTimeMillis();
              intent.putExtra("now",finished *100 /fileInfo.getLength());
              context.sendBroadcast(intent);
            }
            //判断是否是暂停状态
            if(isPause){
              threadDAO.update(info.getUrl(),finished);
              return; //结束循环
            }
          }
          //删除线程信息
          threadDAO.delete(info.getUrl());
        }
      }catch (Exception e){
        e.printStackTrace();
      }finally {//回收工作略
      }
    }
  }
} 

上面也讲到使用自定义的DownLoadUtil类进行具体的文件下载逻辑,这也是最关键的部分了,在该类的构造方法中进行ThreadDAOImpl实例的创建。并在download()中通过数据库查询的操作,判断是否是第一次开始下载任务,如果是,则开启一个子线程MyThread进行下载任务,否则将进度信息从数据库中取出,并将该信息传递给MyThread。

在MyThread中,通过info.getStart() + info.getNow()设置开始下载的位置,如果是第一次下载两个数将都是0,如果是暂停后再下载,则info.getNow()会取出非0值,该值来自数据库存储。使用setRequestProperty告知服务器从哪里开始传递数据,传递到哪里结束,本地使用RandomAccessFile的seek()方法进行数据的本地存储。使用广播将进度的百分比传递给Activity,Activity再改变ProcessBar进行UI调整。

这里很关键的一点是在用户点击暂停后会在Service中调用downLoadUtil.isPause = true,因此上面while循环会结束,停止下载并通过数据库的update()保存进度值。从而在续传时取出该值,重新对服务器发起文件起始点的下载任务请求,同时也在本地文件的相应位置继续写入操作。

6.  效果如下所示

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
断点续传原理
ios 断点续传实现原理、android断点续传原理、android实现断点续传、断点续传 原理、java实现断点续传,以便于您获取更多的相关知识。

时间: 2024-08-02 22:50:43

Android 断点续传原理以及实现_Android的相关文章

Android 断点续传原理以及实现

Android 断点续传原理以及实现 0.  前言 在Android开发中,断点续传听起来挺容易,在下载一个文件时点击暂停任务暂停,点击开始会继续下载文件.但是真正实现起来知识点还是蛮多的,因此今天有时间实现了一下,并进行记录. 1.  断点续传原理 在本地下载过程中要使用数据库实时存储到底存储到文件的哪个位置了,这样点击开始继续传递时,才能通过HTTP的GET请求中的setRequestProperty()方法可以告诉服务器,数据从哪里开始,到哪里结束.同时在本地的文件写入时,RandomAc

Android 断点续传的原理剖析与实例讲解_Android

 本文所要讲的是Android断点续传的内容,以实例的形式进行了详细介绍.        一.断点续传的原理        其实断点续传的原理很简单,就是在http的请求上和一般的下载有所不同而已.        打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:        假设服务器域名为www.jizhuomi.com/android,文件名为down.zip. get /down.zip http/1.1 accept: image/gif, image/x-xbitmap,

PC版与Android手机版带断点续传的多线程下载_Android

一.多线程下载         多线程下载就是抢占服务器资源         原理:服务器CPU 分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源.       1.设置开启线程数,发送http请求到下载地址,获取下载文件的总长度           然后创建一个长度一致的临时文件,避免下载到一半存储空间不够了,并计算每个线程下载多少数据              2.计算每个线程下载数据的开始和结束位置           再次发送

Android编程开发实现多线程断点续传下载器实例_Android

本文实例讲述了Android编程开发实现多线程断点续传下载器.分享给大家供大家参考,具体如下: 使用多线程断点续传下载器在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度,在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,比如无信号断线.电量不足等情况下,这就需要使用到断点续传功能,下次启动时从记录位置继续下载,可避免重复部分的下载.这里采用数据库来记录下载的进度. 效果图:   断点续传 1.断点续传需要在下载过程中记录每条线程的下载进度 2.每次下载开始之前先读取数据库

Android 断点续传的原理剖析与实例讲解

本文所要讲的是Android断点续传的内容,以实例的形式进行了详细介绍. 一.断点续传的原理 其实断点续传的原理很简单,就是在http的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 假设服务器域名为www.jizhuomi.com/android,文件名为down.zip. get /down.zip http/1.1 accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appl

深度理解Android InstantRun原理以及源码分析

深度理解Android InstantRun原理以及源码分析 @Author 莫川 Instant Run官方介绍 简单介绍一下Instant Run,它是Android Studio2.0以后新增的一个运行机制,能够显著减少你第二次及以后的构建和部署时间.简单通俗的解释就是,当你在Android Studio中改了你的代码,Instant Run可以很快的让你看到你修改的效果.而在没有Instant Run之前,你的一个小小的修改,都肯能需要几十秒甚至更长的等待才能看到修改后的效果. 传统的代

android lbs-老师,您好:请教Android定位原理

问题描述 老师,您好:请教Android定位原理 我现在在做的是个Android定位系统,不能用第三方的定位API,像高德.百度等都不能用,我现在除了找到GPS定位能用之外,到目前还没找到其他的合适的定位方法,劳烦老师给我解答一下,谢谢您了! 解决方案 可以通过基站定位,你手机和基站有交互,通过对信号强弱得分析和基站得分布对应的位置,获取你所在得位置.

Android Handler 原理分析及实例代码

Android Handler 原理分析 Handler一个让无数android开发者头疼的东西,希望我今天这边文章能为您彻底根治这个问题 今天就为大家详细剖析下Handler的原理 Handler使用的原因 1.多线程更新Ui会导致UI界面错乱 2.如果加锁会导致性能下降 3.只在主线程去更新UI,轮询处理 Handler使用简介 其实关键方法就2个一个sendMessage,用来接收消息 另一个是handleMessage,用来处理接收到的消息 下面是我参考疯狂android讲义,写的一个子

Android 操作系统获取Root权限 原理详细解析_Android

android root权限破解分析 许多机友新购来的Android机器没有破解过Root权限,无法使用一些需要高权限的软件,以及进行一些高权限的操作,其实破解手机Root权限是比较简单及安全的,破解Root权限的原理就是在手机的/system/bin/或/system/xbin/目录下放置一个可执行文件"su",这是一个二进制文件,相当于电脑上的exe文件,仅仅在系统中置入这个"su"文件是不会给手机的软件或硬件造成任何故障. 下面的代码是android系统原版的