首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android 檔案下載與進度顯示:AsyncTask、IntentService、DownloadManager 三大方案完整比較

     編輯:程式設計綜合問答

在 Android 應用中實現「下載檔案並即時顯示進度」是常見需求。本文詳細比較三種主流方案:AsyncTask(傳統)、IntentService(後台服務)、DownloadManager(系統級),並提供完整可執行程式碼範例。

方案一:AsyncTask + ProgressDialog(適用簡單、短時間下載)

優點:程式碼集中、易理解、可直接更新 UI。
缺點:Activity 旋轉/銷毀會導致 Task 洩漏、無法支援斷點續傳、Android 11+ 已棄用。

核心實作

// Activity 成員變數
private ProgressDialog mProgressDialog;

private void startDownload(String url) {
    mProgressDialog = new ProgressDialog(this);
    mProgressDialog.setMessage("下載中...");
    mProgressDialog.setIndeterminate(false);
    mProgressDialog.setMax(100);
    mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    mProgressDialog.setCancelable(false);
    
    new DownloadTask().execute(url);
}

private class DownloadTask extends AsyncTask {
    @Override
    protected void onPreExecute() {
        mProgressDialog.show();
    }

    @Override
    protected String doInBackground(String... urls) {
        String urlStr = urls[0];
        try {
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.connect();
            int fileLength = conn.getContentLength();
            
            InputStream input = new BufferedInputStream(conn.getInputStream());
            File outputFile = new File(getExternalFilesDir(null), "download.apk");
            OutputStream output = new FileOutputStream(outputFile);
            
            byte[] buffer = new byte[4096];
            long total = 0;
            int count;
            while ((count = input.read(buffer)) != -1) {
                if (isCancelled()) break;
                total += count;
                if (fileLength > 0) {
                    publishProgress((int) (total * 100 / fileLength));
                }
                output.write(buffer, 0, count);
            }
            output.flush();
            output.close();
            input.close();
            return outputFile.getAbsolutePath();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        mProgressDialog.setProgress(progress[0]);
    }

    @Override
    protected void onPostExecute(String result) {
        mProgressDialog.dismiss();
        if (result != null) {
            Toast.makeText(MainActivity.this, "下載完成:" + result, Toast.LENGTH_LONG).show();
            // 安裝 APK 或開啟檔案
        } else {
            Toast.makeText(MainActivity.this, "下載失敗", Toast.LENGTH_SHORT).show();
        }
    }
}

方案二:IntentService + ResultReceiver(適用後台長時間下載、跨 Activity 通知)

優點:獨立於 Activity 生命週期、支援多 Activity 監聽進度、自動在背景執行緒執行。
缺點:程式碼較複雜、Android 8+ 背景執行限制需改用 JobIntentService 或 WorkManager。

核心架構

  1. DownloadService 繼承 IntentService,onHandleIntent 執行下載並透過 ResultReceiver 回報進度。
  2. Activity 建立自訂 ResultReceiver 子類別,覆寫 onReceiveResult 更新 UI。
  3. Manifest 註冊 Service。
// DownloadService.java
public class DownloadService extends IntentService {
    public static final int UPDATE_PROGRESS = 8344;
    public DownloadService() { super("DownloadService"); }

    @Override
    protected void onHandleIntent(Intent intent) {
        String url = intent.getStringExtra("url");
        ResultReceiver receiver = intent.getParcelableExtra("receiver");
        // ... 下載邏輯同 AsyncTask ...
        // 關鍵:每段下載後
        Bundle bundle = new Bundle();
        bundle.putInt("progress", (int)(total * 100 / fileLength));
        receiver.send(UPDATE_PROGRESS, bundle);
        // 完成時
        bundle.putInt("progress", 100);
        receiver.send(UPDATE_PROGRESS, bundle);
    }
}

// Activity 中啟動
Intent intent = new Intent(this, DownloadService.class);
intent.putExtra("url", downloadUrl);
intent.putExtra("receiver", new DownloadReceiver(new Handler()));
startService(intent);

// DownloadReceiver 內部類
private class DownloadReceiver extends ResultReceiver {
    public DownloadReceiver(Handler handler) { super(handler); }
    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        if (resultCode == DownloadService.UPDATE_PROGRESS) {
            int progress = resultData.getInt("progress");
            mProgressDialog.setProgress(progress);
            if (progress == 100) mProgressDialog.dismiss();
        }
    }
}

方案三:DownloadManager(Android 2.3+ 系統級下載器,強烈推薦)

優點:系統自動處理執行緒、斷點續傳、網路切換、通知列進度、媒體掃描、電源管理、權限隔離。
缺點:自訂 UI 困難(進度只在通知列)、無法直接獲取進度回調更新自訂 ProgressBar。

一鍵下載實作

public static void startSystemDownload(Context context, String url, String fileName, String title, String description) {
    DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
    request.setTitle(title);
    request.setDescription(description);
    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
    request.allowScanningByMediaScanner(); // 讓媒體掃描器發現
    
    DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
    manager.enqueue(request);
}

方案選擇建議

場景推薦方案
簡單 App 內下載、需自訂 ProgressBar、檔案較小AsyncTask(或更現代的 Coroutine + Flow)
大檔案、需背景下載、支援斷點續傳、跨 Activity 監控WorkManager + 自訂 Worker(現代替代 IntentService)
APK 更新、媒體檔案、希望系統通知列顯示進度、最省開發力DownloadManager

現代化替代:WorkManager + Coroutine

Android 11+ 棄用 AsyncTask,Android 8+ 限制 IntentService。新專案建議使用 WorkManager 配合 Kotlin Coroutine 與 Flow 實現可觀測的下載進度:

// DownloadWorker.kt
class DownloadWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
    override suspend fun doWork(): Result {
        val url = inputData.getString("url") ?: return Result.failure()
        val fileName = inputData.getString("fileName") ?: "download"
        val outputFile = File(applicationContext.getExternalFilesDir(null), fileName)
        
        val progressChannel = Channel(Channel.CONFLATED)
        // 使用 suspend 函式下載並發送進度
        val success = downloadWithProgress(url, outputFile, progressChannel)
        
        // 透過 ForegroundInfo 顯示通知列進度(可選)
        return if (success) Result.success() else Result.failure()
    }
}

必要權限與檢查清單

  • <uses-permission android:name="android.permission.INTERNET" />
  • <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />(Android 10+ 改用 Scoped Storage)
  • <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />(檢查網路狀態)
  • 下載前檢查:網路連線、儲存空間、目標目錄存在且可寫
  • 支援斷點續傳:記錄已下載位元組,請求 Range: bytes=xxx-
  • 提供取消下載功能:AsyncTask.cancel(true) 或 WorkManager.cancelWorkById()

選對方案、處理好邊界情況,才能打造穩健、使用者體驗良好的下載功能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved