1

I have an activity and it has two listview. First listview has musics. Second listview shows downloading items and each downloading item shows progress bar for download percent. If user click music item, I add an downloading item to second listview and start download file with AsyncTask. User can click more than one music, it means downloading multiple files simultaneously. I can download files with AsyncTask. But I want to download files in service because of downloads must continue even application close.

I dont know how can I download multiple files simultaneously with service. Which steps should I follow? Is there any tutorial for this?

Alexander
  • 1,720
  • 4
  • 22
  • 40
  • 1
    Not an answer, but something to think about. If this is your server, you probably want to avoid excessive download of a lot of files simultaneously. – Lukasz Feb 10 '17 at 07:25

3 Answers3

8

I have implemented the same in one of my production apps:

  1. Create a table to add the items which you want to download. Sorting should be based on the time when item was added for download. Consider this as a queue. Also add one column to check if a file is already downloaed or not. If downloaded then remove it from the queue.
  2. Create an IntentService which will be responsible for downloading data in background even if application is closed. This Service should keep running until the table queue is not empty. It should be started only when a new item is added, or when network connection state changes from dead to live. Within this Service you should check if the queue is empty or not. If empty fetch N number of rows from table to add in queue(ExecutorService) for download. To check the chnage of network state create a BroadcastReceiver with ‎LocalBroadcastManager.
  3. Now the code that you can write in DownloadIntentService. Try to modify as per your needs:

    private static int NUMBER_OF_CORES =
        Runtime.getRuntime().availableProcessors() * 2;
    private final ExecutorService executorService;
    private final ExecutorCompletionService<DownloadModel> executorCompletionService;
    
    public DownloadIntentService() {
    super("DownloadIntentService");
    executorService = Executors.newFixedThreadPool(NUMBER_OF_CORES);
    executorCompletionService = new ExecutorCompletionService<>(executorService);
    }
    
    ......
    @Override
    protected void onHandleIntent(Intent intent) {  
    
    //Check to see for storage permission
    if (ActivityCompat.checkSelfPermission(this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
            && ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
            == PackageManager.PERMISSION_GRANTED) {
     .....
     //Now create job queue
     ....
    //Fetch rows to download from table
    DownloadQuery downloadQuery = new DownloadQuery(DownloadIntentService.this);
    
    ArrayList<DownloadModel> downloadModelArrayList = downloadQuery.getDownload(DownloadContract.IFileDownloadStatus.NOT_DOWNLOADED,
                0, NUMBER_OF_CORES);
    //Check if queue contains any item
    if (downloadModelArrayList.size() > 0) {
    if (!executorService.isShutdown())
                        executorCompletionService.submit(new DownloadWorkerCallable(**Your download model from table to download**, DownloadIntentService.this));
    }
    .....
    //Don't forget to shutdown the executorService
    }
    }
    

This DownloadWorkerCallable is implementing Runnable which actually download file.

Anurag Singh
  • 6,140
  • 2
  • 31
  • 47
  • @Alexander What do you say? – Anurag Singh Feb 10 '17 at 07:46
  • Thanks. But after start service user can add new download item any time. On each download item added I need to start download for new download item. If I'm not wrong in your example is getting all download items at the begining of start service? But in my code after service start user can add new download item – Alexander Feb 10 '17 at 11:10
  • Once a IntentService starts it will keep running until task or onHandleIntent() is not done with it. I fetch items in onHandleIntent(). Even if onHandleIntent() is not finished a new request to start download will be in queue and DownloadIntentService() will not be started again. There is another approach also you can fetch items in do while(if more item in table) from onHandleIntent() – Anurag Singh Feb 10 '17 at 11:14
  • For example; user clicked two music and service finished downloading those items and service stopped. one minute after service stop user clicked new music item for download. Should I start service again? – Alexander Feb 10 '17 at 11:19
  • Yes, exactly start again. And if it helped do vote or accept answer for others to be helpful. – Anurag Singh Feb 10 '17 at 11:20
  • Do let me know if in doubt. Thanks. – Anurag Singh Feb 10 '17 at 11:32
  • I researched more about this. And I learned that I should use normal Service instead of IntentService. IntentService stops after it finish its job. I read that "The IntentService cannot run tasks in parallel" in http://stackoverflow.com/questions/15524280/service-vs-intentservice. Actually I am confused – Alexander Feb 10 '17 at 13:28
  • 1
    It's the advantage that it's stops automatically and gets in queue also. Resources are freed. Long running tasks can be performed in IntenetService and in your case it's downloading. If you will look deeper in the code I am using ExecutorService which runs in async. DownloadWorkerCallable is what it consumes. – Anurag Singh Feb 10 '17 at 13:34
  • I'm working on your example. I use EventBus for communicate activity and service. I'm getting new download item with EventBus message. in onHandleIntent waiting one minute for new downloads ... Could you check Is my way is correct or not? Thanks. https://gist.github.com/serkandaglioglu/dd679cfedcd17c293926b23c3cd773a3 – Alexander Feb 13 '17 at 11:28
  • I would not suggest to do that. Whenever you need to submit a new download item for download, just insert that item in table with current timestamp. Start the service with no bundle parameters. And let the onHandleIntent decide which item to pick as shown in the above example. Use EventBus from Service to notify the RecyclerView when the tem to be download is in queue or has been picked from table is in started downloading, percentage downloading, finished, failed state, etc. – Anurag Singh Feb 13 '17 at 14:33
  • If I follow your way I have to start service for each download item. At the end of onHandleIntent I shutdown executorService. It is mean executorService works for only one thread because we shutdown it after got new download item. So what is the purpose of executorService? – Alexander Feb 13 '17 at 14:48
  • Before shutting down you need to check if there are more items pending in table for downloading. If yes then pick those items and continue otherwise stop executorService. – Anurag Singh Feb 13 '17 at 15:47
  • I used normal Service, I stop in on main activity destroy. I didn't use ExecutorCompletionService. Service gets new download item onStartCommand. In onStartCommand "executorService.execute(new TestThread(msg));" Thanks for your help – Alexander Feb 17 '17 at 13:49
  • But if you are going to stop the service in activity, how would the download continue in background even if you app is removed from recents. – Anurag Singh Feb 17 '17 at 14:02
  • I tested it. If I stop service thread continue to work. Actually there is no difference between IntentService and Service about what you said. The difference is intent service stop after call threads, service stop by manually on main activity destroy – Alexander Feb 17 '17 at 14:05
  • I am sorry for the wrong information. I didn't gave deep thought on your last comment about the executor thread. It is because of that only it works. Regarding stop you and I are correct. And Regarding difference between these two I would not like to discuss in detail here. But Thanks for the information. I appreciate it. Cheers. – Anurag Singh Feb 17 '17 at 15:23
2

You should use ThreadPool or better EventBus. AsyncTask is not good choice for multiple downloads.

0

This will really help you.you have executeOnExecutor in android

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
 private void StartAsyncTaskInParallel(MyAsyncTask task) {
     if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
         task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     else
         task.execute();
 }

for more details http://android-er.blogspot.in/2014/04/run-multi-asynctask-as-same-time.html

you can use 5-6 different urls to download it simultaneously.

santosh kumar
  • 2,952
  • 1
  • 15
  • 27