国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android多線程+單線程+斷點續傳+進度條顯示下載功能

2019-10-23 18:27:29
字體:
來源:轉載
供稿:網友

效果圖

android,多線程斷點續傳,單線程斷點續傳,進度條下載

白話分析:

多線程:肯定是多個線程咯

斷點:線程停止下載的位置

續傳:線程從停止下載的位置上繼續下載,直到完成任務為止。

核心分析:

斷點:

當前線程已經下載的數據長度

續傳:

向服務器請求上次線程停止下載位置的數據

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,url,length,finised,isStop,isDownloading這些屬性。線程的實體類ThreadInfo肯定有threadId,start,end,completed,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,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 +        '}';  }}

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, threadId, start , end, completed, 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,null,values);    if(rowId!=-1){      UtilsLog.i("插入線程記錄成功");    }else{      UtilsLog.i("插入線程記錄失敗");    }  }  //查詢當前線程 下載的進度  public synchronized ThreadInfo query(String threadId,String queryUrl){    Cursor cursor=db.query(ThreadHelper.TABLE_NAME,null,"threadId= ? and url= ?",new String[]{threadId,queryUrl},null,null,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,"threadId= ? and url= ?",new String[]{info.getThreadId(),info.getUrl()});  }  //關閉db  public void close(){    db.close();  }  //判斷多線程任務下載 是否第一次創建線程  public boolean isExist(String url){    Cursor cursor=db.query(ThreadHelper.TABLE_NAME,null,"url= ?",new String[]{url},null,null,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, start, end, 0,newInfo.getUrl());              Thread.getInstance().insert(threadInfo);              DownLoadTask thread = new DownLoadTask(threadInfo,newInfo, threadId, start, end, 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,newInfo,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, 0, 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多線程+單線程+斷點續傳+進度條顯示下載功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VEVB武林網網站的支持!


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 横峰县| 花莲市| 满洲里市| 和林格尔县| 精河县| 花莲市| 沙田区| 霍林郭勒市| 青田县| 安龙县| 晋中市| 九龙城区| 喜德县| 响水县| 屯门区| 锦屏县| 新巴尔虎右旗| 开平市| 肃宁县| 南召县| 鄄城县| 乐业县| 自贡市| 宜宾市| 图木舒克市| 温宿县| 永和县| 博客| 景宁| 西丰县| 石泉县| 古丈县| 共和县| 九江市| 敦化市| 吴旗县| 晋州市| 高台县| 东城区| 黑山县| 八宿县|