Keywords: Android | File Download | Progress Indication | AsyncTask | Service
Abstract: This article explores various methods to download files in Android while displaying progress, including AsyncTask, Service-based approaches, and DownloadManager. It covers implementation details, code examples, and best practices for robust file handling.
In modern Android applications, downloading files from the internet is a common requirement, especially for features like app updates. Displaying the download progress enhances user experience by providing feedback on the operation. This article delves into several methods to achieve file download with progress indication, focusing on robust implementation and best practices.
Using AsyncTask for File Download
AsyncTask is a convenient class in Android that allows performing background operations and publishing results on the UI thread without manipulating threads directly. It is ideal for short-lived tasks such as file downloads.
To implement file download with progress using AsyncTask, follow these steps. First, initialize a ProgressDialog in your activity. Then, create a subclass of AsyncTask that handles the download in the background and updates the progress.
Here is a rewritten code example based on the core concepts:
// Example of DownloadTask extending AsyncTask
private class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
private ProgressDialog progressDialog;
private PowerManager.WakeLock wakeLock;
public DownloadTask(Context context, ProgressDialog progressDialog) {
this.context = context;
this.progressDialog = progressDialog;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// Acquire wake lock to prevent device sleep
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
wakeLock.acquire();
progressDialog.show();
}
@Override
protected String doInBackground(String... urls) {
String urlString = urls[0];
HttpURLConnection connection = null;
InputStream input = null;
OutputStream output = null;
try {
URL url = new URL(urlString);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "HTTP error: " + connection.getResponseCode();
}
int fileLength = connection.getContentLength();
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/downloaded_file.ext");
byte[] buffer = new byte[4096];
int bytesRead;
long totalBytesRead = 0;
while ((bytesRead = input.read(buffer)) != -1) {
if (isCancelled()) {
input.close();
return null;
}
totalBytesRead += bytesRead;
if (fileLength > 0) {
int progress = (int) (totalBytesRead * 100 / fileLength);
publishProgress(progress);
}
output.write(buffer, 0, bytesRead);
}
} catch (Exception e) {
return e.toString();
} finally {
// Close streams and connection
try {
if (output != null) output.close();
if (input != null) input.close();
} catch (IOException e) {
// Ignore
}
if (connection != null) connection.disconnect();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressDialog.setIndeterminate(false);
progressDialog.setMax(100);
progressDialog.setProgress(values[0]);
}
@Override
protected void onPostExecute(String result) {
wakeLock.release();
progressDialog.dismiss();
if (result != null) {
Toast.makeText(context, "Download failed: " + result, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Download completed", Toast.LENGTH_SHORT).show();
}
}
}In this code, the doInBackground method handles the network request and file writing, while onProgressUpdate updates the ProgressDialog. Note that the WAKE_LOCK permission is required in the manifest to prevent the device from sleeping during download.
Using Service for Background Download
For long-running downloads or when the app might be in the background, using a Service is more appropriate. Services run independently of the activity lifecycle. IntentService is a subclass that handles intents on a worker thread, making it suitable for download tasks.
To update the UI from a Service, ResultReceiver can be used to send progress updates back to the activity.
Here is a simplified example:
// DownloadService extending IntentService
public class DownloadService extends IntentService {
public static final int UPDATE_PROGRESS = 1;
public DownloadService() {
super("DownloadService");
}
@Override
protected void onHandleIntent(Intent intent) {
String url = intent.getStringExtra("url");
ResultReceiver receiver = intent.getParcelableExtra("receiver");
try {
URL downloadUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection();
connection.connect();
int fileLength = connection.getContentLength();
InputStream input = connection.getInputStream();
OutputStream output = new FileOutputStream("/sdcard/file.ext");
byte[] data = new byte[1024];
int count;
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
int progress = (int) (total * 100 / fileLength);
Bundle bundle = new Bundle();
bundle.putInt("progress", progress);
receiver.send(UPDATE_PROGRESS, bundle);
output.write(data, 0, count);
}
output.close();
input.close();
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
Bundle bundle = new Bundle();
bundle.putInt("progress", 100);
receiver.send(UPDATE_PROGRESS, bundle);
}
}In the activity, you can start the service and handle progress updates via a custom ResultReceiver.
// In activity
private class DownloadReceiver extends ResultReceiver {
private ProgressDialog dialog;
public DownloadReceiver(Handler handler, ProgressDialog dialog) {
super(handler);
this.dialog = dialog;
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == DownloadService.UPDATE_PROGRESS) {
int progress = resultData.getInt("progress");
dialog.setProgress(progress);
if (progress == 100) {
dialog.dismiss();
}
}
}
}
// To start download
ProgressDialog dialog = new ProgressDialog(this);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.show();
Intent intent = new Intent(this, DownloadService.class);
intent.putExtra("url", "http://example.com/file.ext");
intent.putExtra("receiver", new DownloadReceiver(new Handler(), dialog));
startService(intent);This approach ensures that the download continues even if the activity is destroyed, and the progress is updated when the activity is active.
Utilizing DownloadManager
For simpler use cases, Android provides DownloadManager, which handles downloads in the background and shows progress in the notification bar. It is available from GingerBread (API level 9) onwards.
Example usage:
// Check if DownloadManager is available
public static boolean isDownloadManagerAvailable(Context context) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
}
// Download file using DownloadManager
if (isDownloadManagerAvailable(this)) {
String url = "http://example.com/file.ext";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setTitle("Download File");
request.setDescription("Downloading file...");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "file.ext");
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
} else {
Toast.makeText(this, "DownloadManager not available", Toast.LENGTH_SHORT).show();
}DownloadManager simplifies the process but offers less control over the download. It does not support response caching, so repeated downloads may occur.
Comparison and Best Practices
Each method has its strengths. AsyncTask is suitable for short tasks within an activity. Service-based approaches are better for background operations. DownloadManager is ideal for standard downloads without custom UI.
Key best practices include:
- Check network availability before starting download.
- Ensure appropriate permissions (INTERNET, WRITE_EXTERNAL_STORAGE).
- Handle errors and allow user cancellation.
- Consider using ProgressBar instead of ProgressDialog for embedded UI, as ProgressDialog is deprecated in API 26.
- For large files, implement resume capability.
By understanding these methods, developers can choose the right approach based on app requirements, balancing control and simplicity.
In conclusion, file download with progress indication in Android can be implemented using AsyncTask, Service, or DownloadManager. While AsyncTask and Service provide more control, DownloadManager offers a system-level solution. Always test on various devices and handle edge cases for a seamless user experience.