Android多线程+单线程+断点续传+进度条显示下载功能

Android多线程+单线程+断点续传+进度条显示下载功能,第1张

概述效果图白话分析:多线程:肯定是多个线程咯断点:线程停止下载的位置续传:线程从停止下载的位置上继续下载,直到完成任务为止。

效果图

白话分析:

多线程:肯定是多个线程咯

断点:线程停止下载的位置

续传:线程从停止下载的位置上继续下载,直到完成任务为止。

核心分析:

断点:

当前线程已经下载的数据长度

续传:

向服务器请求上次线程停止下载位置的数据

con.setRequestProperty("Range","bytes=" + start + "-" + end);

分配线程:

 int currentPartSize = fileSize / mThreadNum;

定义位置

定义线程开始下载的位置和结束的位置

for (int i = 0; i < mThreadNum; i++) {int start = i * currentPartSize;//计算每条线程下载的开始位置 int end = start + currentPartSize-1;//线程结束的位置 if(i==mThreadNum-1){   end=fileSize;  }}

创建数据库:

由于每一个文件要分成多个部分,要被不同的线程同时进行下载。当然要创建线程表,保存当前线程下载开始的位置和结束的位置,还有完成进度等。创建file表,保存当前下载的文件信息,比如:文件名,url,下载进度等信息
线程表:

public static final String CREATE_table_sql="create table "+table_name+"(_ID integer primary "      +"key autoincrement,threadID,start,end,completed,url)";

file表:

public static final String CREATE_table_sql="create table "+table_name+"(_ID integer primary" +    " key autoincrement,filename,url,length,finished)";

创建线程类

无非就2个类,一个是线程管理类DownLoadManager.Java,核心方法:start(),stop(),restart(),addTask().clear()。另一个是线程任务类

DownLoadTask.java,就是一个线程类,用于下载线程分配好的任务。后面会贴出具体代码。

创建数据库方法类

无非就是单例模式,封装一些增删改查等基础数据库方法,后面会贴出具体代码。

创建实体类

也就是创建ThreadInfo和fileInfo这2个实体类,把下载文件信息和线程信息暂时存储起来。

引入的第三方开源库

NumberProgressbar是一个关于进度条的开源库,挺不错的。直达链接

代码具体分析

1.首先是创建实体类,文件的实体类fileInfo,肯定有filename,finised,isstop,isDownloading这些属性。线程的实体类ThreadInfo肯定有threadID,url这些属性。这些都很简单

 //ThredInfo.java  public class fileInfo {  private String filename; //文件名  private String url; //下载地址  private int length; //文件大小  private int finished; //下载已完成进度  private boolean isstop=false; //是否暂停下载  private boolean isDownloading=false; //是否正在下载  public fileInfo(){  }  public fileInfo(String filename,String url){    this.filename=filename;    this.url=url;  }  public String getfilename() {    return filename;  }  public voID setfilename(String filename) {    this.filename = filename;  }  public String getUrl() {    return url;  }  public voID setUrl(String url) {    this.url = url;  }  public int getLength() {    return length;  }  public voID setLength(int length) {    this.length = length;  }  public int getFinished() {    return finished;  }  public voID setFinished(int finished) {    this.finished = finished;  }  public boolean isstop() {    return isstop;  }  public voID setStop(boolean stop) {    isstop = stop;  }  public boolean isDownloading() {    return isDownloading;  }  public voID setDownloading(boolean downloading) {    isDownloading = downloading;  }  @OverrIDe  public String toString() {    return "fileInfo{" +        "filename='" + filename + '\'' +        ",url='" + url + '\'' +        ",length=" + length +        ",finished=" + finished +        ",isstop=" + isstop +        ",isDownloading=" + isDownloading +        '}';  }} //fileInfo.java  public class fileInfo {  private String filename; //文件名  private String url; //下载地址  private int length; //文件大小  private int finished; //下载已完成进度  private boolean isstop=false; //是否暂停下载  private boolean isDownloading=false; //是否正在下载  public fileInfo(){  }  public fileInfo(String filename,isDownloading=" + isDownloading +        '}';  }}

2.实体类写完了,那么接下来写创建一个类,继承sqliteOpenHelper类,来管理数据库连接,主要作用:管理数据库的初始化,并允许应用程序通过该类获取sqliteDatabase对象。

 public class ThreadHelper extends sqliteOpenHelper{  public static final String table_name="downthread";  public static final String CREATE_table_sql="create table "+table_name+"(_ID integer primary "      +"key autoincrement,url)";  public ThreadHelper(Context context,String name,int version) {    super(context,name,null,version);  }  @OverrIDe  public voID onCreate(sqliteDatabase db) {    db.execsql(CREATE_table_sql);  }  @OverrIDe  public voID onUpgrade(sqliteDatabase db,int oldVersion,int newVersion) {  }}

3.接下来封装一些数据库的增删改查 *** 作,用的单例模式,用双重检验锁实现单例。好处:既能很大程度上确保线程安全,又能实现延迟加载。 缺点:使用volatile关键字会使JVM对该代码的优化丧失,影响性能。并且在一些高并发的情况,仍然可能会创建多个实例,这称为双重检验锁定失效。单例模式

 public class Thread {  private sqliteDatabase db;  public static final String DB_name="downthread.db3";  public static final int VERSION=1;  private Context mContext;  private volatile static Thread t=null;  private Thread(){    mContext= BaseApplication.getContext();    db=new ThreadHelper(mContext,DB_name,VERSION).getReadableDatabase();  }  public static Thread getInstance(){    if(t==null){      synchronized (Thread.class){        if(t==null){          t=new Thread();        }      }    }    return t;  }  public sqliteDatabase getDb(){    return db;  }  //保存当前线程下载进度  public synchronized voID insert(ThreadInfo threadInfo){    ContentValues values=new ContentValues();    values.put("threadID",threadInfo.getThreadID());    values.put("start",threadInfo.getStart());    values.put("end",threadInfo.getEnd());    values.put("completed",threadInfo.getCompeleted());    values.put("url",threadInfo.getUrl());    long rowID=db.insert(ThreadHelper.table_name,values);    if(rowID!=-1){      UtilsLog.i("插入线程记录成功");    }else{      UtilsLog.i("插入线程记录失败");    }  }  //查询当前线程 下载的进度  public synchronized ThreadInfo query(String threadID,String queryUrl){    Cursor cursor=db.query(ThreadHelper.table_name,"threadID= ? and url= ?",new String[]{threadID,queryUrl},null);    ThreadInfo info=new ThreadInfo();    if(cursor!=null){      while (cursor.movetoNext()){        int start=cursor.getInt(2);        int end=cursor.getInt(3);        int completed=cursor.getInt(4);        String url=cursor.getString(5);        info.setThreadID(threadID);        info.setStart(start);        info.setEnd(end);        info.setCompeleted(completed);        info.setUrl(url);      }      cursor.close();    }    return info;  }  //更新当前线程下载进度  public synchronized voID update(ThreadInfo info){    ContentValues values=new ContentValues();    values.put("start",info.getStart());    values.put("completed",info.getCompeleted());    db.update(ThreadHelper.table_name,values,new String[]{info.getThreadID(),info.getUrl()});  }  //关闭db  public voID close(){    db.close();  }  //判断多线程任务下载 是否第一次创建线程  public boolean isExist(String url){    Cursor cursor=db.query(ThreadHelper.table_name,"url= ?",new String[]{url},null);    boolean isExist=cursor.movetoNext();    cursor.close();    return isExist;  }  public synchronized voID delete(ThreadInfo info){    long rowID=db.delete(ThreadHelper.table_name,"url =? and threadID= ?",new String[]{info.getUrl(),info.getThreadID()});    if(rowID!=-1){      UtilsLog.i("删除下载线程记录成功");    }else{      UtilsLog.i("删除下载线程记录失败");    }  }  public synchronized voID delete(String url){    long rowID=db.delete(ThreadHelper.table_name,"url =? ",new String[]{url});    if(rowID!=-1){      UtilsLog.i("删除下载线程记录成功");    }else{      UtilsLog.i("删除下载线程记录失败");    }  }}

4.基本的准备 *** 作我们已经完成了,那么开始写关于下载的类吧。首先写的肯定是DownLoadManager类,就是管理任务下载的类。不多说,直接看代码。

public class DownLoadManager {  private Map<String,fileInfo> map = new HashMap<>();  private static int mThreadNum;  private int fileSize;  private boolean flag = false; //true第一次下载 false不是第一次下载  private List<DownLoadTask> threads;  private static fileInfo mInfo;  private static ResultListener mListener;  public static ExecutorService executorService = Executors.newCachedThreadPool();  public static file file;  private int totalComleted;  private DownLoadManager() {    threads = new ArrayList<>();  }  public static DownLoadManager getInstance(fileInfo info,int threadNum,ResultListener Listener) {    mListener = Listener;    mThreadNum = threadNum;    mInfo = info;    return DownLoadManagerHolder.dlm;  }  private static class DownLoadManagerHolder {    private static final DownLoadManager dlm = new DownLoadManager();  }  public voID start() {    totalComleted=0;    clear();    final fileInfo newInfo = DownLoad.getInstance().queryData(mInfo.getUrl());    newInfo.setDownloading(true);    map.put(mInfo.getUrl(),newInfo);    prepare(newInfo);  }  //停止下载任务  public voID stop() {    map.get(mInfo.getUrl()).setDownloading(false);    map.get(mInfo.getUrl()).setStop(true);  }  public voID clear(){    if(threads.size()>0){      threads.clear();    }  }  //重新下载任务  public voID restart() {    stop();    try {      file file = new file(com.cmazxiaoma.downloader.download.DownLoadManager.file_PATH,map.get(mInfo.getUrl()).getfilename());      if (file.exists()) {        file.delete();      }      java.lang.Thread.sleep(100);    } catch (InterruptedException e) {      e.printstacktrace();    }    DownLoad.getInstance().resetData(mInfo.getUrl());    start();  }  //获取当前任务状态,是否在下载  public boolean getCurrentState() {    return map.get(mInfo.getUrl()).isDownloading();  }  //添加下载任务  public voID addTask(fileInfo info) {    //判断数据库是否已经存在此下载信息    if (!DownLoad.getInstance().isExist(info)) {      DownLoad.getInstance().insertData(info);      map.put(info.getUrl(),info);    } else {      DownLoad.getInstance().delete(info);      DownLoad.getInstance().insertData(info);      UtilsLog.i("map已经更新");      map.remove(info.getUrl());      map.put(info.getUrl(),info);    }  }  private voID prepare(final fileInfo newInfo) {    new java.lang.Thread(){      @OverrIDe      public voID run() {        httpURLConnection con = null;        RandomAccessfile raf=null;        try {          //连接资源          URL url = new URL(newInfo.getUrl());          UtilsLog.i("url=" + url);          con = (httpURLConnection) url.openConnection();          con.setConnectTimeout(2 * 1000);          con.setRequestMethod("GET");          int length = -1;          UtilsLog.i("responseCode=" + con.getResponseCode());          if (con.getResponseCode() == 200) {            length = con.getContentLength();            UtilsLog.i("文件大小=" + length);          }          if (length <= 0) {            return;          }          //创建文件保存路径          file dir = new file(com.cmazxiaoma.downloader.download.DownLoadManager.file_PATH);          if (!dir.exists()) {            dir.mkdirs();//建立多级文件夹          }          newInfo.setLength(length);          fileSize = length;          UtilsLog.i("当前线程ID=" + java.lang.Thread.currentThread().getID() + ",name=" + java.lang.Thread.currentThread().getname());          int currentPartSize = fileSize / mThreadNum;          file = new file(com.cmazxiaoma.downloader.download.DownLoadManager.file_PATH,newInfo.getfilename());          raf = new RandomAccessfile(file,"rwd");          raf.setLength(fileSize);          if (Thread.getInstance().isExist(newInfo.getUrl())) {            flag = false;          } else {            flag = true;          }          for (int i = 0; i < mThreadNum; i++) {            if (flag) {              UtilsLog.i("第一次多线程下载");              int start = i * currentPartSize;//计算每条线程下载的开始位置              int end = start + currentPartSize-1;//线程结束的位置              if(i==mThreadNum-1){                end=fileSize;              }              String threadID = "xiaoma" + i;              ThreadInfo threadInfo = new ThreadInfo(threadID,newInfo.getUrl());              Thread.getInstance().insert(threadInfo);              DownLoadTask thread = new DownLoadTask(threadInfo,newInfo,0);              DownLoadManager.executorService.execute(thread);              threads.add(thread);            } else {              UtilsLog.i("不是第一次多线程下载");              ThreadInfo threadInfo = Thread.getInstance().query("xiaoma" + i,newInfo.getUrl());              DownLoadTask thread = new DownLoadTask(threadInfo,threadInfo.getThreadID(),threadInfo.getStart(),threadInfo.getEnd(),threadInfo.getCompeleted());//这里出现过问题              DownLoadManager.executorService.execute(thread);              threads.add(thread);            }          }          boolean isCompleted=false;          while(!isCompleted){            isCompleted=true;            for(DownLoadTask thread:threads){              totalComleted+=thread.completed;              if(!thread.isCompleted){                isCompleted=false;              }            }            if(newInfo.isstop()){              totalComleted=0;              return;            }            Message message=new Message();            message.what=0x555;            message.arg1=fileSize;            message.arg2=totalComleted;            handler.sendMessage(message);            if(isCompleted){              totalComleted=0;              //任务线程全部完成,清空集合              clear();              handler.sendEmptyMessage(0x666);              return;            }            totalComleted=0;            java.lang.Thread.sleep(1000);          }        }catch (Exception e) {          e.printstacktrace();        }finally {          try {            if (con != null) {              con.disconnect();            }            if(raf!=null){              raf.close();            }          } catch (IOException e) {            e.printstacktrace();          }        }      }    }.start();  }  private Handler handler=new Handler(){    @OverrIDe    public voID handleMessage(Message msg) {      super.handleMessage(msg);      switch (msg.what){        case 0x555:          if(mListener!=null){            mListener.progress(msg.arg1,msg.arg2);          }          break;        case 0x666:          if(mListener!=null){            mListener.comleted();          }          break;      }    }  };}

5.接下来呢,就是DownLoadTask类了,就是一个线程下载类。

 public class DownLoadTask extends java.lang.Thread{  private int start;//当前线程的开始下载位置  private int end;//当前线程结束下载的位置  private RandomAccessfile raf;//当前线程负责下载的文件大小  public int completed=0;//当前线程已下载的字节数  private String threadID;//自己定义的线程ID  private fileInfo info;  private ThreadInfo threadInfo;  public boolean isCompleted=false; //true为当前线程完成任务,false为当前线程未完成任务  //保存新的start  public int finshed=0;  public int newStart=0;  public DownLoadTask(ThreadInfo threadInfo,fileInfo info,String threadID,int start,int end,int completed){    this.threadInfo=threadInfo;    this.info=info;    this.threadID=threadID;    this.start=start;    this.end=end;    this.completed=completed;  }  @OverrIDe  public voID run() {      httpURLConnection con = null;      try {        UtilsLog.i("start="+start+",end="+end+",completed="+completed+",threadID="+getThreadID());        URL url = new URL(info.getUrl());        con = (httpURLConnection) url.openConnection();        con.setConnectTimeout(2 * 1000);        con.setRequestMethod("GET");        con.setRequestProperty("Range","bytes=" + start + "-"+end);//重点        raf=new RandomAccessfile(DownLoadManager.file,"rwd");        //从文件的某一位置写入        raf.seek(start);        if (con.getResponseCode() == 206) { //文件部分下载 返回码是206          inputStream is = con.getinputStream();          byte[] buffer = new byte[4096];          int hasRead = 0;          while ((hasRead = is.read(buffer)) != -1) {            //写入文件            raf.write(buffer,hasRead);            //单个文件的完成程度            completed += hasRead;            threadInfo.setCompeleted(completed);            //保存新的start            finshed=finshed+hasRead;//这里出现过问题,嘻嘻            newStart=start+finshed;            threadInfo.setStart(newStart);            //UtilsLog.i("Thread:"+getThreadID()+",completed="   + completed);            //停止下载            if (info.isstop()) {              UtilsLog.i("isstop="+info.isstop());              //保存下载进度              UtilsLog.i("现在Thread:"+getThreadID()+",completed=" + completed);              Thread.getInstance().update(threadInfo);              return;            }          }          //删除该线程下载记录          Thread.getInstance().delete(threadInfo);          isCompleted=true;          Thread.getInstance().update(threadInfo);          UtilsLog.i("thread:"+getThreadID()+"已经完成任务!--"+"completed="+completed);        }      } catch (Exception e) {        if (con != null) {          con.disconnect();        }        try {          if (raf != null) {            raf.close();          }        } catch (IOException e1) {          e1.printstacktrace();        }      }    }  public String getThreadID() {    return threadID;  }}

6.接口,就是一个监听下载进度的接口,也是很简单。

 public interface ResultListener{  voID progress(int max,int progress);  voID comleted();}

结束

大致 *** 作就是这样,其实多线程也挺简单的。

以上所述是小编给大家介绍的AndroID多线程+单线程+断点续传+进度条显示下载功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程小技巧网站的支持!

总结

以上是内存溢出为你收集整理的Android多线程+单线程+断点续传+进度条显示下载功能全部内容,希望文章能够帮你解决Android多线程+单线程+断点续传+进度条显示下载功能所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1146142.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存