大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節(jié)點LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 多線程下載完整流程

多線程下載完整流程

更新時間:2020-11-25 17:47:39 來源:動力節(jié)點 瀏覽1507次

說到多線程下載,我們可以把線程理解為下載的通道,一個線程就是文件下載的一個通道,多線程就是同時打開了多個通道對文件進行下載。當(dāng)服務(wù)器提供下載服務(wù)時,用戶之間共享帶寬,在優(yōu)先級相同的情況下,總服務(wù)器會對總下載線程進行平均分配。我們平時用的許多下載器下都是多線程下載。本文我們就來看看多線程下載的完整流程。

 

1.入口DownLoadManager.download()

/**

 *

 * @param request 請求實體參數(shù)Entity

 * @param tag 下載地址

 * @param callBack 返回給調(diào)用的CollBack

 */

public void download(DownloadRequest request, String tag, CallBack callBack) {

    final String key = createKey(tag);

    if (check(key)) {

        // 請求的響應(yīng) 需要狀態(tài)傳遞類 以及對應(yīng)的回調(diào)

        DownloadResponse response = new DownloadResponseImpl(mDelivery, callBack);

        // 下載器 需要線程池 數(shù)據(jù)庫管理者 對應(yīng)的url key值 之后回調(diào)給自己

        Downloader downloader = new DownloaderImpl(request, response,

            mExecutorService, mDBManager, key, mConfig, this);

        mDownloaderMap.put(key, downloader);

        //開始下載

        downloader.start();

    }

}

DownloadResponseImpl下載響應(yīng)需要把本身的下載事件插入給調(diào)用者,由于下載是在子線程里面的,所以專門搞了一個下載狀態(tài)的傳遞類

DownLoaderImpl下載器需要的參數(shù)就比較多了,請求實體,對應(yīng)的下載響應(yīng),線程池,數(shù)據(jù)庫管理器,url的哈希值,對應(yīng)的配置,還有下載的一部分

加入進LinkedHashMap做一個有序的存儲

之后調(diào)用下載器的start方法。

 

2.開始下載開始

  @Override

  public void start() {

    //修改為Started狀態(tài)

    mStatus = DownloadStatus.STATUS_STARTED;

    //CallBack 回調(diào)給調(diào)用者

    mResponse.onStarted();

    // 連接獲取是否支持多線程下載

    connect();

    }

/**

 * 執(zhí)行連接任務(wù)

 */

private void connect() {

    mConnectTask = new ConnectTaskImpl(mRequest.getUri(), this);

    mExecutor.execute(mConnectTask);

}

在正式下載之前需要確定后臺是否支持?jǐn)帱c下載,所以才有先執(zhí)行這個ConnectTaskImpl連接任務(wù)。

 

