欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android多線程+單線程+斷點(diǎn)續(xù)傳+進(jìn)度條顯示下載功能

 更新時(shí)間:2017年06月01日 14:34:54   作者:cmazxiaoma  
這篇文章主要介紹了Android多線程+單線程+斷點(diǎn)續(xù)傳+進(jìn)度條顯示下載功能,需要的朋友可以參考下

效果圖

download.gif

白話分析:

多線程:肯定是多個(gè)線程咯

斷點(diǎn):線程停止下載的位置

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

核心分析:

斷點(diǎn):

當(dāng)前線程已經(jīng)下載的數(shù)據(jù)長度

續(xù)傳:

向服務(wù)器請(qǐng)求上次線程停止下載位置的數(shù)據(jù)

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

分配線程:

 int currentPartSize = fileSize / mThreadNum;

定義位置

定義線程開始下載的位置和結(jié)束的位置

for (int i = 0; i < mThreadNum; i++) {
int start = i * currentPartSize;//計(jì)算每條線程下載的開始位置
 int end = start + currentPartSize-1;//線程結(jié)束的位置
 if(i==mThreadNum-1){
   end=fileSize;
  }}

創(chuàng)建數(shù)據(jù)庫:

由于每一個(gè)文件要分成多個(gè)部分,要被不同的線程同時(shí)進(jìn)行下載。當(dāng)然要?jiǎng)?chuàng)建線程表,保存當(dāng)前線程下載開始的位置和結(jié)束的位置,還有完成進(jìn)度等。創(chuàng)建file表,保存當(dāng)前下載的文件信息,比如:文件名,url,下載進(jìn)度等信息
線程表:

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)";

創(chuàng)建線程類

無非就2個(gè)類,一個(gè)是線程管理類DownLoadManager.Java,核心方法:start(),stop(),restart(),addTask().clear()。另一個(gè)是線程任務(wù)類

DownLoadTask.java,就是一個(gè)線程類,用于下載線程分配好的任務(wù)。后面會(huì)貼出具體代碼。

創(chuàng)建數(shù)據(jù)庫方法類

無非就是單例模式,封裝一些增刪改查等基礎(chǔ)數(shù)據(jù)庫方法,后面會(huì)貼出具體代碼。

創(chuàng)建實(shí)體類

也就是創(chuàng)建ThreadInfo和FileInfo這2個(gè)實(shí)體類,把下載文件信息和線程信息暫時(shí)存儲(chǔ)起來。

引入的第三方開源庫

NumberProgressBar是一個(gè)關(guān)于進(jìn)度條的開源庫,挺不錯(cuò)的。直達(dá)鏈接

代碼具體分析

