首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Android 文件下載執行緒類實作:支援斷點續傳與多執行緒下載

     編輯:Java 相關知識

前言

在 Android 應用開發中,文件下載是常見需求。為了提升下載效率並支援斷點續傳,採用多執行緒分段下載是主流方案。本文提供一個實用的 FileDownloadThread 類別,採用 HTTP Range 請求頭實現分段下載,並透過 RandomAccessFile 實現隨機寫入,適合整合至下載管理器中。

核心設計思路

  • 分段下載:每個執行緒負責下載檔案的特定位元組範圍(startPosition ~ endPosition)。
  • HTTP Range 請求:利用 Connection.setRequestProperty("Range", "bytes=start-end") 向伺服器請求特定區塊。
  • 隨機存取寫入:使用 RandomAccessFile.seek(position) 定位寫入位置,避免多執行緒寫入衝突。
  • 狀態控制:提供暫停、繼續、銷毀等控制方法,方便上層管理。

完整原始碼

package android.mooc.tools;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection;
import android.util.Log;

public class FileDownloadThread extends Thread {
    private static final int BUFFER_SIZE = 1024;
    
    private URL url;
    private File file;
    private int startPosition;
    private int endPosition;
    private int curPosition;
    private boolean finished = false;
    private int downloadSize;
    private boolean state;
    private boolean destory;

    public boolean isDestory() { return destory; }
    public void setDestory(boolean destory) { this.destory = destory; }

    public FileDownloadThread(URL url, File file, int startPosition, int endPosition) {
        this.url = url;
        this.file = file;
        this.startPosition = startPosition;
        this.curPosition = startPosition;
        this.endPosition = endPosition;
        this.downloadSize = 0;
    }

    @Override
    public void run() {
        destory = false;
        state = true;
        BufferedInputStream bis = null;
        RandomAccessFile fos = null;
        byte[] buf = new byte[BUFFER_SIZE];
        URLConnection con = null;
        
        try {
            con = url.openConnection();
            con.setAllowUserInteraction(true);
            con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
            con.setRequestProperty("accept", "*/*");
            con.setRequestProperty("connection", "Keep-Alive");
            con.setRequestProperty("Accept-Language", "zh-CN");
            con.setRequestProperty("Charset", "UTF-8");
            con.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");

            fos = new RandomAccessFile(file, "rw");
            fos.seek(startPosition);
            bis = new BufferedInputStream(con.getInputStream());

            while ((curPosition < endPosition) && (!destory)) {
                while (!state) {
                    sleep(2000);
                }
                int len = bis.read(buf, 0, BUFFER_SIZE);
                if (len != -1) {
                    fos.write(buf, 0, len);
                    curPosition += len;
                    if (curPosition > endPosition) {
                        downloadSize += len - (curPosition - endPosition);
                    } else {
                        downloadSize += len;
                    }
                }
                Log.i("DownloadThread", "run: len=" + len);
            }
            finished = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bis != null) bis.close();
                if (fos != null) fos.close();
            } catch (Exception ignored) {}
        }
    }

    public boolean isState() { return state; }
    public void setState(boolean state) { this.state = state; }
    public boolean isFinished() { return finished; }
    public int getDownloadSize() { return downloadSize; }
    public void setDownloadSize(int downloadSize) { this.downloadSize = downloadSize; }
}

關鍵點解析

1. Range 請求頭

con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition); 告訴伺服器只傳回指定位元組範圍,這是分段下載的基礎。伺服器需支援 Accept-Ranges: bytes。

2. RandomAccessFile 隨機寫入

fos.seek(startPosition) 將檔案指標移至該執行緒負責的起始位置,各執行緒寫入各自區塊,互不干擾。

3. 暫停/繼續機制

state 旗標控制下載迴圈。呼叫 setState(false) 可暫停,執行緒進入 sleep(2000) 輪詢;呼叫 setState(true) 即可繼續。

4. 銷毀與清理

設定 destory = true 可讓執行緒在下一次迴圈檢查時退出,並在 finally 區塊關閉流,釋放資源。

使用範例

// 在下載管理器中建立多個執行緒
int threadCount = 4;
int fileSize = getFileSize(url);
int blockSize = fileSize / threadCount;

for (int i = 0; i < threadCount; i++) {
    int start = i * blockSize;
    int end = (i == threadCount - 1) ? fileSize - 1 : (i + 1) * blockSize - 1;
    FileDownloadThread thread = new FileDownloadThread(url, saveFile, start, end);
    thread.start();
    threadList.add(thread);
}

注意事項

  • 伺服器必須支援 HTTP Range 請求,否則無法分段下載。
  • 實際專案中應加入進度回呼、錯誤重試、資料庫持久化下載進度等功能。
  • 大檔案建議使用 long 型別儲存位置與大小,避免整數溢位。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved