How To Create A Download Manager In Android

This example will show you how to create a download manager to download file from a url in android application. It use android activity, foreground service, asynctask and notification etc.

1. Android Download Manager Examples.

how to create a download manager in android

  1. First input a download file url in editor text box.
  2. Click the “Start Download” button, there will start a foreground service which download the file in a AsyncTask object and send a head-up notification to show the download progress.
  3. There are three buttons in the notification. Click Pause button will pause the download progress. Click Continue button to continue download. Click Cancel to cancel the download.

2. Example Java Files.

android download manager java files structure

There are six java files used in this example. Below is the relationship between them.

android download manager java files relations

When DownloadActivity is created, it will first bind DownloadService object use ServiceConnection and get DownloadService’s DownloadBinder object.

When click the start button, it will call DownloadBinder‘s startDownload(String downloadUrl, int progress) method. In this method, it will create an instance of DownloadManager ( a sub class of  android.os.AsyncTask ) and call it’s execute method to run the AsyncTask.

The DownloadManager will execute it’s doInBackground method. In this method it will call DownloadUtil‘s downloadFileFromUrl method to download file.

During the download process, DownloadUtil will check the download progress and then call DownloadManager‘s updateTaskProgress method to update download progress.

In updateTaskProgress method, download manager will invoke DownloadListener‘s onUpdateDownloadProgress method to send a head-up notification to show user what current download progress is.

3. Example Java File Source Code.

3.1 Main Activity.

DownloadActivity.java

package com.dev2qa.example.service.download;

import android.Manifest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import android.os.Handler;
import com.dev2qa.example.R;

public class DownloadActivity extends AppCompatActivity {

    private EditText downloadUrlEditor = null;

    private Button startDownloadButton = null;

    private DownloadBinder downloadBinder = null;

    private int REQUEST_WRITE_PERMISSION_CODE = 1;

    private Handler updateButtonStateHandler = null;

    private int MESSAGE_UPDATE_START_BUTTON = 2;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder = (DownloadBinder)service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download);

        setTitle("dev2qa.com - Android Download Manager Example.");

        startAndBindDownloadService();

        initControls();

        startDownloadButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                    if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                        Toast.makeText(DownloadActivity.this, "This app need write sdcard permission, please allow.", Toast.LENGTH_LONG).show();
                        ActivityCompat.requestPermissions(DownloadActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_PERMISSION_CODE);
                    } else {
                        String downloadFileUrl = downloadUrlEditor.getText().toString();
                        downloadBinder.startDownload(downloadFileUrl, 0);
                        startDownloadButton.setEnabled(false);

                        Thread enableButtonThread = new Thread()
                        {
                            @Override
                            public void run() {
                                while (true) {
                                    try {
                                        if (downloadBinder.getDownloadManager().isDownloadCanceled()) {
                                            Message msg = new Message();
                                            msg.what = MESSAGE_UPDATE_START_BUTTON;
                                            updateButtonStateHandler.sendMessage(msg);
                                            break;
                                        }

                                        Thread.sleep(2000);
                                    }catch(Exception ex)
                                    {
                                        Log.e(DownloadUtil.TAG_DOWNLOAD_MANAGER, ex.getMessage(), ex);
                                    }
                                }
                            }
                        };
                        enableButtonThread.start();
                    }
            }
        });
    }

    private void initControls()
    {
        if(downloadUrlEditor == null)
        {
            downloadUrlEditor = (EditText)findViewById(R.id.download_manager_url_editor);
            downloadUrlEditor.setText("http://dev2qa.com/demo/media/play_video_test.mp4");
        }

        if(startDownloadButton == null)
        {
            startDownloadButton = (Button)findViewById(R.id.download_manager_start_button);
        }

        if(updateButtonStateHandler == null)
        {
            updateButtonStateHandler = new Handler()
            {
                @Override
                public void handleMessage(Message msg) {
                    if(msg.what == MESSAGE_UPDATE_START_BUTTON)
                    {
                        startDownloadButton.setEnabled(true);
                    }
                }
            };
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if(requestCode == REQUEST_WRITE_PERMISSION_CODE)
        {
            int grantResult = grantResults[0];
            if(grantResult == PackageManager.PERMISSION_GRANTED)
            {
                Toast.makeText(this, "You can continue to use this app.", Toast.LENGTH_SHORT).show();
            }else
            {
                Toast.makeText(this, "You disallow write external storage permission, app closed.", Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }

    private void startAndBindDownloadService()
    {
        Intent downloadIntent = new Intent(this, DownloadService.class);
        startService(downloadIntent);
        bindService(downloadIntent, serviceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);
    }
}

3.2 Download Binder.

DownloadBinder.java

package com.dev2qa.example.service.download;

import android.app.Notification;
import android.os.Binder;
import android.text.TextUtils;

/**
 * Created by Jerry on 3/9/2018.
 */

public class DownloadBinder extends Binder {

    private DownloadManager downloadManager = null;

    private DownloadListener downloadListener = null;

    private String currDownloadUrl = "";

    public DownloadManager getDownloadManager() {
        return downloadManager;
    }

    public DownloadBinder() {

        if(downloadListener == null)
        {
            downloadListener = new DownloadListener();
        }
    }

    public void startDownload(String downloadUrl, int progress)
    {
        /* Because downloadManager is a subclass of AsyncTask, and AsyncTask can only be executed once,
        * So each download need a new downloadManager. */
        downloadManager = new DownloadManager(downloadListener);

        /* Because DownloadUtil has a static variable of downloadManger, so each download need to use new downloadManager. */
        DownloadUtil.setDownloadManager(downloadManager);

        // Execute download manager, this will invoke downloadManager's doInBackground() method.
        downloadManager.execute(downloadUrl);

        // Save current download file url.
        currDownloadUrl = downloadUrl;

        // Create and start foreground service with notification.
        Notification notification = downloadListener.getDownloadNotification("Downloading...", progress);
        downloadListener.getDownloadService().startForeground(1, notification);
    }

    public void continueDownload()
    {
        if(currDownloadUrl != null && !TextUtils.isEmpty(currDownloadUrl))
        {
            int lastDownloadProgress = downloadManager.getLastDownloadProgress();
            startDownload(currDownloadUrl, lastDownloadProgress);
        }
    }

    public void cancelDownload()
    {
        downloadManager.cancelDownload();
    }

    public void pauseDownload()
    {
        downloadManager.pauseDownload();
    }

    public DownloadListener getDownloadListener() {
        return downloadListener;
    }
}

3.3 Download Service.

DownloadService.java

package com.dev2qa.example.service.download;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class DownloadService extends Service {

    public static final String ACTION_PAUSE_DOWNLOAD = "ACTION_PAUSE_DOWNLOAD";

    public static final String ACTION_CONTINUE_DOWNLOAD = "ACTION_CONTINUE_DOWNLOAD";

    public static final String ACTION_CANCEL_DOWNLOAD = "ACTION_CANCEL_DOWNLOAD";

    private DownloadBinder downloadBinder = new DownloadBinder();

    public DownloadService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        downloadBinder.getDownloadListener().setDownloadService(this);
        return downloadBinder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String action = intent.getAction();
        if(ACTION_PAUSE_DOWNLOAD.equals(action))
        {
            downloadBinder.pauseDownload();
            Toast.makeText(getApplicationContext(), "Download is paused", Toast.LENGTH_LONG).show();
        }else if(ACTION_CANCEL_DOWNLOAD.equals(action))
        {
            downloadBinder.cancelDownload();
            Toast.makeText(getApplicationContext(), "Download is canceled", Toast.LENGTH_LONG).show();
        }else if(ACTION_CONTINUE_DOWNLOAD.equals(action))
        {
            downloadBinder.continueDownload();
            Toast.makeText(getApplicationContext(), "Download continue", Toast.LENGTH_LONG).show();
        }

        return super.onStartCommand(intent, flags, startId);
    }
}

3.4 Download Manager.

DownloadManager.java

package com.dev2qa.example.service.download;

import android.os.AsyncTask;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.io.IOException;

/**
 * Created by Jerry on 3/9/2018.
 */

public class DownloadManager extends AsyncTask<String, Integer, Integer> {

    private DownloadListener downloadListener = null;

    private boolean downloadCanceled = false;

    private boolean downloadPaused = false;

    private int lastDownloadProgress = 0;

    private String currDownloadUrl = "";

    public boolean isDownloadCanceled() {
        return downloadCanceled;
    }

    public void setDownloadCanceled(boolean downloadCanceled) {
        this.downloadCanceled = downloadCanceled;
    }

    public boolean isDownloadPaused() {
        return downloadPaused;
    }

    public void setDownloadPaused(boolean downloadPaused) {
        this.downloadPaused = downloadPaused;
    }

    public int getLastDownloadProgress() {
        return lastDownloadProgress;
    }

    public void setLastDownloadProgress(int lastDownloadProgress) {
        this.lastDownloadProgress = lastDownloadProgress;
    }

    public DownloadManager(DownloadListener downloadListener) {
        this.downloadListener = downloadListener;
        this.setDownloadPaused(false);
        this.setDownloadCanceled(false);
    }

    /* This method is invoked after doInBackground() method. */
    @Override
    protected void onPostExecute(Integer downloadStatue) {
        if(downloadStatue == DownloadUtil.DOWNLOAD_SUCCESS)
        {
            this.setDownloadCanceled(false);
            this.setDownloadPaused(false);
            downloadListener.onSuccess();
        }else if(downloadStatue == DownloadUtil.DOWNLOAD_FAILED)
        {
            this.setDownloadCanceled(false);
            this.setDownloadPaused(false);
            downloadListener.onFailed();
        }else if(downloadStatue == DownloadUtil.DOWNLOAD_PAUSED)
        {
            downloadListener.onPaused();
        }else if(downloadStatue == DownloadUtil.DOWNLOAD_CANCELED)
        {
            downloadListener.onCanceled();
        }
    }


    /* Invoked when this async task execute.When this method return, onPostExecute() method will be called.*/
    @Override
    protected Integer doInBackground(String... params) {

        // Set current thread priority lower than main thread priority, so main thread Pause, Continue and Cancel action will not be blocked.
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY - 2);

        String downloadFileUrl = "";
        if(params!=null && params.length > 0)
        {
            downloadFileUrl = params[0];
        }

        currDownloadUrl = downloadFileUrl;

        File downloadLocalFile = createDownloadLocalFile(downloadFileUrl);

        int ret = DownloadUtil.downloadFileFromUrl(downloadFileUrl, downloadLocalFile);

        return ret;
    }

    /*
    * Parse the download file name from the download file url,
    * check whether the file exist in sdcard download directory or not.
    * If the file do not exist then create it.
    *
    * Return the file object.
    * */
    private File createDownloadLocalFile(String downloadFileUrl)
    {
        File ret = null;

        try {
            if (downloadFileUrl != null && !TextUtils.isEmpty(downloadFileUrl)) {
                int lastIndex = downloadFileUrl.lastIndexOf("/");
                if (lastIndex > -1) {
                    String downloadFileName = downloadFileUrl.substring(lastIndex + 1);
                    String downloadDirectoryName = Environment.DIRECTORY_DOWNLOADS;
                    File downloadDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
                    String downloadDirectoryPath = downloadDirectory.getPath();

                    ret = new File(downloadDirectoryPath + "/" + downloadFileName);

                    if (!ret.exists()) {
                        ret.createNewFile();
                    }
                }
            }
        }catch(IOException ex)
        {
            Log.e(DownloadUtil.TAG_DOWNLOAD_MANAGER, ex.getMessage(), ex);
        }finally {
            return ret;
        }
    }

    /* Update download async task progress. */
    public void updateTaskProgress(Integer newDownloadProgress)
    {
        lastDownloadProgress = newDownloadProgress;
        downloadListener.onUpdateDownloadProgress(newDownloadProgress);
    }

    public void pauseDownload()
    {
        this.setDownloadPaused(true);
    }

    public void cancelDownload()
    {
        this.setDownloadCanceled(true);
    }
}

3.5 Download Util.

DownloadUtil.java

package com.dev2qa.example.service.download;

import android.text.TextUtils;
import android.util.Log;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * Created by Jerry on 3/9/2018.
 */

public class DownloadUtil {

    public static final String TAG_DOWNLOAD_MANAGER = "TAG_DOWNLOAD_MANAGER";

    public static final int DOWNLOAD_SUCCESS = 1;

    public static final int DOWNLOAD_FAILED = 2;

    public static final int DOWNLOAD_PAUSED = 3;

    public static final int DOWNLOAD_CANCELED = 4;

    private static DownloadManager downloadManager = null;

    private static OkHttpClient okHttpClient = new OkHttpClient();

    public static DownloadManager getDownloadManager() {
        return downloadManager;
    }

    public static void setDownloadManager(DownloadManager downloadManager) {
        DownloadUtil.downloadManager = downloadManager;
    }

    /* Get download file size returned from http server header. */
    public static long getDownloadUrlFileSize(String downloadUrl)
    {
        long ret = 0;

        try {
            if (downloadUrl != null && !TextUtils.isEmpty(downloadUrl)) {
                Request.Builder builder = new Request.Builder();
                builder = builder.url(downloadUrl);
                Request request = builder.build();

                Call call = okHttpClient.newCall(request);
                Response response = call.execute();

                if(response != null) {
                    if(response.isSuccessful())
                    {
                        String contentLength = response.header("Content-Length");
                        ret = Long.parseLong(contentLength);
                    }
                }
            }
        }catch(Exception ex)
        {
            Log.e(TAG_DOWNLOAD_MANAGER, ex.getMessage(), ex);
        }finally {
            return ret;
        }
    }


    public static int downloadFileFromUrl(String downloadFileUrl, File existLocalFile)
    {
        int ret = DOWNLOAD_SUCCESS;
        try {

            long downloadFileLength = getDownloadUrlFileSize(downloadFileUrl);

            long existLocalFileLength = existLocalFile.length();

            if(downloadFileLength==0)
            {
                ret = DOWNLOAD_FAILED;
            }else if(downloadFileLength == existLocalFileLength)
            {
                ret = DOWNLOAD_SUCCESS;
            }else {

                Request.Builder builder = new Request.Builder();
                builder = builder.url(downloadFileUrl);
                builder = builder.addHeader("RANGE", "bytes=" + existLocalFileLength);
                Request request = builder.build();

                Call call = okHttpClient.newCall(request);
                Response response = call.execute();

                if (response != null && response.isSuccessful()) {
                    RandomAccessFile downloadFile = new RandomAccessFile(existLocalFile, "rw");
                    downloadFile.seek(existLocalFileLength);

                    ResponseBody responseBody = response.body();
                    InputStream inputStream = responseBody.byteStream();
                    BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
                    byte data[] = new byte[102400];

                    long totalReadLength = 0;

                    int readLength = bufferedInputStream.read(data);

                    while (readLength != -1) {

                        if(getDownloadManager().isDownloadPaused())
                        {
                            ret = DOWNLOAD_PAUSED;
                            break;
                        }else if(getDownloadManager().isDownloadCanceled())
                        {
                            ret = DOWNLOAD_CANCELED;
                            break;
                        }else {

                            downloadFile.write(data, 0, readLength);

                            totalReadLength = totalReadLength + readLength;

                            int downloadProgress = (int) ((totalReadLength + existLocalFileLength) * 100 / downloadFileLength);

                            getDownloadManager().updateTaskProgress(downloadProgress);

                            readLength = bufferedInputStream.read(data);
                        }
                    }
                }
            }

        }catch(Exception ex)
        {
            Log.e(TAG_DOWNLOAD_MANAGER, ex.getMessage(), ex);
        }finally
        {
            return ret;
        }
    }
}

3.6 Download Listener.

DownloadListener.java

package com.dev2qa.example.service.download;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.NotificationCompat;

import static android.content.Context.NOTIFICATION_SERVICE;

/**
 * Created by Jerry on 3/9/2018.
 */

public class DownloadListener {

    private DownloadService downloadService = null;

    private int lastProgress = 0;

    public void setDownloadService(DownloadService downloadService) {
        this.downloadService = downloadService;
    }

    public DownloadService getDownloadService() {
        return downloadService;
    }

    public void onSuccess()
    {
        downloadService.stopForeground(true);
        sendDownloadNotification("Download success.", -1);
    }

    public void onFailed()
    {
        downloadService.stopForeground(true);
        sendDownloadNotification("Download failed.", -1);
    }
    public void onPaused()
    {
        sendDownloadNotification("Download paused.", lastProgress);
    }
    public void onCanceled()
    {
        downloadService.stopForeground(true);
        sendDownloadNotification("Download canceled.", -1);
    }

    public void onUpdateDownloadProgress(int progress)
    {
        try {
            lastProgress = progress;
            sendDownloadNotification("Downloading...", progress);

            // Thread sleep 0.2 seconds to let Pause, Continue and Cancel button in notification clickable.
            Thread.sleep(200);
        }catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }


    public void sendDownloadNotification(String title, int progress)
    {
        Notification notification = getDownloadNotification(title, progress);

        NotificationManager notificationManager = (NotificationManager)downloadService.getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(1, notification);

    }

    public Notification getDownloadNotification(String title, int progress)
    {
        Intent intent = new Intent();
        PendingIntent pendingIntent = PendingIntent.getActivity(downloadService, 0, intent, 0);
        NotificationCompat.Builder notifyBuilder = new NotificationCompat.Builder(downloadService);
        notifyBuilder.setSmallIcon(android.R.mipmap.sym_def_app_icon);

        Bitmap bitmap = BitmapFactory.decodeResource(downloadService.getResources(), android.R.drawable.stat_sys_download);
        notifyBuilder.setLargeIcon(bitmap);

        notifyBuilder.setContentIntent(pendingIntent);
        notifyBuilder.setContentTitle(title);
        notifyBuilder.setFullScreenIntent(pendingIntent, true);

        if(progress > 0 && progress < 100)
        {
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("Download progress ");
            stringBuffer.append(progress);
            stringBuffer.append("%");

            notifyBuilder.setContentText("Download progress " + progress + "%");

            notifyBuilder.setProgress(100, progress, false);

            // Add Pause download button intent in notification.
            Intent pauseDownloadIntent = new Intent(getDownloadService(), DownloadService.class);
            pauseDownloadIntent.setAction(DownloadService.ACTION_PAUSE_DOWNLOAD);
            PendingIntent pauseDownloadPendingIntent = PendingIntent.getService(getDownloadService(), 0, pauseDownloadIntent, 0);
            NotificationCompat.Action pauseDownloadAction = new NotificationCompat.Action(android.R.drawable.ic_media_pause, "Pause", pauseDownloadPendingIntent);
            notifyBuilder.addAction(pauseDownloadAction);

            // Add Continue download button intent in notification.
            Intent continueDownloadIntent = new Intent(getDownloadService(), DownloadService.class);
            continueDownloadIntent.setAction(DownloadService.ACTION_CONTINUE_DOWNLOAD);
            PendingIntent continueDownloadPendingIntent = PendingIntent.getService(getDownloadService(), 0, continueDownloadIntent, 0);
            NotificationCompat.Action continueDownloadAction = new NotificationCompat.Action(android.R.drawable.ic_media_pause, "Continue", continueDownloadPendingIntent);
            notifyBuilder.addAction(continueDownloadAction);

            // Add Cancel download button intent in notification.
            Intent cancelDownloadIntent = new Intent(getDownloadService(), DownloadService.class);
            cancelDownloadIntent.setAction(DownloadService.ACTION_CANCEL_DOWNLOAD);
            PendingIntent cancelDownloadPendingIntent = PendingIntent.getService(getDownloadService(), 0, cancelDownloadIntent, 0);
            NotificationCompat.Action cancelDownloadAction = new NotificationCompat.Action(android.R.drawable.ic_delete, "Cancel", cancelDownloadPendingIntent);
            notifyBuilder.addAction(cancelDownloadAction);
        }

        Notification notification = notifyBuilder.build();

        return notification;
    }

}

3.7 Layout Xml File.

activity_download.xml

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <EditText
        android:id="@+id/download_manager_url_editor"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Input download file url."/>

    <Button
        android:id="@+id/download_manager_start_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Download"
        android:textAllCaps="false"
        android:enabled="true"/>

</LinearLayout>

3.8 Android Manifest Xml File.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.dev2qa.example">

    <!-- Example require below permissions. -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
       
        <activity android:name=".service.download.DownloadActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".service.download.DownloadService"
            android:enabled="true"
            android:exported="true"></service>
    </application>

</manifest>

Reference

  1. Android Foreground Service Example
  2. Android Play Audio File In Background Service Example
  3. Android Custom Notification Example
(Visited 484 times, 7 visits today)
READ :   Android Update UI From Child Thread Example

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.