3.ConnectTaskImpl連接任務(wù)

  @Override

  public void run() {

    // 設(shè)置為后臺線程

    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

    //修改連接中狀態(tài)

    mStatus = DownloadStatus.STATUS_CONNECTING;

    //回調(diào)給調(diào)用者

    mOnConnectListener.onConnecting();

    try {

      //執(zhí)行連接方法

      executeConnection();

    } catch (DownloadException e) {

      handleDownloadException(e);

    }

  }

 

  /**

   *

   * @throws DownloadException

   */

  private void executeConnection() throws DownloadException {

    mStartTime = System.currentTimeMillis();

    HttpURLConnection httpConnection = null;

    final URL url;

    try {

      url = new URL(mUri);

    } catch (MalformedURLException e) {

      throw new DownloadException(DownloadStatus.STATUS_FAILED, "Bad url.", e);

    }

    try {

      httpConnection = (HttpURLConnection) url.openConnection();

      httpConnection.setConnectTimeout(Constants.HTTP.CONNECT_TIME_OUT);

      httpConnection.setReadTimeout(Constants.HTTP.READ_TIME_OUT);

      httpConnection.setRequestMethod(Constants.HTTP.GET);

      httpConnection.setRequestProperty("Range", "bytes=" + 0 + "-");

      final int responseCode = httpConnection.getResponseCode();

      if (responseCode == HttpURLConnection.HTTP_OK) {

        //后臺不支持?jǐn)帱c下載,啟用單線程下載

        parseResponse(httpConnection, false);

      } else if (responseCode == HttpURLConnection.HTTP_PARTIAL) {

        //后臺支持?jǐn)帱c下載,啟用多線程下載

        parseResponse(httpConnection, true);

      } else {

        throw new DownloadException(DownloadStatus.STATUS_FAILED,

            "UnSupported response code:" + responseCode);

      }

    } catch (ProtocolException e) {

      throw new DownloadException(DownloadStatus.STATUS_FAILED, "Protocol error", e);

    } catch (IOException e) {

      throw new DownloadException(DownloadStatus.STATUS_FAILED, "IO error", e);

    } finally {

      if (httpConnection != null) {

        httpConnection.disconnect();

      }

    }

  }

 

  private void parseResponse(HttpURLConnection httpConnection, boolean isAcceptRanges)

      throws DownloadException {

 

    final long length;

    //header獲取length

    String contentLength = httpConnection.getHeaderField("Content-Length");

    if (TextUtils.isEmpty(contentLength) || contentLength.equals("0") || contentLength

        .equals("-1")) {

      //判斷后臺給你length,為null 0,-1,從連接中獲取

      length = httpConnection.getContentLength();

    } else {

      //直接轉(zhuǎn)化

      length = Long.parseLong(contentLength);

    }

 

    if (length <= 0) {

      //拋出異常數(shù)據(jù)

      throw new DownloadException(DownloadStatus.STATUS_FAILED, "length <= 0");

    }

    //判斷是否取消和暫停

    checkCanceledOrPaused();

 

    //Successful

    mStatus = DownloadStatus.STATUS_CONNECTED;

    //獲取時間差

    final long timeDelta = System.currentTimeMillis() - mStartTime;

    //回調(diào)給調(diào)用者

    mOnConnectListener.onConnected(timeDelta, length, isAcceptRanges);

  }

 

  private void checkCanceledOrPaused() throws DownloadException {

    if (isCanceled()) {

      // cancel

      throw new DownloadException(DownloadStatus.STATUS_CANCELED, "Connection Canceled!");

    } else if (isPaused()) {

      // paused

      throw new DownloadException(DownloadStatus.STATUS_PAUSED, "Connection Paused!");

    }

  }

 

  //統(tǒng)一執(zhí)行對應(yīng)的異常信息

  private void handleDownloadException(DownloadException e) {

    switch (e.getErrorCode()) {

      case DownloadStatus.STATUS_FAILED:

        synchronized (mOnConnectListener) {

          mStatus = DownloadStatus.STATUS_FAILED;

          mOnConnectListener.onConnectFailed(e);

        }

        break;

      case DownloadStatus.STATUS_PAUSED:

        synchronized (mOnConnectListener) {

          mStatus = DownloadStatus.STATUS_PAUSED;

          mOnConnectListener.onConnectPaused();

        }

        break;

      case DownloadStatus.STATUS_CANCELED:

        synchronized (mOnConnectListener) {

          mStatus = DownloadStatus.STATUS_CANCELED;

          mOnConnectListener.onConnectCanceled();

        }

        break;

      default:

        throw new IllegalArgumentException("Unknown state");

    }

  }

HttpURLConnection.HTTP_OK不支持?jǐn)帱c下載使用單線程下載

HttpURLConnection.HTTP_PARTIAL支持?jǐn)帱c下載使用多線程下載

如果成功就會發(fā)生到OnConnectListener.onConnected(timeDelta,length,isAcceptRanges)方法中。

 

4.查看下載器的onConnected()

@Override

public void onConnected(long time, long length, boolean isAcceptRanges) {

    if (mConnectTask.isCanceled()) {

        //連接取消

        onConnectCanceled();

    } else {

        mStatus = DownloadStatus.STATUS_CONNECTED;

        //回調(diào)給你響應(yīng)連接成功狀態(tài)

        mResponse.onConnected(time, length, isAcceptRanges);

        mDownloadInfo.setAcceptRanges(isAcceptRanges);

        mDownloadInfo.setLength(length);

        //真正開始下載

        download(length, isAcceptRanges);

    }

}

 

@Override

public void onConnectCanceled() {

    deleteFromDB();

    deleteFile();

    mStatus = DownloadStatus.STATUS_CANCELED;

    mResponse.onConnectCanceled();

    onDestroy();

}

 

@Override

public void onDestroy() {

    // trigger the onDestroy callback tell download manager

    mListener.onDestroyed(mTag, this);

}

根據(jù)狀態(tài)來處理,isCanceled()刪除數(shù)據(jù)庫里面的數(shù)據(jù),刪除文件,更改為取消狀態(tài)狀態(tài)

未取消,進去下載。

 

5.下載文件下載方法

/**

 * 下載開始

 * @param length 設(shè)置下載的長度

 * @param acceptRanges 是否支持?jǐn)帱c下載

 */

private void download(long length, boolean acceptRanges) {

    mStatus = DownloadStatus.STATUS_PROGRESS;

    initDownloadTasks(length, acceptRanges);

    //開始下載任務(wù)

    for (DownloadTask downloadTask : mDownloadTasks) {

        mExecutor.execute(downloadTask);

    }

}

 

/**

 * 初始化下載任務(wù)

 * @param length

 * @param acceptRanges

 */

private void initDownloadTasks(long length, boolean acceptRanges) {

    mDownloadTasks.clear();

    if (acceptRanges) {

        List<ThreadInfo> threadInfos = getMultiThreadInfos(length);

        // init finished

        int finished = 0;

        for (ThreadInfo threadInfo : threadInfos) {

            finished += threadInfo.getFinished();

        }

        mDownloadInfo.setFinished(finished);

        for (ThreadInfo info : threadInfos) {

            //開始多線程下載

            mDownloadTasks.add(new MultiDownloadTask(mDownloadInfo, info, mDBManager, this));

        }

    } else {

        //單線程下載不需要保存進度信息

        ThreadInfo info = getSingleThreadInfo();

        mDownloadTasks.add(new SingleDownloadTask(mDownloadInfo, info, this));

    }

}

 

//TODO

private List<ThreadInfo> getMultiThreadInfos(long length) {

    // init threadInfo from db

    final List<ThreadInfo> threadInfos = mDBManager.getThreadInfos(mTag);

    if (threadInfos.isEmpty()) {

        final int threadNum = mConfig.getThreadNum();

        for (int i = 0; i < threadNum; i++) {

            // calculate average

            final long average = length / threadNum;

            final long start = average * i;

            final long end;

            if (i == threadNum - 1) {

                end = length;

            } else {

                end = start + average - 1;

            }

            ThreadInfo threadInfo = new ThreadInfo(i, mTag, mRequest.getUri(), start, end, 0);

            threadInfos.add(threadInfo);

        }

    }

    return threadInfos;

}

 

//單線程數(shù)據(jù)

private ThreadInfo getSingleThreadInfo() {

    ThreadInfo threadInfo = new ThreadInfo(0, mTag, mRequest.getUri(), 0);

    return threadInfo;

}

根據(jù)已連接返回的數(shù)據(jù)判斷是否支持?jǐn)帱c下載,支持acceptRanges就調(diào)用getMultiThreadInfos來組裝多線程下載數(shù)據(jù),多線程需要初始化下載的進度信息,二單線程getSingleThreadInfo自己組裝一個簡單的就可以可以了。

 

6.執(zhí)行DownloadTaskImpl

@Override

public void run() {

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

// 插入數(shù)據(jù)庫

insertIntoDB(mThreadInfo);

try {

  mStatus = DownloadStatus.STATUS_PROGRESS;

  executeDownload();

  //根據(jù)回調(diào)對象,加鎖

  synchronized (mOnDownloadListener) {

    //沒出異常就代表下載完成了

    mStatus = DownloadStatus.STATUS_COMPLETED;

    mOnDownloadListener.onDownloadCompleted();

  }

} catch (DownloadException e) {

  handleDownloadException(e);

}

}

 