1.首先是創(chuàng)建實(shí)體類,文件的實(shí)體類FileInfo,肯定有fileName,url,length,finised,isStop,isDownloading這些屬性。線程的實(shí)體類ThreadInfo肯定有threadId,start,end,completed,url這些屬性。這些都很簡單

 //ThredInfo.java
  public class FileInfo {
  private String fileName; //文件名
  private String url; //下載地址
  private int length; //文件大小
  private int finished; //下載已完成進(jìn)度
  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; //下載已完成進(jìn)度
  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.實(shí)體類寫完了,那么接下來寫創(chuàng)建一個(gè)類,繼承SQLiteOpenHelper類,來管理數(shù)據(jù)庫連接,主要作用:管理數(shù)據(jù)庫的初始化,并允許應(yīng)用程序通過該類獲取SQLiteDatabase對(duì)象。

 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.接下來封裝一些數(shù)據(jù)庫的增刪改查操作,用的單例模式,用雙重檢驗(yàn)鎖實(shí)現(xiàn)單例。好處:既能很大程度上確保線程安全,又能實(shí)現(xiàn)延遲加載。 缺點(diǎn):使用volatile關(guān)鍵字會(huì)使JVM對(duì)該代碼的優(yōu)化喪失,影響性能。并且在一些高并發(fā)的情況,仍然可能會(huì)創(chuàng)建多個(gè)實(shí)例,這稱為雙重檢驗(yàn)鎖定失效。單例模式

 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;
  }
  //保存當(dāng)前線程下載進(jìn)度
  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("插入線程記錄失敗");
    }
  }
  //查詢當(dāng)前線程 下載的進(jìn)度
  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;
  }
  //更新當(dāng)前線程下載進(jìn)度
  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()});
  }
  //關(guān)閉db
  public void close(){
    db.close();
  }
  //判斷多線程任務(wù)下載 是否第一次創(chuàng)建線程
  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.基本的準(zhǔn)備操作我們已經(jīng)完成了,那么開始寫關(guān)于下載的類吧。首先寫的肯定是DownLoadManager類,就是管理任務(wù)下載的類。不多說,直接看代碼。

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);
  }
  //停止下載任務(wù)
  public void stop() {
    map.get(mInfo.getUrl()).setDownloading(false);
    map.get(mInfo.getUrl()).setStop(true);
  }
  public void clear(){
    if(threads.size()>0){
      threads.clear();
    }
  }
  //重新下載任務(wù)
  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();
  }
  //獲取當(dāng)前任務(wù)狀態(tài), 是否在下載
  public boolean getCurrentState() {
    return map.get(mInfo.getUrl()).isDownloading();
  }
  //添加下載任務(wù)
  public void addTask(FileInfo info) {
    //判斷數(shù)據(jù)庫是否已經(jīng)存在此下載信息
    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已經(jīng)更新");
      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;
          }
          //創(chuàng)建文件保存路徑
          File dir = new File(com.cmazxiaoma.downloader.download.DownLoadManager.FILE_PATH);
          if (!dir.exists()) {
            dir.mkdirs();//建立多級(jí)文件夾
          }
          newInfo.setLength(length);
          fileSize = length;
          UtilsLog.i("當(dāng)前線程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;//計(jì)算每條線程下載的開始位置
              int end = start + currentPartSize-1;//線程結(jié)束的位置
              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());//這里出現(xiàn)過問題
              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;
              //任務(wù)線程全部完成,清空集合
              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類了,就是一個(gè)線程下載類。

 public class DownLoadTask extends java.lang.Thread{
  private int start;//當(dāng)前線程的開始下載位置
  private int end;//當(dāng)前線程結(jié)束下載的位置
  private RandomAccessFile raf;//當(dāng)前線程負(fù)責(zé)下載的文件大小
  public int completed=0;//當(dāng)前線程已下載的字節(jié)數(shù)
  private String threadId;//自己定義的線程Id
  private FileInfo info;
  private ThreadInfo threadInfo;
  public boolean isCompleted=false; //true為當(dāng)前線程完成任務(wù),false為當(dāng)前線程未完成任務(wù)
  //保存新的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);//重點(diǎn)
        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);
            //單個(gè)文件的完成程度
            completed += hasRead;
            threadInfo.setCompeleted(completed);
            //保存新的start
            finshed=finshed+hasRead;//這里出現(xiàn)過問題,嘻嘻
            newStart=start+finshed;
            threadInfo.setStart(newStart);
            //UtilsLog.i("Thread:"+getThreadId()+",completed="   + completed);
            //停止下載
            if (info.isStop()) {
              UtilsLog.i("isStop="+info.isStop());
              //保存下載進(jìn)度
              UtilsLog.i("現(xiàn)在Thread:"+getThreadId()+",completed=" + completed);
              Thread.getInstance().update(threadInfo);
              return;
            }
          }
          //刪除該線程下載記錄
          Thread.getInstance().delete(threadInfo);
          isCompleted=true;
          Thread.getInstance().update(threadInfo);
          UtilsLog.i("thread:"+getThreadId()+"已經(jīng)完成任務(wù)!--"+"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.接口,就是一個(gè)監(jiān)聽下載進(jìn)度的接口,也是很簡單。

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

結(jié)束

大致操作就是這樣,其實(shí)多線程也挺簡單的。

以上所述是小編給大家介紹的Android多線程+單線程+斷點(diǎn)續(xù)傳+進(jìn)度條顯示下載功能,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

最新評(píng)論