android开发分享Android实现断点多线程下载

断点多线程下载的几个关键点:①:得到要下载的文件大小后,均分给几个线程。②:使用randomaccessfile类进行读写,可以指定开始写入的位置。③:数据库保存下载信息,

断点多线程下载的几个关键点:①:得到要下载的文件大小后,均分给几个线程。②:使用randomaccessfile类进行读写,可以指定开始写入的位置。③:数据库保存下载信息,下一次继续下载的时候从数据库取出数据,然后从上次下载结束的地方开始。

这里我使用了finaldb的数据库框架,同时在内存中存储了一份所有线程的下载信息,负责时时更新和查询下载进度。我测试用的是百度云网盘,文件大小50m左右。注意,线程中指定了开始结束下载位置的网络请求成功的返回码是206,并不是200。

效果图:

Android实现断点多线程下载

线程类:线程类负责具体的下载,线程的下载信息被存储到了数据库。线程开始下载时,根据线程id查询自己的存储信息,然后开始从指定的位置下载和写入文件。完毕后根据自己的当前下载结果设置自己当前的下载状态。时时的下载进度存储只存储到了内存,只在本次下载结束才存储到数据库。

  public class downloadthread extends thread {       /**     * 数据库操作工具     */    private finaldb finaldb;       /**     * 下载状态:未开始     */    public static final int state_ready = 1;       /**     * 下载状态:下载中     */    public static final int state_loading = 2;       /**     * 下载状态:下载暂停中     */    public static final int state_pausing = 3;       /**     * 下载状态:下载完成     */    public static final int state_finish = 4;       /**     * 下载状态     */    public int downloadstate;       /**     * 线程id     */    private int threadid;       /**     * 要下载的url路径     */    private string url;       /**     * 本线程要下载的文件     */    public randomaccessfile file;       /**     * 构造器     */    public downloadthread(context context, int threadid, string downloadurl, randomaccessfile randomaccessfile) {      this.threadid = threadid;      this.url = downloadurl;      this.file = randomaccessfile;      finaldb = dbutil.getfinaldb(context);    }       @override    public void run() {      //数据库查询本线程下载进度      list<threaddownloadinfobean> list = finaldb.findallbywhere(threaddownloadinfobean.class, "threadid='" + threadid + "'");      //下载信息存放到内存      if (list.get(0) != null) {        maputil.map.put(threadid, list.get(0));      }      //取出实体类      threaddownloadinfobean bean = maputil.map.get(threadid);      utils.print("bean:" + bean.tostring());      inputstream is;      httpurlconnection conn;      try {        utils.print("线程" + threadid + "开始连接");        conn = (httpurlconnection) new url(url).openconnection();        conn.setconnecttimeout(5000);        conn.setreadtimeout(5000);        conn.setrequestmethod("get");        //设置下载开始和结束的位置        conn.setrequestproperty("range", "bytes=" + (bean.startdownloadposition + bean.downloadedsize) + "-" + bean.enddownloadposition);        conn.connect();        if (conn.getresponsecode() == 206) {          //更改下载状态          downloadstate = state_loading;          bean.downloadstate = state_loading;          utils.print("线程" + threadid + "连接成功");          is = conn.getinputstream();          // 1k的数据缓冲          byte[] bs = new byte[1024];          // 读取到的数据长度          int len;          //从指定的位置开始下载          file.seek(bean.startdownloadposition);          // 循环读取,当已经下载的大小达到了指定的本线程负责的大小时跳出循环,线程之间负责的文件首尾有重合的话没有影响,因为写入的内容时相同的          while ((len = is.read(bs)) != -1) {            //不用在这个循环里面更新数据库            file.write(bs, 0, len);            //时时更新内存中的已下载大小信息            bean.downloadedsize += len;            //如果调用者暂停下载,则跳出结束方法            if (downloadstate == state_pausing) {              utils.print("线程" + threadid + "暂停下载");              break;            }          }          is.close();          file.close();        } else {          utils.print("线程" + threadid + "连接失败");        }        conn.disconnect();        //如果这个线程已经下载完了自己负责的部分就修改下载状态        if (bean.downloadedsize >= bean.downloadtotalsize) {          bean.downloadstate = state_finish;        } else {          bean.downloadstate = state_pausing;        }        //内存中信息更新至数据库        finaldb.update(bean, "threadid='" + bean.threadid + "'");      } catch (ioexception e) {        utils.print("线程" + threadid + "io异常");        e.printstacktrace();      }    }  }

线程信息的封装类:负责存储每个线程的id,开始下载的位置,结束下载的位置,已经下载的大小,下载状态等;这个类用finaldb数据库存储到数据库,一定要写get,set方法和空参构造器

  @table(name = "threadinfo")  public class threaddownloadinfobean {       /**     * id     */    public int id;       /**     * 线程id     */    public int threadid;       /**     * 本线程时时下载开始的位置     */    public long startdownloadposition;       /**     * 本线程时时下载结束的位置     */    public long enddownloadposition;       /**     * 本线程负责下载的文件大小     */    public long downloadtotalsize;       /**     * 已经下载了的文件大小     */    public long downloadedsize;       /**     * 本线程的下载状态     */    public int downloadstate;       public threaddownloadinfobean() {       }       public threaddownloadinfobean(int downloadstate, long downloadedsize, long downloadtotalsize, long enddownloadposition, long startdownloadposition, int threadid) {      this.downloadstate = downloadstate;      this.downloadedsize = downloadedsize;      this.downloadtotalsize = downloadtotalsize;      this.enddownloadposition = enddownloadposition;      this.startdownloadposition = startdownloadposition;      this.threadid = threadid;    }       public int getid() {      return id;    }       public void setid(int id) {      this.id = id;    }       public int getthreadid() {      return threadid;    }       public void setthreadid(int threadid) {      this.threadid = threadid;    }       public long getstartdownloadposition() {      return startdownloadposition;    }       public void setstartdownloadposition(long startdownloadposition) {      this.startdownloadposition = startdownloadposition;    }       public long getenddownloadposition() {      return enddownloadposition;    }       public void setenddownloadposition(long enddownloadposition) {      this.enddownloadposition = enddownloadposition;    }       public long getdownloadtotalsize() {      return downloadtotalsize;    }       public void setdownloadtotalsize(long downloadtotalsize) {      this.downloadtotalsize = downloadtotalsize;    }       public long getdownloadedsize() {      return downloadedsize;    }       public void setdownloadedsize(long downloadedsize) {      this.downloadedsize = downloadedsize;    }       public int getdownloadstate() {      return downloadstate;    }       public void setdownloadstate(int downloadstate) {      this.downloadstate = downloadstate;    }       @override    public string tostring() {      return "threaddownloadinfobean{" +          "id=" + id +          ", threadid=" + threadid +          ", startdownloadposition=" + startdownloadposition +          ", enddownloadposition=" + enddownloadposition +          ", downloadtotalsize=" + downloadtotalsize +          ", downloadedsize=" + downloadedsize +          ", downloadstate=" + downloadstate +          '}';    }  }

下载工具类:这个类负责得到下载文件大小,分配线程下载大小,管理下载线程

  public class downutil {       /**     * 数据库操作工具     */    public finaldb finaldb;       /**     * 下载状态:准备好     */    public static final int state_ready = 1;       /**     * 下载状态:下载中     */    public static final int state_loading = 2;       /**     * 下载状态:暂停中     */    public static final int state_pausing = 3;       /**     * 下载状态:下载完成     */    public static final int state_finish = 4;       /**     * 下载状态     */    public int downloadstate;       /**     * context     */    private context context;       /**     * 要下载文件的大小     */    public long filesize;       /**     * 线程集合     */    private arraylist<downloadthread> threadlist = new arraylist<>();       /**     * 构造器     */    public downutil(context context) {      this.context = context;      finaldb = dbutil.getfinaldb(context);      judgedownstate();    }       /**     * 初始化时判断下载状态     */    public void judgedownstate() {      //取出数据库中的下载信息,存储到内存中      list<threaddownloadinfobean> list = finaldb.findall(threaddownloadinfobean.class);      if (list != null && list.size() == downloadactivity.threadnum) {        for (int i = 0; i < list.size(); i++) {          maputil.map.put(i, list.get(i));        }      }      //查询sp中是否存储过要下载的文件大小      long spfilesize = sputil.getinstance(context).getlong(downloadactivity.filename, 0l);      long downloadedsize = getfinishedsize();      //sp中或者数据库中没有查询到说明从没有进行过下载      if (spfilesize == 0 || downloadedsize == 0) {        downloadstate = state_ready;      } else if (downloadedsize >= spfilesize) {        downloadstate = state_finish;      } else {        downloadstate = state_pausing;        filesize = spfilesize;      }    }       /**     * 点击了开始按钮     */    public void clickdownloadbtn() {      if (downloadstate == state_ready) {        startdownload();      } else if (downloadstate == state_pausing) {        continuedownload();      }    }       /**     * 进入应用第一次开始下载     */    private void startdownload() {      //开启新线程,得到要下载的文件大小      new thread() {        @override        public void run() {          try {            httpurlconnection conn;            conn = (httpurlconnection) new url(downloadactivity.url).openconnection();            conn.setconnecttimeout(5000);            conn.setreadtimeout(5000);            conn.setrequestmethod("get");            conn.connect();            if (conn.getresponsecode() == 200) {              utils.print("downutil连接成功");              //得到要下载的文件大小              filesize = conn.getcontentlength();              //得到文件名后缀名              string contentdisposition = new string(conn.getheaderfield("content-disposition").getbytes("iso-8859-1"), "utf-8");              string filename = "下载测试" + contentdisposition.substring(contentdisposition.lastindexof("."), contentdisposition.lastindexof("""));              //得到存储路径              string sdcardpath = context.getexternalfilesdir(null).getpath();              downloadactivity.filename = sdcardpath + "/" + filename;              sputil.getinstance(context).savestring(downloadactivity.file_name, downloadactivity.filename);              sputil.getinstance(context).savelong(downloadactivity.filename, filesize);              /*               * 计算一下每个线程需要分担的下载文件大小 比如 总下载量为100 一共有三个线程               * 那么 线程1负责0-32,线程2负责33-65,线程3负责66-99和100,               * 也就是说下载总量除以线程数如果有余数,那么最后一个线程多下载一个余数部分               */              //每个线程均分的大小              long threaddownsize = filesize / downloadactivity.threadnum;              //线程均分完毕剩余的大小              long leftdownsize = filesize % downloadactivity.threadnum;              //创建要写入的文件              randomaccessfile file = new randomaccessfile(downloadactivity.filename, "rw");              //设置文件大小              file.setlength(filesize);              //关闭              file.close();              for (int i = 0; i < downloadactivity.threadnum; i++) {                utils.print("开启线程" + i);                //指定每个线程开始下载的位置                long startposition = i * threaddownsize;                //指定每个线程负责下载的大小,当现场是集合里面最后一个线程的时候,它要增加leftdownsize的大小                threaddownsize = i == downloadactivity.threadnum - 1 ? threaddownsize + leftdownsize : threaddownsize;                //存储線程信息                threaddownloadinfobean bean = new threaddownloadinfobean(downloadthread.state_ready, 0, threaddownsize, startposition + threaddownsize, startposition, i);                finaldb.save(bean);                randomaccessfile threadfile = new randomaccessfile(downloadactivity.filename, "rw");                threadlist.add(new downloadthread(context, i, downloadactivity.url, threadfile));                threadlist.get(i).start();              }              downloadstate = state_loading;              downloadinfolistener.connectsuccess();            } else {              utils.print("downutil-连接失败");              downloadinfolistener.connectfail();            }            conn.disconnect();          } catch (ioexception e) {            utils.print("downutil-io异常");            downloadinfolistener.ioexception();            e.printstacktrace();          }        }      }.start();    }       /**     * 继续下载     */    private void continuedownload() {      list<threaddownloadinfobean> list = finaldb.findall(threaddownloadinfobean.class);      for (int i = 0; i < downloadactivity.threadnum; i++) {        //当前线程已经下载完了就不再开启        if (list.get(i).downloadstate != downloadthread.state_finish) {          utils.print("重新开启线程" + i);          randomaccessfile threadfile = null;          try {            threadfile = new randomaccessfile(downloadactivity.filename, "rw");          } catch (filenotfoundexception e) {            e.printstacktrace();          }          downloadthread downloadthread = new downloadthread(context, i, downloadactivity.url, threadfile);          threadlist.add(downloadthread);          downloadthread.start();        }      }      downloadstate = state_loading;      downloadinfolistener.connectsuccess();    }       /**     * 点击了暂停的按钮     */    public void clickpausebtn() {      if (downloadstate == state_loading) {        stopdownload();      }    }       /**     * 暂停下载     */    private void stopdownload() {      for (int i = 0; i < threadlist.size(); i++) {        if (threadlist.get(i).downloadstate == downloadthread.state_loading) {          threadlist.get(i).downloadstate = downloadthread.state_pausing;        }      }      downloadstate = state_pausing;      threadlist.clear();    }       /**     * 返回此刻所有线程完成的下载大小     */    public long getfinishedsize() {      long totalsize = 0;      for (int i = 0; i < downloadactivity.threadnum; i++) {        threaddownloadinfobean bean = maputil.map.get(i);        if (bean != null) {          //如果该线程已经下载的部分大于分配给它的部分多余部分不予计算          long addsize = bean.downloadedsize > bean.downloadtotalsize ? bean.downloadtotalsize : bean.downloadedsize;          totalsize += addsize;        }      }      return totalsize;    }       /**     * 下载信息监听器     */    private downloadinfolistener downloadinfolistener;       /**     * 下载信息监听器     */    public interface downloadinfolistener {         void connectsuccess();         void connectfail();         void ioexception();    }       /**     * 设置下载信息监听器     */    public void setdownloadinfolistener(downloadinfolistener downloadinfolistener) {      this.downloadinfolistener = downloadinfolistener;    }     }

页面activity:负责展示下载进度等信息,提供操作页面

  public class downloadactivity extends baseactivity {       /**     * 下载地址输入框     */    private edittext et_download_url;       /**     * 确定输入地址的按钮     */    private button btn_download_geturl;       /**     * 进度条     */    private numberprogressview np_download;       /**     * 开始下载的按钮     */    private button btn_download_start;       /**     * 暂停下载的按钮     */    private button btn_download_pause;       /**     * 取消下载的按钮     */    private button btn_download_cancel;       /**     * 文件信息显示     */    private textview tv_download_file_info;       /**     * 下载速度显示     */    private textview tv_download_speed;       /**     * 显示下载信息     */    private textview tv_download_speed_info;       /**     * 每隔一段时间刷新下载进度显示     */    private final static int what_increace = 1;       /**     * 得到了文件名称     */    private final static int what_get_filename = 2;       /**     * downutil连接失败     */    private final static int whai_connect_fail = 3;       /**     * downutilio异常     */    private final static int what_io_exception = 4;       /**     * 下载工具     */    private downutil downutil;       /**     * 需要开启的线程数量     */    public static final int threadnum = 5;       /**     * 存放文件路径名称的sp键名     */    public static final string file_name = "filename";       /**     * 要下载的文件的url地址     */    public static string url = "";        /**     * 文件下载路径和文件名称     */    public static string filename;       /**     * 上次统计已经完成下载的文件大小     */    private long lastfinishedsize;       private handler handler = new handler() {      @override      public void handlemessage(message msg) {        super.handlemessage(msg);        switch (msg.what) {          case what_increace:            updateview();            break;          case what_get_filename:            tv_download_file_info.settext("下载路径:" + filename);            break;          case whai_connect_fail:            tv_download_file_info.settext("连接失败");            break;          case what_io_exception:            tv_download_file_info.settext("io异常");            break;        }      }    };       /**     * 更新视图     */    private void updateview() {      //当前已经完成下载的文件大小      long currentfinishedsize = downutil.getfinishedsize();      //要显示的下载信息的文字      stringbuilder downloadinfo = new stringbuilder();      tv_download_speed.settext("当前下载速度:" + formatesize(currentfinishedsize - lastfinishedsize) + "/s");      //本次统计比上次统计增加了,更新视图,本次统计较上次统计没有变化,不做视图更新      if (currentfinishedsize > lastfinishedsize) {        lastfinishedsize = currentfinishedsize;        downloadinfo.append("下载进度:" + formatesize(currentfinishedsize) + "/" + formatesize(downutil.filesize) + "n");        for (int i = 0; i < threadnum; i++) {          threaddownloadinfobean bean = maputil.map.get(i);          if (bean.downloadtotalsize != 0) {            downloadinfo.append("线程" + i + "下载进度:" + bean.downloadedsize * 100 / bean.downloadtotalsize + "%  " + formatesize(bean.downloadedsize) + "/" + formatesize(bean.downloadtotalsize) + "n");          }        }        np_download.setprogress((int) (currentfinishedsize * 100 / downutil.filesize));        tv_download_speed_info.settext(downloadinfo);        //下载完成后        if (currentfinishedsize >= downutil.filesize) {          tv_download_speed.settext("下载完毕");          handler.removemessages(what_increace);          return;        }      }      handler.sendemptymessagedelayed(what_increace, 1000);    }       /**     * 返回子类要显示的布局 r.layout......     */    @override    protected int childview() {      return r.layout.activity_download;    }       /**     * 强制子类实现该抽象方法,初始化各自的view     */    @override    protected void findchildview() {      et_download_url = (edittext) findviewbyid(r.id.et_download_url);      btn_download_geturl = (button) findviewbyid(r.id.btn_download_geturl);      np_download = (numberprogressview) findviewbyid(r.id.np_download);      btn_download_start = (button) findviewbyid(r.id.btn_download_start);      btn_download_pause = (button) findviewbyid(r.id.btn_download_pause);      btn_download_cancel = (button) findviewbyid(r.id.btn_download_cancel);      tv_download_file_info = (textview) findviewbyid(r.id.tv_download_file_info);      tv_download_speed = (textview) findviewbyid(r.id.tv_download_speed);      tv_download_speed_info = (textview) findviewbyid(r.id.tv_download_speed_info);    }       /**     * 强制子类实现该抽象方法,初始化各自数据     */    @override    protected void initchilddata() {      downutil = new downutil(this);      lastfinishedsize = downutil.getfinishedsize();      stringbuilder downloadinfo = new stringbuilder();      filename = sputil.getinstance(this).getstring(file_name, null);      if (filename != null) {        downloadinfo.append("下载路径:" + filename);        if (downutil.downloadstate == downutil.state_finish) {          np_download.setprogress(100);        } else if (downutil.downloadstate == downutil.state_pausing) {          np_download.setprogress((int) (downutil.getfinishedsize() * 100 / downutil.filesize));        }        tv_download_file_info.settext(downloadinfo);      }    }       /**     * 强制子类实现该抽象方法,设置自己的监听器     */    @override    protected view[] setchildlistener() {      downutil.setdownloadinfolistener(new downutil.downloadinfolistener() {           @override        public void connectsuccess() {          //不能在此更新,需要到主线程刷新ui          handler.sendemptymessage(what_get_filename);          handler.sendemptymessage(what_increace);        }           @override        public void connectfail() {          handler.sendemptymessage(whai_connect_fail);        }           @override        public void ioexception() {          handler.sendemptymessage(what_io_exception);        }      });      return new view[]{btn_download_start, btn_download_pause, btn_download_geturl, btn_download_cancel};    }       /**     * 子类在这个方法里面实现自己view的点击监听事件的相应     *     * @param v 父类传递到子类的点击的view     */    @override    protected void clickchildview(view v) {      switch (v.getid()) {           case r.id.btn_download_start:          downutil.clickdownloadbtn();          break;           case r.id.btn_download_pause:          downutil.clickpausebtn();          handler.removemessages(what_increace);          break;           case r.id.btn_download_cancel:          //停止发送消息          handler.removemessages(what_increace);          //暂停下载          downutil.clickpausebtn();          //重置状态          downutil.downloadstate = downutil.state_ready;          //统计下载的大小归零          lastfinishedsize = 0;          //重置文本信息          tv_download_speed.settext("");          tv_download_file_info.settext("");          tv_download_speed_info.settext("");          //进度条归零          np_download.setprogress(0);          //清空内存数据          maputil.map.clear();          //sp清理          sputil.getinstance(this).removeall();          //清空数据库          downutil.finaldb.deleteall(threaddownloadinfobean.class);          //删除文件          if (filename != null) {            file file = new file(filename);            if (file.exists()) {              boolean delete = file.delete();              if (delete) {                utils.toasts(this, "删除" + filename + "成功");              } else {                utils.toasts(this, "删除失败");              }            } else {              utils.toasts(this, "文件不存在");            }          } else {            utils.toasts(this, "文件不存在");          }          break;           case r.id.btn_download_geturl:          string edittexturl = et_download_url.gettext().tostring();          if (edittexturl.trim().equals("")) {            utils.toasts(this, "请输入地址");          } else {            url = edittexturl;          }          break;      }    }       @override    protected void ondestroy() {      handler.removecallbacksandmessages(null);      downutil.clickpausebtn();      super.ondestroy();    }       /**     * 格式化数据大小     */    private string formatesize(long size) {      return formatter.formatfilesize(this, size);    }     }

布局

  <?xml version="1.0" encoding="utf-8"?>  <linearlayout    xmlns:android="https://schemas.android.com/apk/res/android"    xmlns:tools="https://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="com.example.testdemo.activity.downloadactivity">       <linearlayout      android:layout_width="match_parent"      android:layout_height="wrap_content"      android:orientation="horizontal">         <edittext        android:id="@+id/et_download_url"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_weight="1"        android:hint="输入下载地址"/>         <button        android:id="@+id/btn_download_geturl"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="确定"/>       </linearlayout>       <com.example.testdemo.view.numberprogressview      android:id="@+id/np_download"      android:layout_width="match_parent"      android:layout_height="100dp">    </com.example.testdemo.view.numberprogressview>       <linearlayout      android:layout_width="match_parent"      android:layout_height="wrap_content"      android:orientation="horizontal">         <button        android:id="@+id/btn_download_start"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="开始下载"/>         <button        android:id="@+id/btn_download_pause"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="暂停下载"/>         <button        android:id="@+id/btn_download_cancel"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="取消下载"/>       </linearlayout>       <textview      android:id="@+id/tv_download_file_info"      android:layout_width="match_parent"      android:layout_height="wrap_content"/>       <textview      android:id="@+id/tv_download_speed"      android:layout_width="match_parent"      android:layout_height="wrap_content"/>       <textview      android:id="@+id/tv_download_speed_info"      android:layout_width="match_parent"      android:layout_height="wrap_content"/>     </linearlayout>

下载activity的父类

  public abstract class baseactivity extends activity {       /**     * 点击监听器,子类也可以使用     */    protected baseonclicklistener onclicklistener = new baseonclicklistener();       /**     * 在oncreate方法里面,找到视图,初始化数据,设置点击监听器     */    @override    protected void oncreate(bundle savedinstancestate) {      super.oncreate(savedinstancestate);      setcontentview(childview());      findview();      initdata();      setlistener();    }       /**     * 返回子类要显示的布局 r.layout......     */    protected abstract int childview();       /**     * 找到需要的视图---网址:https://www.buzzingandroid.com/tools/android-layout-finder/     */    private void findview() {      findchildview();    }       /**     * 初始化数据     */    private void initdata() {      // 初始化子类的数据      initchilddata();    }       /**     * 对需要设置监听 的视图设置监听     */    private void setlistener() {      // 子类实现setchildlistene()方法,返回一个view数组,里面包含所有需要设置点击监听的view,然后进行for循环,对里面所有view设置监听器      if (setchildlistener() == null || setchildlistener().length == 0) {        return;      }      view[] viewarray = setchildlistener();      for (view view : viewarray) {        view.setonclicklistener(onclicklistener);      }    }       /**     * 自定义的点击监听类     */    protected class baseonclicklistener implements view.onclicklistener {      @override      public void onclick(view v) {        clickchildview(v);      }    }       /**     * 强制子类实现该抽象方法,初始化各自的view     */    protected abstract void findchildview();       /**     * 强制子类实现该抽象方法,初始化各自数据     */    protected abstract void initchilddata();       /**     * 强制子类实现该抽象方法,设置自己的监听器     */    protected abstract view[] setchildlistener();       /**     * 子类在这个方法里面实现自己view的点击监听事件的相应     *     * @param v 父类传递到子类的点击的view     */       protected abstract void clickchildview(view v);     }

得到数据库的操作类

  public class dbutil {       public static finaldb getfinaldb(final context context) {         finaldb finaldb = finaldb.create(context, "remuxing.db", false, 1, new finaldb.dbupdatelistener() {        @override        public void onupgrade(sqlitedatabase db, int oldvirsion, int newvirsion) {           }      });      return finaldb;    }  }

内存中存储下载信息的类

  public class maputil {    public static hashmap<integer, threaddownloadinfobean> map = new hashmap<>();  }

sp存储的工具类

  public enum sputil {       sputil;       public static final string notificationbar_height = "notificationbar_height";       private static sharedpreferences sp;       public static sputil getinstance(context context) {         if (sp == null) {        sp = context.getsharedpreferences("remuxing", context.mode_private);      }      return sputil;    }       public void savelong(string key, long value) {      sp.edit().putlong(key, value).apply();    }       public long getlong(string key, long defvalue) {      return sp.getlong(key, defvalue);    }       public void savestring(string key, string value) {      sp.edit().putstring(key, value).apply();    }       public string getstring(string key, string defvalue) {      return sp.getstring(key, defvalue);    }       public void removeall() {      sp.edit().clear().apply();    }     }

工具类

  public class utils {       /**     * 打印     */    public static void print(string message) {      log.e("tag", "-----   " + message + "   ------") ;    }       /**     * 短吐司     */    public static void toasts(context context, string message) {      toast.maketext(context, message, toast.length_short).show();    }  }

进度条:这个可以忽略,用安卓原生的,代码看我的另一篇博客:android自定义view实现水平带数字百分比进度条

以上就是android开发分享Android实现断点多线程下载的全部内容,希望对大家的学习有所帮助,也希望大家多多支持<计算机技术网(www.ctvol.com)!!>。

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/addevelopment/901929.html

(0)
上一篇 2021年10月24日
下一篇 2021年10月24日

精彩推荐