1. 程式人生 > >Android 基於OkHttp的下載,支援https,斷點下載,優化下載速度

Android 基於OkHttp的下載,支援https,斷點下載,優化下載速度

package com.lenovo.smarthcc.http.okhttp;

import android.text.TextUtils;

import com.lenovo.smarthcc.http.Listenner;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import okhttp3.Call;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okio.Buffer;
import okio.Okio;
import okio.Sink;
import okio.Source;

/**
 * <p>Author:    shijiale</p>
 * <p>Date:      2017/3/14.<p>

 * <p>Describe:
 */

public class BRDownloadThread extends Thread {

    private static OkHttpClient mClient;                 //okhttp client
    private Builder mBuilder;                            //配置
    private boolean isExit = false;                      //是否退出下載
    private long mStartTime = 0;                         //當前這次下載起始時間
    private long mDownloadLength;                        //當前這次下載的長度
    private final int MAX_BUFF_SIZE = 4 * 1024;          //每次傳送的位元組(這個數值可能沒有什麼作用,因為大部分http流都會做快取)
    private Call mCall;                                  //當前請求
    private DaemonThead mDaemonMsgThread;                //用於進度回撥的執行緒

    /***
     * 退出當前下載執行緒
     */
    public synchronized void exit() {
        isExit = true;
    }


    private BRDownloadThread() {
        mClient = OkHttpProxy.getClient();
        if (mDaemonMsgThread == null) {
            mDaemonMsgThread = new DaemonThead();
            mDaemonMsgThread.isRunning = true;
        }
    }

    @Override
    public void run() {
        super.run();
        try {
            downloadFile();
        } catch (Exception e) {
            mBuilder.mErrorListenner.onChanged(Listenner.STATUS_ERROR, mBuilder.mSavePath, e.getMessage());
        } finally {
            if (mDaemonMsgThread != null) {
                mDaemonMsgThread.isRunning = false;
                mDaemonMsgThread = null;
            }
        }
    }

    /***
     * 自動修改下載的檔名稱
     * @param fileLength 檔案在伺服器的大小
     */
    private void autoChangeFileName(long fileLength) {
        File file = new File(mBuilder.mSavePath);
        if(file != null && file.exists() && file.length() == fileLength) {
            String path = mBuilder.mSavePath;
            path = path.substring(0,path.lastIndexOf(".") + 1);

            String expresion = mBuilder.mSavePath.substring(path.lastIndexOf(".") + 1,mBuilder.mSavePath.length());
            path += "(" + new Date(System.currentTimeMillis()).toLocaleString() + ")" + expresion;
            mBuilder.mSavePath = path;
        }
    }

    private boolean chekDirectory() {
        String path = mBuilder.mSavePath;
        path = path.substring(0,path.lastIndexOf(File.separator) + 1);
        File file = new File(path);
        if(file != null && file.exists()) {
            return true;
        }
        if(file == null || !file.exists()) {
            boolean mkdirs = file.mkdirs();
            if(!mkdirs) {
                return false;
            }
        }
        return true;
    }

