Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Multiple download pause resume in listview with progress update

I am trying to download multiple files in listview with progressbar. What I achieved is, I can start a particular download, pause/resume it using AsyncTask and progress bar is updated(for single file), this part works well

My problem is I am not able to download multiple files simultaneously and when I leave the listview to another screen although my download is going on in the background but progress is not updated, progress bar shows 0 progress as if it is not downloading but its been downloading in the background.

like image 720
ingsaurabh Avatar asked Nov 15 '14 10:11

ingsaurabh


2 Answers

Finally I found the answer which was much simpler than I thought, here it is as follows

  1. Create a service having Asynctask for downloading and hashtable of values(url, Asynctask)
  2. Pass the value(url, Asynctask) when a list item is clicked and check whether that hashtable contain the value already if yes cancel that Asynctask task if no add it to hashtable and start Asynctask
  3. now for updating progress in my adapter I ran a thread which iterate over hashtable and passes the value using BroadcastListener.
  4. And in activity intercept the broadcast and depending on the ListItem visible update the progress

PS: If anybody needs some code I can provide basic code of the description explained above

public class DownloadingService extends Service {
public static String PROGRESS_UPDATE_ACTION = DownloadingService.class.getName() + ".progress";

private static final long INTERVAL_BROADCAST = 800;
private long mLastUpdate = 0;
private Hashtable<String, DownloadFile> downloadTable;

private LocalBroadcastManager broadcastManager;

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    MessageEntity entityRecieved = (MessageEntity) intent.getSerializableExtra("ENTITY");
    queueDownload(entityRecieved);
    return super.onStartCommand(intent, flags, startId);
}

private void queueDownload(MessageEntity entityRecieved){
    if (downloadTable.containsKey(entityRecieved.getPacketID())) {
        DownloadFile downloadFile = downloadTable.get(entityRecieved.getPacketID());
        if (downloadFile.isCancelled()) {
            downloadFile = new DownloadFile(entityRecieved);
            downloadTable.put(entityRecieved.getPacketID(), downloadFile);
            startDownloadFileTask(downloadFile);
        } else {
            downloadFile.cancel(true);
            downloadTable.remove(entityRecieved.getPacketID());
        }

    } else {
        DownloadFile downloadFile = new DownloadFile(entityRecieved);
        downloadTable.put(entityRecieved.getPacketID(), downloadFile);
        startDownloadFileTask(downloadFile);
    }
}

@Override
public void onCreate() {
    super.onCreate();
    downloadTable = new Hashtable<String, DownloadFile>();
    broadcastManager = LocalBroadcastManager.getInstance(this);
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
void startDownloadFileTask(DownloadFile asyncTask) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    else
        asyncTask.execute();
}

private void publishCurrentProgressOneShot(boolean forced) {
    if (forced || System.currentTimeMillis() - mLastUpdate > INTERVAL_BROADCAST) {
        mLastUpdate = System.currentTimeMillis();
        int[] progresses = new int[downloadTable.size()];
        String[] packetIds = new String[downloadTable.size()];
        int index = 0;
        Enumeration<String> enumKey = downloadTable.keys();
        while (enumKey.hasMoreElements()) {
            String key = enumKey.nextElement();
            int val = downloadTable.get(key).progress;
            progresses[index] = val;
            packetIds[index++] = key;
        }
    Intent i = new Intent();
    i.setAction(PROGRESS_UPDATE_ACTION);
    i.putExtra("packetIds", packetIds);
    i.putExtra("progress", progresses);
    mBroadcastManager.sendBroadcast(i);
}

class DownloadFile extends AsyncTask<Void, Integer, Void> {
    private MessageEntity entity;
    private File file;
    private int progress;

    public DownloadFile(MessageEntity entity) {
        this.entity = entity;
    }

    @Override
    protected Void doInBackground(Void... arg0) {
        String filename = entity.getMediaURL().substring(entity.getMediaURL().lastIndexOf('/') + 1);
        file = new File(FileUtil.getAppStorageDir().getPath(), filename);
        downloadFile(entity.getMediaURL(), file); 
        return null;
    }

    public String downloadFile(String download_file_path, File file) {
        int downloadedSize = 0;
        int totalSize = 0;
        try {
            // download the file here
            while ((bufferLength = inputStream.read(buffer)) > 0 && !isCancelled()) {

                progress = percentage;
                publishCurrentProgressOneShot(true);
            }


        } catch (final Exception e) {
            return null;
        }

        return file.getPath();
    }

}
like image 200
ingsaurabh Avatar answered Oct 29 '22 19:10

ingsaurabh


On big problem with AsynchTask is when you finish its activity, AsynchTask looses it's track with your UI. After that when you return back to that activity the progressBar is not updating even if the download progress still running in background. In fact that AsynchTask is not belong to the new Activity you lunched so the new instance of progress bar in new Activity will not updating. To fix this problem I suggest you:

1- Run a thread with a timerTask in onResume() which updates ur progressbar with values updating from the AsyncTask running background. Something like this:

private void updateProgressBar(){
    Runnable runnable = new updateProgress();
    background  = new Thread(runnable);
    background.start();
}

public class updateProgress implements Runnable {
    public void run() {
        while(Thread.currentThread()==background)
            try {
                Thread.sleep(1000); 
                Message msg = new Message();
                progress = getProgressPercentage();        
                handler.sendMessage(msg);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (Exception e) {
          }
      }
}
private Handler handler =  new Handler(){
    @Override
    public void handleMessage(Message msg) {
        progress.setProgress(msg.what);
    }
};

and when your activity is not visible you must destroy the thread:

private void destroyRunningThreads()
{
    if(background!=null)
    {
        background.interrupt();
        background=null;
    }
}

2- Define a global static boolean variable. Set it true in onPreExecute and in onPostExecute set it to false. It shows that you are downloading or not, so you can check if the variable is equal to true, show the previous progressbar dialog.(you can do something like this with an integer value-or array of integers- in order to show the update percentage for each download progress).

3- The last way I personally used is to show the download progress in Notification Bar and in my list view I just show that it is downloading right now or not.(using 2nd method with a boolean values). In this way even if you finish the activity the notification bar is still updated with download progress.

like image 43
Milad Faridnia Avatar answered Oct 29 '22 19:10

Milad Faridnia