Reputation: 670
I'm following a tutorial to download files using the Retrofit library.
My app's UI has several different buttons which download different files using the above method. The problem is when someone presses another button after pressing the first, it's queued and starts after the first finishes. I want it to start right away simultaneously.
Here's the code for the DownloadService
:
public class DownloadService extends IntentService {
public DownloadService() {
super("Download Service");
}
private int totalFileSize;
private NotificationCompat.Builder notificationBuilder;
private NotificationManager notificationManager;
@Override
protected void onHandleIntent(Intent intent) {
MyApp x = (MyApp)getApplicationContext();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_file_download_deep_orange_a400_18dp)
.setContentTitle("Downloading")
.setContentText("Please wait...")
.setAutoCancel(true);
notificationManager.notify(x.ID, notificationBuilder.build());
Log.i("Paras", "onHandleIntent: " + x.filename + x.url);
initDownload(x.filename,x.url,x.ID);
}
private void initDownload(String filename, String url, int id) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://dl.dropboxusercontent.com/")
.build();
RequestInterface.RetrofitInterface retrofitInterface = retrofit.create(RequestInterface.RetrofitInterface.class);
Call<ResponseBody> request = retrofitInterface.downloadFile(url);
try {
downloadFile(request.execute().body(),filename,id);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void downloadFile(ResponseBody body, String filename,int id) throws IOException {
int count;
byte data[] = new byte[1024 * 4];
long fileSize = body.contentLength();
InputStream bis = new BufferedInputStream(body.byteStream(), 1024 * 8);
File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename);
OutputStream output = new FileOutputStream(outputFile);
long total = 0;
long startTime = System.currentTimeMillis();
int timeCount = 1;
while ((count = bis.read(data)) != -1) {
total += count;
totalFileSize = (int) (fileSize / (Math.pow(1, 2))) / 1000;
double current = Math.round(total / (Math.pow(1, 2))) / 1000;
int progress = (int) ((total * 100) / fileSize);
long currentTime = System.currentTimeMillis() - startTime;
Download download = new Download();
download.setTotalFileSize(totalFileSize);
if (currentTime > 1000 * timeCount) {
download.setCurrentFileSize((int) current);
download.setProgress(progress);
sendNotification(download,id);
timeCount++;
}
output.write(data, 0, count);
}
onDownloadComplete(filename,id);
output.flush();
output.close();
bis.close();
}
private void sendNotification(Download download, int id) {
sendIntent(download);
notificationBuilder.setProgress(100, download.getProgress(), false);
notificationBuilder.setContentText("Downloading file " + download.getCurrentFileSize() + "/" + totalFileSize + " KB");
notificationManager.notify(id, notificationBuilder.build());
}
private void sendIntent(Download download) {
Intent intent = new Intent(subject.MESSAGE_PROGRESS);
intent.putExtra("download", download);
LocalBroadcastManager.getInstance(DownloadService.this).sendBroadcast(intent);
}
private void onDownloadComplete(String filename,int id) {
try {
Download download = new Download();
download.setProgress(100);
sendIntent(download);
notificationManager.cancel(id);
notificationBuilder.setProgress(0, 0, false);
notificationBuilder.setContentText("Tap to open");
notificationManager.notify(id, notificationBuilder.build());
String path1 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + filename;
File file = new File(path1);
Uri uri_path = Uri.fromFile(file);
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension
(MimeTypeMap.getFileExtensionFromUrl(path1));
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
intent.setType(mimeType);
intent.setDataAndType(uri_path, mimeType);
PendingIntent pIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), intent, 0);
String string = filename;
notificationBuilder
.setContentIntent(pIntent)
.setAutoCancel(true)
.setContentTitle(string + " Downloaded");
Log.i("Paras", "onDownloadComplete: " + string);
notificationManager.notify(id, notificationBuilder.build());
} catch (Exception ex) {
}
}
@Override
public void onTaskRemoved(Intent rootIntent) {
}
}
Then I read about IntentService
and Service
classes. Do Service
classes allow simultaneous downloads? I tried something like this:
public class DownloadService extends Service {
public DownloadService() {
super();
}
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
MyApp x = (MyApp)getApplicationContext();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationBuilder = new NotificationCompat.Builder(getBaseContext())
.setSmallIcon(R.drawable.ic_file_download_deep_orange_a400_18dp)
.setContentTitle("Downloading")
.setContentText("Please wait...")
.setAutoCancel(true);
notificationManager.notify(x.ID, notificationBuilder.build());
Log.i("Paras", "onHandleIntent: " + x.filename + x.url);
initDownload(x.filename,x.url,x.ID);
}
}
@Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
MyApp x = (MyApp)getApplicationContext();
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj= intent.putExtra("ID",x.ID);
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private int totalFileSize;
private NotificationCompat.Builder notificationBuilder;
private NotificationManager notificationManager;
private void initDownload(String filename, String url, int id) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://dl.dropboxusercontent.com/")
.build();
RequestInterface.RetrofitInterface retrofitInterface = retrofit.create(RequestInterface.RetrofitInterface.class);
Call<ResponseBody> request = retrofitInterface.downloadFile(url);
try {
downloadFile(request.execute().body(),filename,id);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void downloadFile(ResponseBody body, String filename,int id) throws IOException {
int count;
byte data[] = new byte[1024 * 4];
long fileSize = body.contentLength();
InputStream bis = new BufferedInputStream(body.byteStream(), 1024 * 8);
File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename);
OutputStream output = new FileOutputStream(outputFile);
long total = 0;
long startTime = System.currentTimeMillis();
int timeCount = 1;
while ((count = bis.read(data)) != -1) {
total += count;
totalFileSize = (int) (fileSize / (Math.pow(1, 2))) / 1000;
double current = Math.round(total / (Math.pow(1, 2))) / 1000;
int progress = (int) ((total * 100) / fileSize);
long currentTime = System.currentTimeMillis() - startTime;
Download download = new Download();
download.setTotalFileSize(totalFileSize);
if (currentTime > 1000 * timeCount) {
download.setCurrentFileSize((int) current);
download.setProgress(progress);
sendNotification(download,id);
timeCount++;
}
output.write(data, 0, count);
}
onDownloadComplete(filename,id);
output.flush();
output.close();
bis.close();
}
private void sendNotification(Download download, int id) {
sendIntent(download);
notificationBuilder.setProgress(100, download.getProgress(), false);
notificationBuilder.setContentText("Downloading file " + download.getCurrentFileSize() + "/" + totalFileSize + " KB");
notificationManager.notify(id, notificationBuilder.build());
}
private void sendIntent(Download download) {
Intent intent = new Intent(subject.MESSAGE_PROGRESS);
intent.putExtra("download", download);
LocalBroadcastManager.getInstance(DownloadService.this).sendBroadcast(intent);
}
private void onDownloadComplete(String filename,int id) {
try {
Download download = new Download();
download.setProgress(100);
sendIntent(download);
notificationManager.cancel(id);
notificationBuilder.setProgress(0, 0, false);
notificationBuilder.setContentText("Tap to open");
notificationManager.notify(id, notificationBuilder.build());
String path1 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + filename;
File file = new File(path1);
Uri uri_path = Uri.fromFile(file);
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension
(MimeTypeMap.getFileExtensionFromUrl(path1));
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
intent.setType(mimeType);
intent.setDataAndType(uri_path, mimeType);
PendingIntent pIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), intent, 0);
String string = filename;
notificationBuilder
.setContentIntent(pIntent)
.setAutoCancel(true)
.setContentTitle(string + " Downloaded");
Log.i("Paras", "onDownloadComplete: " + string);
notificationManager.notify(id, notificationBuilder.build());
} catch (Exception ex) {
}
}
@Override
public void onTaskRemoved(Intent rootIntent) {
}
}
But it didn't work. Any clues what I should do? I'm ready to give more details if required.
Edit 1: DownloadService runs on a function "startDownload" which is executed by various buttons. As you can see in 2nd code, class Extends Service. There is one thread which handles all those button clicks. If you look in comments, it's suggested that I should use Service and different threads for all those clicks. Now how can I make so many threads programmatically. There are almost 40 buttons which make use of DownloadService.
Upvotes: 2
Views: 3283
Reputation: 670
Thanks to @Lxu, I have got it working. So IntentService
is meant to do one task at a time and can't do multiple tasks simultaneously. We should use Service
instead. It allows multiple tasks to be performed simultaneously. We can create multiple threads inside the Service
which will be executed simultaneously. My problem got solved by putting all the code of onCreate()
to onStartCommand()
. When a service is called for the first time, onCreate()
is called and after that, it's not called anymore no matter how many times service is called. On every service call, onStartCommand()
is executed which creates new thread every time. That's it.
Here's the complete code:
public class DownloadService extends Service {
public DownloadService() {
super();
}
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
int id1;
int id2;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
MyApp x = (MyApp)getApplicationContext();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationBuilder = new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(R.drawable.ic_file_download_deep_orange_a400_18dp)
.setContentTitle("Downloading")
.setContentText("Please wait...")
.setAutoCancel(true);
notificationManager.notify(x.ID, notificationBuilder.build());
Log.i("Paras", "onHandleIntent: " + x.filename + x.url + " " + x.ID);
initDownload(x.filename,x.url,x.ID);
}
}
@Override
public void onCreate() {
// Get the HandlerThread's Looper and use it for our Handler
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
MyApp x = (MyApp)getApplicationContext();
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private int totalFileSize;
private NotificationCompat.Builder notificationBuilder;
private NotificationManager notificationManager;
private void initDownload(String filename, String url, int id) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://dl.dropboxusercontent.com/")
.build();
RequestInterface.RetrofitInterface retrofitInterface = retrofit.create(RequestInterface.RetrofitInterface.class);
Call<ResponseBody> request = retrofitInterface.downloadFile(url);
try {
downloadFile(request.execute().body(),filename,id);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void downloadFile(ResponseBody body, String filename,int id) throws IOException {
int count;
byte data[] = new byte[1024 * 4];
long fileSize = body.contentLength();
InputStream bis = new BufferedInputStream(body.byteStream(), 1024 * 8);
File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename);
OutputStream output = new FileOutputStream(outputFile);
long total = 0;
long startTime = System.currentTimeMillis();
int timeCount = 1;
while ((count = bis.read(data)) != -1) {
total += count;
totalFileSize = (int) (fileSize / (Math.pow(1, 2))) / 1000;
double current = Math.round(total / (Math.pow(1, 2))) / 1000;
int progress = (int) ((total * 100) / fileSize);
long currentTime = System.currentTimeMillis() - startTime;
Download download = new Download();
download.setTotalFileSize(totalFileSize);
if (currentTime > 1000 * timeCount) {
download.setCurrentFileSize((int) current);
download.setProgress(progress);
sendNotification(download,id);
timeCount++;
}
output.write(data, 0, count);
}
onDownloadComplete(filename,id);
output.flush();
output.close();
bis.close();
}
private void sendNotification(Download download, int id) {
sendIntent(download,id);
notificationBuilder.setProgress(100, download.getProgress(), false)
.setContentTitle("Downloading");
notificationBuilder.setContentText("Downloading file " + download.getCurrentFileSize() + "/" + totalFileSize + " KB");
notificationManager.notify(id, notificationBuilder.build());
}
private void sendIntent(Download download, int id) {
Intent intent = new Intent(subject.MESSAGE_PROGRESS);
intent.putExtra("download", download);
LocalBroadcastManager.getInstance(DownloadService.this).sendBroadcast(intent);
}
private void onDownloadComplete(String filename,int id) {
try {
Download download = new Download();
download.setProgress(100);
sendIntent(download,id);
notificationManager.cancel(id);
notificationBuilder.setProgress(0, 0, false);
notificationBuilder.setContentText("Tap to open");
notificationManager.notify(id, notificationBuilder.build());
String path1 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + filename;
File file = new File(path1);
Uri uri_path = Uri.fromFile(file);
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension
(MimeTypeMap.getFileExtensionFromUrl(path1));
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
intent.setType(mimeType);
intent.setDataAndType(uri_path, mimeType);
PendingIntent pIntent = PendingIntent.getActivity(this,(int) System.currentTimeMillis(), intent, 0);
String string = filename;
notificationBuilder
.setContentIntent(pIntent)
.setAutoCancel(true)
.setContentTitle(string + " Downloaded");
Log.i("Paras", "onDownloadComplete: " + string);
notificationManager.notify(id, notificationBuilder.build());
}catch (Exception ex){
}
}
@Override
public void onTaskRemoved(Intent rootIntent) {
}
}
Upvotes: 3