    /***
     * 下載檔案
     * @throws IOException
     */
    private void downloadFile() throws IOException {

        if(!chekDirectory()) {
            mBuilder.mErrorListenner.onChanged(Listenner.STATUS_ERROR,mBuilder.mSavePath,"directory create failed!");
            return;
        }
        //獲取檔案長度
        mBuilder.mFileAllLength = getNetFileSize();
        //如果允許重複下載,則對檔案進行重新命名,修改後的檔名會會在 onStop onSuccess中體現
        if(mBuilder.isAutoRename) {
            autoChangeFileName(mBuilder.mFileAllLength);
        }
        RandomAccessFile file = new RandomAccessFile(mBuilder.mSavePath, "rw");
        //如果本地已經存在該檔案,並且大小和伺服器相同則返回下載成功
        if (file != null && file.length() == mBuilder.mFileAllLength && mBuilder.mFileAllLength != 0) {
            mBuilder.mSuccessListenner.onChanged(Listenner.STATUS_SUCCESS, mBuilder.mSavePath,mBuilder.mFileAllLength + "");
            return;
        }
        //設定檔案起始指標
        if (file != null && file.length() != 0) {
            if (mBuilder.mStartOffset > 0 && mBuilder.mStartOffset < file.length()) {
                file.seek(mBuilder.mStartOffset);
            } else {
                file.seek(file.length() - 1);
                mBuilder.mStartOffset = file.length() - 1;
            }
        }
        //設定請求引數
        callRequest(mBuilder.mStartOffset);
        if (mCall == null) {
            mBuilder.mErrorListenner.onChanged(Listenner.STATUS_ERROR, mBuilder.mSavePath, "CONNECTION ERROR!");
            return;
        }
        //建立連線
        Response res = mCall.execute();
        if (!res.isSuccessful()) {
            mBuilder.mErrorListenner.onChanged(Listenner.STATUS_ERROR, mBuilder.mSavePath, "CONNECTION ERROR!");
            return;
        }
        //更新檔案長度
        mBuilder.mLengthChangeListenner.onChanged(Listenner.STATUS_LENGTH_CHANGE, mBuilder.mFileAllLength + "", (mBuilder.mStartOffset + mDownloadLength) + "");
        //開啟進度重新整理執行緒
        mDaemonMsgThread.start();
        //儲存檔案
        boolean ret = saveData(res.body().byteStream(), new FileOutputStream(file.getFD()));
        if (ret) {
            mBuilder.mSuccessListenner.onChanged(Listenner.STATUS_SUCCESS, mBuilder.mSavePath,mBuilder.mFileAllLength + "");
        } else {
            mBuilder.mStopListenner.onChanged(Listenner.STATUS_STOP, mBuilder.mSavePath, mBuilder.mFileAllLength + "", (mBuilder.mStartOffset + mDownloadLength) + "");
        }
        mBuilder.mLengthChangeListenner.onChanged(Listenner.STATUS_LENGTH_CHANGE, mBuilder.mFileAllLength + "", (mBuilder.mStartOffset + mDownloadLength) + "");
    }

    /***
     * 獲取檔案在伺服器的長度,head請求
     * @return
     * @throws IOException
     */
    private long getNetFileSize() throws IOException {
        Request request = getRequestBuilder(0)
                .head()
                .build();
        Call call = mClient.newCall(request);
        if (call == null) {
            mBuilder.mErrorListenner.onChanged(Listenner.STATUS_ERROR, mBuilder.mSavePath, "CONNECTION ERROR,Get File Size!");
            return -1;
        }

        Response response = call.execute();
        if (!response.isSuccessful()) {
            mBuilder.mErrorListenner.onChanged(Listenner.STATUS_ERROR, mBuilder.mSavePath, "CONNECTION ERROR,Get File Size!");
            return -1;
        }

        Headers headers = response.headers();
        long fileSize = 0;
        try {
            fileSize = Long.parseLong(headers.get("Content-Length"));
        } catch (Exception e) {
            fileSize = -1;
        }
        return fileSize;
    }

    /***
     * 執行請求
     * @param offset 起始下載偏移量,該偏移量以當前已經下載的檔案長度為準
     */
    private void callRequest(long offset) {
        Request request = getRequestBuilder(offset)
                .build();
        mCall = mClient.newCall(request);
    }

    /***
     * 初始化請求引數
     * @param offset 起始下載偏移量
     * @return
     */
    private Request.Builder getRequestBuilder(long offset) {
        Headers.Builder builder = new Headers.Builder();
        //header
        if (mBuilder.mHeaders != null) {
            for (String k : mBuilder.mHeaders.keySet()) {
                builder.set(k, mBuilder.mHeaders.get(k));
            }
        }
        if (builder.get("Range") == null) {
            builder.set("Range", "bytes=" + offset + "-");
        }
        Headers headers = builder.build();
        //urlparams url後面的引數
        if (mBuilder.mUrlParams != null && !mBuilder.mUrlParams.isEmpty()) {
            if (!mBuilder.mUrl.contains("?")) {
                mBuilder.mUrl += "?";
            }
            for (String k : mBuilder.mUrlParams.keySet()) {
                mBuilder.mUrl += k + "=" + mBuilder.mUrlParams.get(k);
                mBuilder.mUrl += "&";
            }
        }
        Request.Builder reBuilder = new Request.Builder()
                .url(mBuilder.mUrl)
                .headers(headers);
        return reBuilder;
    }


    /***
     * 儲存檔案
     * @param is 源輸入流
     * @param os 目標輸出流
     * @return 如果是下載完成返回true,如果停止導致返回false
     * @throws IOException
     */
    private boolean saveData(InputStream is, OutputStream os) throws IOException {
        Source source = Okio.source(is);
        Sink sink = Okio.sink(os);
        Buffer buf = new Buffer();
        long len = 0;

        while ((len = source.read(buf, MAX_BUFF_SIZE)) != -1 && !isExit) {
            sink.write(buf, len);
            mDownloadLength += len;
        }

        sink.flush();
        sink.close();
        source.close();
        return !isExit;
    }