/**

   * 開始下載數(shù)據(jù)

   */

   private void executeDownload() throws DownloadException {

    final URL url;

    try {

      url = new URL(mThreadInfo.getUri());

    } catch (MalformedURLException e) {

      throw new DownloadException(DownloadStatus.STATUS_FAILED, "Bad url.", e);

    }

 

    HttpURLConnection httpConnection = null;

    try {

      //設(shè)置http連接信息

      httpConnection = (HttpURLConnection) url.openConnection();

      httpConnection.setConnectTimeout(HTTP.CONNECT_TIME_OUT);

      httpConnection.setReadTimeout(HTTP.READ_TIME_OUT);

      httpConnection.setRequestMethod(HTTP.GET);

      //設(shè)置header數(shù)據(jù),斷點下載設(shè)置關(guān)鍵

      setHttpHeader(getHttpHeaders(mThreadInfo), httpConnection);

      final int responseCode = httpConnection.getResponseCode();

      if (responseCode == getResponseCode()) {

        //下載數(shù)據(jù)

        transferData(httpConnection);

      } else {

        throw new DownloadException(DownloadStatus.STATUS_FAILED,

            "UnSupported response code:" + responseCode);

      }

    } catch (ProtocolException e) {

      throw new DownloadException(DownloadStatus.STATUS_FAILED, "Protocol error", e);

    } catch (IOException e) {

      throw new DownloadException(DownloadStatus.STATUS_FAILED, "IO error", e);

    } finally {

      if (httpConnection != null) {

        httpConnection.disconnect();

      }

    }

  }

 

  /**

   * 設(shè)置header數(shù)據(jù)

   *

   * @param headers header元數(shù)據(jù)

   */

  private void setHttpHeader(Map<String, String> headers, URLConnection connection) {

    if (headers != null) {

      for (String key : headers.keySet()) {

        connection.setRequestProperty(key, headers.get(key));

      }

    }

  }

 

  /**

   * 下載數(shù)據(jù)

   */

  private void transferData(HttpURLConnection httpConnection) throws DownloadException {

    InputStream inputStream = null;

    RandomAccessFile raf = null;

    try {

      try {

        inputStream = httpConnection.getInputStream();

      } catch (IOException e) {

        throw new DownloadException(DownloadStatus.STATUS_FAILED, "http get inputStream error", e);

      }

  //獲取下載的偏移量

      final long offset = mThreadInfo.getStart() + mThreadInfo.getFinished();

      try {

//設(shè)置偏移量

        raf = getFile(mDownloadInfo.getDir(), mDownloadInfo.getName(), offset);

      } catch (IOException e) {

        throw new DownloadException(DownloadStatus.STATUS_FAILED, "File error", e);

      }

      //開始寫入數(shù)據(jù)

      transferData(inputStream, raf);

    } finally {

      try {

        IOCloseUtils.close(inputStream);

        IOCloseUtils.close(raf);

      } catch (IOException e) {

        e.printStackTrace();

      }

    }

  }

 

  /**

   * 寫入數(shù)據(jù)

   */

  private void transferData(InputStream inputStream, RandomAccessFile raf)

      throws DownloadException {

    final byte[] buffer = new byte[1024 * 8];

    while (true) {

      checkPausedOrCanceled();

      int len = -1;

      try {

        len = inputStream.read(buffer);

        if (len == -1) {

          break;

        }

        raf.write(buffer, 0, len);

        //設(shè)置下載的信息

        mThreadInfo.setFinished(mThreadInfo.getFinished() + len);

        synchronized (mOnDownloadListener) {

          mDownloadInfo.setFinished(mDownloadInfo.getFinished() + len);

          //回調(diào)進度

          mOnDownloadListener

              .onDownloadProgress(mDownloadInfo.getFinished(), mDownloadInfo.getLength());

        }

      } catch (IOException e) {

        //更新數(shù)據(jù)庫

        updateDB(mThreadInfo);

        throw new DownloadException(DownloadStatus.STATUS_FAILED, e);

      }

    }

  }

斷點下載的關(guān)鍵是在header頭信息里面添加了已經(jīng)下載的長度,下載數(shù)據(jù)也是從下載的長度點開始寫入數(shù)據(jù),寫入數(shù)據(jù),每個線程在對應(yīng)的片段里面下載對應(yīng)的數(shù)據(jù),后續(xù)使用RandomAccessFile組裝起來,合成一個文件。

 

以上就是整個多線程下載的完整流程,多線程下載的優(yōu)勢也是一覽無余。在同等的網(wǎng)絡(luò)傳輸速度下,多線程下載還是要比單線程下載更加高效的。本站的Java多線程教程中對此有詳細的講解,希望喜歡刨根問底的小伙伴可以找到自己滿意的答案。


提交申請后,顧問老師會電話與您溝通安排學(xué)習(xí)

免費課程推薦 >>
技術(shù)文檔推薦 >>
主站蜘蛛池模板: 国产成人aa在线观看视频 | 日韩字幕一中文在线综合 | 精品一区二区三区18 | 日本免费一区二区三区看片 | 91久久精品国产一区二区 | 国产网红在线视频 | 欧美白人猛性xxxxx交69 | 国产福利资源 | 高清国产一区二区 | 色综合婷婷在线 | 午夜dj影院在线视频观看完整 | 久久网免费视频 | 看全色黄大色黄大片爽一下 | 国产福利小视频在线观看 | 久久久久一区二区三区 | 激情综合五月亚洲婷婷 | 日韩a级片 | 天天干视频 | 久久99爱视频 | 日本高清不卡码 | 一本久久a久久精品vr综合 | 性欧美网站 | 久久精品国产一区二区三区 | 思99re久久这里只有精品首页 | 日韩国产欧美精品综合二区 | 老司机午夜视频在线观看 | 西西做人爱免费视频 | 九九热精品视频 | 亚洲国产99在线精品一区二区 | 久久综合欧美 | 国产亚洲欧美ai在线看片 | 亚洲午夜久久影院 | 久草手机在线观看 | 在线中文字幕亚洲 | 性做爰片视频毛片 | 草草福利影院 | 精品国产一区二区三区久久影院 | 97久久国产一区二区三区四区 | 日韩亚洲欧美性感视频影片免费看 | 热re久久精品国产99热 | 四虎影视永久免费观看地址 |