    /***
     * 重新整理當前下載進度
     */
    private synchronized void changeSpeed() {
        long endTime = System.currentTimeMillis();
        mBuilder.mSpeedChangeListenner.onChanged(Listenner.STATUS_SPEED_CHANGE, mBuilder.mFileAllLength + "", (mBuilder.mStartOffset + mDownloadLength) + "",(endTime - mStartTime) + "", mDownloadLength + "");
    }

    /**
     * 用於在一個新的執行緒中重新整理下載進度,防止回撥中有耗時操作阻塞下載執行緒影響下載速度
     */
    private final class DaemonThead extends Thread {
        //是否開啟
        private boolean isRunning = true;

        @Override
        public void run() {
            while (isRunning) {
                //防止下載完成以及其他狀態回撥時,進度還在重新整理
                synchronized (mBuilder) {
                    changeSpeed();
                    try {
                        Thread.sleep(mBuilder.mSpeedRefreshHZ);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /***
     * Listtenner 的簡單封裝,可以簡單的得到狀態回撥中的數值
     */
    public static abstract class SimpleDownLoadListenner implements Listenner {

        @Override
        public void onChanged(int code, String... value) {
            switch (code) {
                case Listenner.STATUS_ERROR: {
                    onDownloadError(value[0], value[1]);
                }
                break;
                case Listenner.STATUS_SUCCESS: {
                    onDownloadSuccessful(value[0],getLongValue(value[1]));
                }
                break;
                case Listenner.STATUS_SPEED_CHANGE: {
                    onDownloadSpeedChanged(getLongValue(value[0]), getLongValue(value[1]), getLongValue(value[2]),getLongValue(value[3]));
                }
                break;
                case Listenner.STATUS_STOP: {
                    onDownloadStoped(value[0], getLongValue(value[1]), getLongValue(value[2]));
                }
                break;
                case Listenner.STATUS_LENGTH_CHANGE: {
                    onDownloadLengthChanged(getLongValue(value[0]), getLongValue(value[1]));
                }
                break;
            }
        }

        private long getLongValue(String value) {
            try {
                return Long.parseLong(value);
            } catch (Exception e) {
                return 0;
            }
        }

        public void onDownloadSuccessful(String path,long allFileLength) {

        }

        public void onDownloadStoped(String path, long allLength, long allDownloadlength) {

        }

        public void onDownloadError(String path, String msg) {

        }

        public void onDownloadLengthChanged(long allLength, long allDownloadLength) {

        }

        public synchronized void onDownloadSpeedChanged(long allLength,long allDownloadLenght , long times, long nowDownloadLength) {

        }
    }

    /***
     * BRDownloadThread以Builder模式建立,不允許new
     */
    public static class Builder implements Listenner {
        private Map<String, String> mUrlParams;        //url後的引數
        private Map<String, String> mHeaders;          //請求頭
        private long mFileAllLength;                   //檔案總長度
        private long mStartOffset = 0;                 //起始下載偏移量
        private String mUrl;                           //請求地址
        private String mSavePath;                      //儲存路徑(全路徑)
        private String mBasePath;                      //儲存資料夾名稱
        private String mSaveName;                      //檔名稱
        private Listenner mSuccessListenner;           //成功回撥
        private Listenner mErrorListenner;             //失敗回撥
        private Listenner mLengthChangeListenner;      //檔案長度變化回撥(只在獲取到伺服器檔案大小時回撥,以及執行緒終止後回撥(包括完成和停止))
        private Listenner mSpeedChangeListenner;       //進度重新整理 重新整理頻率為(1/mSpeedRefreshHZ)
        private Listenner mStopListenner;              //停止回撥,只在下載手動終止時回撥
        private long mSpeedRefreshHZ = 500;            //進度重新整理間隔 毫秒
        private boolean isAutoRename = false;          //是否支援檔案重新命名,如果本地已存在 false 不再重複下載 true 重新命名後繼續下載

        public Builder setAutoRename(boolean auto) {
            isAutoRename = auto;
            return this;
        }

        /***
         * 設定進度重新整理間隔
         * @param times 間隔時間 ms
         * @return
         */
        public Builder setDaemonRefreshDelayTime(long times) {
            mSpeedRefreshHZ = times;
            return this;
        }

        /***
         * 設定儲存的的全路徑
         * @param path
         * @return
         */
        public Builder setSavePath(String path) {
            mSavePath = path;
            return this;
        }

        /**
         * 設定儲存資料夾名稱
         * @param basePath 資料夾名稱
         * @return
         */
        public Builder setBasePath(String basePath) {
            mBasePath = basePath;
            return this;
        }

        /***
         * 儲存檔名
         * @param name 檔名
         * @return
         */
        public Builder setSaveName(String name) {
            mSaveName = name;
            return this;
        }

        /***
         * 請求地址
         * @param url
         */
        public Builder(String url) {
            mUrl = url;
        }

        public Builder() {

        }

        /***
         * 設定狀態監聽回撥
         * @param l
         * @return
         */
        public Builder setListenners(Listenner l) {
            mSuccessListenner = l;
            mErrorListenner = l;
            mLengthChangeListenner = l;
            mSpeedChangeListenner = l;
            mStopListenner = l;
            return this;
        }

        /***
         * 成功監聽
         * @param l
         * @return
         */
        public Builder setSuccessListenner(Listenner l) {
            mSuccessListenner = l;
            return this;
        }

        /***
         * 失敗監聽
         * @param l
         * @return
         */
        public Builder setErrorListenner(Listenner l) {
            mErrorListenner = l;
            return this;
        }

        public Builder setStopListenner(Listenner l) {
            mStopListenner = l;
            return this;
        }

        /***
         * 長度變化監聽
         * @param l
         * @return
         */
        public Builder setLengthChangeListenner(Listenner l) {
            mLengthChangeListenner = l;
            return this;
        }

        /***
         * 進度重新整理監聽
         * @param l
         * @return
         */
        public Builder setOnSpeedListenner(Listenner l) {
            mSpeedChangeListenner = l;
            return this;
        }

        /***
         * 設定起始下載偏移量和檔案長度
         * @param offset 偏移量 首次下載為 0,該值只作為參考實際已檔案長度為準
         * @param fileAllLength 檔案長度,可有可無
         * @return
         */
        public Builder setOffset(long offset, long fileAllLength) {
            mStartOffset = offset;
            mFileAllLength = fileAllLength;
            return this;
        }

        public Builder setUrl(String url) {
            mUrl = url;
            return this;
        }

        /***
         * 請求頭(如果header頭中包含Range 則會替換預設的Range屬性,建議不要包含)
         * @param headers
         * @return
         */
        public Builder setHeaders(Map<String, String> headers) {
            mHeaders = headers;
            return this;
        }

        /***
         * 請求頭
         * @param headers
         * @return
         */
        public synchronized Builder addHeaders(Map<String, String> headers) {
            if (mHeaders == null) {
                mHeaders = headers;
            } else {
                mHeaders.putAll(headers);
            }
            return this;
        }

        /***
         * 請求頭
         * @param k
         * @param v
         * @return
         */
        public synchronized Builder addHeader(String k, String v) {
            if (mHeaders == null) {
                mHeaders = new HashMap<>();
            }
            return this;
        }

        /***
         * 如果url後面包含引數,可以使用該方法為url新增引數
         * @param params
         * @return
         */
        public synchronized Builder setUrlParams(Map<String, String> params) {
            mUrlParams = params;
            return this;
        }

        /***
         * url引數
         * @param params
         * @return
         */
        public synchronized Builder addUrlParams(Map<String, String> params) {
            if (mUrlParams == null) {
                mUrlParams = params;
            } else {
                mUrlParams.putAll(mUrlParams);
            }
            return this;
        }

        /**
         * url 引數
         * @param k
         * @param v
         * @return
         */
        public synchronized Builder addUrlParams(String k, String v) {
            if (mUrlParams == null) {
                mUrlParams = new HashMap<>();
            }
            mUrlParams.put(k, v);
            return this;
        }


        public BRDownloadThread build() throws RuntimeException {

            if (mErrorListenner == null) {
                mErrorListenner = this;
            }

            if (mSpeedChangeListenner == null) {
                mSpeedChangeListenner = this;
            }

            if (mSuccessListenner == null) {
                mSuccessListenner = this;
            }

            if (mStopListenner == null) {
                mStopListenner = this;
            }

            if (mLengthChangeListenner == null) {
                mLengthChangeListenner = this;
            }

            //檔案儲存路徑錯誤
            if (TextUtils.isEmpty(mSavePath) && (TextUtils.isEmpty(mBasePath)
                    || TextUtils.isEmpty(mSaveName))) {
                throw new RuntimeException("can not build BRDownloadthread,because savepath or (saveBasePasth and saveName is null)!");
            }

            //起始下載偏移量大於檔案長度
            if (mStartOffset > mFileAllLength) {
                throw new RuntimeException("can not continue to download form startOffset,Because file length is null!");
            }

            if (TextUtils.isEmpty(mSavePath) && !TextUtils.isEmpty(mBasePath) && !TextUtils.isEmpty(mSaveName)) {
                mSavePath = mBasePath + File.separator + mSaveName;
            }

            BRDownloadThread thread = new BRDownloadThread();
            thread.mBuilder = this;
            return thread;
        }

        @Override
        public void onChanged(int code, String... value) {

        }
    }
}

示例:

 mDownloadThread = new BRDownloadThread.Builder(downloadPath)
                        .setSavePath(savePasth)
                        .setOffset(0,0)
                        .setListenners(new BRDownloadThread.SimpleDownLoadListenner() {
                            @Override
                            public void onChanged(int code, String... value) {
                                switch (code) {
                                    case Listenner.STATUS_ERROR: {
                                        LogUtil.i("ERROR == > " + getString(value));
                                    }break;
                                    case Listenner.STATUS_LENGTH_CHANGE: {
                                        LogUtil.i("LENGTH == >" + getString(value));
                                    }break;
                                    case Listenner.STATUS_SPEED_CHANGE: {
                                        LogUtil.i("SPEED == >" + getString(value));
                                    }break;
                                    case Listenner.STATUS_STOP: {
                                        LogUtil.i("STOP == >" + getString(value));
                                    }break;
                                    case Listenner.STATUS_SUCCESS: {
                                        LogUtil.i("SUCCESSFUL == >" + getString(value));
                                    }break;
                                }
                            }
                        })
                        .build();
                mDownloadThread.start();


相關推薦

Android 基於OkHttp下載,支援https,斷點下載,優化下載速度

package com.lenovo.smarthcc.http.okhttp; import android.text.TextUtils; import com.lenovo.smarthcc.http.Listenner; import java.io.File

Android實現網路多執行緒斷點續傳下載

本示例介紹在Android平臺下通過HTTP協議實現斷點續傳下載。       我們編寫的是Andorid的HTTP協議多執行緒斷點下載應用程式。直接使用單執行緒下載HTTP檔案對我們來說是一件非常簡單的事。那麼,多執行緒斷點需要什麼功能?       1.多執行緒下載,

Android 基於Zxing的二維碼掃描優化

最近公司專案App中要整合二維碼掃描來適應在戶外工作的時候,對碼頭集裝箱等上面貼的A4紙張列印的二維碼進行識別, 一般App二維碼整合後,能掃出來就不管了,但是我們在整合成功後,根據使用者反饋,在戶外的環境下,很多二維碼識別不了,或者識別速度慢,我們自己也是適用了一下,發現也確實是這樣. &nb

Android網路程式設計 --斷點續傳下載檔案

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

AndroidOkHttp使用(包括上傳與下載)

OkHttp介紹 OkHttp是Square公司的出品,一個Http請求庫,Google官方文件不推薦人們使用HttpClient,可是HttpURLConnection 實在是太難用了,因此很多人使用了OkHttp 來解決這問題, Andro

installgithub-支援斷點續傳下載GitHubDesktop離線安裝檔案

用GitHub賬號提交程式碼的都希望下載本地客戶端克隆倉庫 https://desktop.github.com/ 可是在天朝用GitHub.exe線上下載安裝這個客戶端實在是太難了 由於不支援斷點續傳 公司千M光纖外帶翻牆都無法成功下載這個玩意

基於OkHttp網路通訊工具類(傳送get、post請求、檔案上傳和下載)

一、為什麼要用OkHttp? okhttp是專注於提升網路連線效率的http客戶端。 優點: 1、它能實現同一ip和埠的請求重用一個socket,這種方式能大大降低網路連線的時間,和每次請求都建立socket,再斷開socket的方式相比,降低了伺服器伺服器的壓力。 2、okhttp 對

yunBT:一個基於TP3.1的多使用者BT離線下載程式,支援線上播放

說明:yunBT這個專案其實很早就有了,只是老沒更新,現在作者基於ThinkCMS重做該程式,支援多使用者註冊下載,Magnet和HTTP下載。每個單獨使用者支援10個任務,預設下載檔案最大為10GB,可以在後臺修改。下載完成後使用者可以直接檢視下載的檔案僅支援mp4檔案線上播放。管理員可以新增使用者的下載量

基於OkHttp Retrofit RxJava 多執行緒下載。請求、快取、自動更新.限制佇列數.封裝庫

XDownload介紹 本庫封裝基於Okhttp3,Retrofit2,RxJava2.0,Greendao3.2 ps : 當然當然,都封裝好了,你也可以無視 GitHub地址 如果你覺得好用,對你有幫助,請給個star 介面

Android下載-實時進度-斷點續傳

概述 帶顯示進度和斷點續傳的下載示例 使用Okhttp框架 思路 獲取下載檔案的總大小,獲取本地檔案,如果不存在,就下載,存在,就獲取檔案的大小, 如果本地檔案的大小與網路上檔案的大小,就提示下載完成。 如果本地檔案大小 < 網路上檔案的大小,

Android使用okhttp封裝多檔案批量下載 (帶進度條,取消下載

在網上搜索了很多關於okhttp封裝的網路框架,唯獨沒找到完美實現了多個檔案批量下載的案例,當前使用的最多的也就是okhttp了,所以,我學習了各位大神的封裝後,自己也試著封裝了一個關於okhttp的網路請求框架,方便專案中的使用。 實現的功能基本如下:

WinHttp支援HTTPS下載

#include "windows.h" #include "winhttp.h" #include "wchar.h" #pragma comment(lib,"Winhttp.lib") // SSL (Secure Sockets Layer) example //

OkHttp實現多執行緒斷點續傳下載,單例模式下多工下載管理器,一起拋掉sp,sqlite的輔助吧

        最近專案需要使用到斷點下載功能,筆者比較喜歡折騰,想方設法拋棄SharedPreferences,尤其是sqlite作記錄輔助,改用臨時記錄檔案的形式記錄下載進度,本文以斷點下載為例。先看看demo執行效果圖:               斷點續傳:記

Android okHttp網路請求之檔案上傳下載

前言:    前面介紹了基於okHttp的get、post基本使用(http://www.cnblogs.com/whoislcj/p/5526431.html),今天來實現一下基於okHttp的檔案上傳、下載。      okHttp相關文章地址: 檔案上傳: 1.)不帶引數上傳檔案

Android FTP 多執行緒斷點續傳下載\上傳

最近在給我的開源下載框架Aria增加FTP斷點續傳下載和上傳功能,在此過程中,爬了FTP的不少坑,終於將功能實現了,在此把一些核心功能點記錄下載。 FTP下載原理 FTP單執行緒斷點續傳 FTP和傳統的HTTP協議有所不同,由於FTP沒有所謂的標頭

Android檔案下載(實現斷點續傳)

http://www.ideasandroid.com/archives/328#more-328 本文將介紹在android平臺下如何實現多執行緒下載,大家都知道,android平臺使用java做為開發語言,所以java中支援的多執行緒下載方式在android平臺下都支援,

android 多執行緒斷點續傳下載

今天跟大家一起分享下android開發中比較難的一個環節,可能很多人看到這個標題就會感覺頭很大,的確如果沒有良好的編碼能力和邏輯思維,這塊是很難搞明白的,前面2次總結中已經為大家分享過有關技術的一些基本要領,我們先一起簡單回顧下它的基本原理。什麼是多執行緒下載?多執行緒下載其

Android-斷點續傳下載

工作找完了,玩也玩完了,該好好學習了,最近我把《Java併發程式設計的藝術》這本書給讀完了,對於併發程式設計以及執行緒池的使用還是不嫻熟,我就在imooc上找到一個專案“Android-Service系列之斷點續傳下載“,這是我對這個專案在編寫的時候記錄。 涉及

Android基於Okhttp3的檔案下載工具類

需求中有需要簡易的下載檔案的,例如圖片,音訊,視訊等。首先這個下載工具類沒有斷點下載,也就是說沒有暫停,快取。不過解決日常工作中的小檔案下載是綽綽有餘的。下面可以看一看 一、新增okhttp3的遠端依賴 compile 'com.squareup.okhttp3:ok

Android開發經驗】關於“多執行緒斷點續傳下載”功能的一個簡單實現和講解

    上班第一天,在技術群裡面和大家閒扯,無意中談到了關於框架的使用,一個同學說為了用xUtils的斷線續傳下載功能,把整個庫引入到了專案中,在google的官方建議中,是非常不建議這種做法的,集合框架雖然把很多功能整合起來,但是程式碼越多,出現問題的可能越大,而且無形之中