This is a working sample, take a look.
Launch the app, press back button and then again come back to test the case of launching another Activity
and coming back.
Make sure to get PARTIAL_WAKE_LOCK
for your IntentService
to ensure that CPU keeps running.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.app.Activity;
import android.app.IntentService;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.content.LocalBroadcastManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
public static final String ID = "id";
private ListView mListView;
private ArrayAdapter<File> mAdapter;
private boolean mReceiversRegistered;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = mListView = (ListView) findViewById(R.id.list);
long id = 0;
File[] files = {getFile(id++),
getFile(id++), getFile(id++), getFile(id++),
getFile(id++), getFile(id++), getFile(id++),
getFile(id++), getFile(id++), getFile(id++),
getFile(id++), getFile(id++), getFile(id++),
getFile(id++), getFile(id++), getFile(id++),
getFile(id++), getFile(id++), getFile(id++),
getFile(id++), getFile(id++), getFile(id++),
getFile(id++), getFile(id++), getFile(id++),
getFile(id++), getFile(id++), getFile(id)};
listView.setAdapter(mAdapter = new ArrayAdapter<File>(this,
R.layout.row, R.id.textView, files) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
updateRow(getItem(position), v);
return v;
}
});
if (savedInstanceState == null) {
Intent intent = new Intent(this, DownloadingService.class);
intent.putParcelableArrayListExtra("files", new ArrayList<File>(Arrays.asList(files)));
startService(intent);
}
registerReceiver();
}
private File getFile(long id) {
return new File(id, "https://someurl/" + id);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver();
}
private void registerReceiver() {
unregisterReceiver();
IntentFilter intentToReceiveFilter = new IntentFilter();
intentToReceiveFilter
.addAction(DownloadingService.PROGRESS_UPDATE_ACTION);
LocalBroadcastManager.getInstance(this).registerReceiver(
mDownloadingProgressReceiver, intentToReceiveFilter);
mReceiversRegistered = true;
}
private void unregisterReceiver() {
if (mReceiversRegistered) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(
mDownloadingProgressReceiver);
mReceiversRegistered = false;
}
}
private void updateRow(final File file, View v) {
ProgressBar bar = (ProgressBar) v.findViewById(R.id.progressBar);
bar.setProgress(file.progress);
TextView tv = (TextView) v.findViewById(R.id.textView);
tv.setText(file.toString());
v.findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent();
i.setAction(DownloadingService.ACTION_CANCEL_DOWNLOAD);
i.putExtra(ID, file.getId());
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(i);
}
});
}
// don't call notifyDatasetChanged() too frequently, have a look at
// following url http://stackoverflow.com/a/19090832/1112882
protected void onProgressUpdate(int position, int progress) {
final ListView listView = mListView;
int first = listView.getFirstVisiblePosition();
int last = listView.getLastVisiblePosition();
mAdapter.getItem(position).progress = progress > 100 ? 100 : progress;
if (position < first || position > last) {
// just update your data set, UI will be updated automatically in next
// getView() call
} else {
View convertView = mListView.getChildAt(position - first);
// this is the convertView that you previously returned in getView
// just fix it (for example:)
updateRow(mAdapter.getItem(position), convertView);
}
}
protected void onProgressUpdateOneShot(int[] positions, int[] progresses) {
for (int i = 0; i < positions.length; i++) {
int position = positions[i];
int progress = progresses[i];
onProgressUpdate(position, progress);
}
}
private final BroadcastReceiver mDownloadingProgressReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
DownloadingService.PROGRESS_UPDATE_ACTION)) {
final boolean oneShot = intent
.getBooleanExtra("oneshot", false);
if (oneShot) {
final int[] progresses = intent
.getIntArrayExtra("progress");
final int[] positions = intent.getIntArrayExtra("position");
onProgressUpdateOneShot(positions, progresses);
} else {
final int progress = intent.getIntExtra("progress", -1);
final int position = intent.getIntExtra("position", -1);
if (position == -1) {
return;
}
onProgressUpdate(position, progress);
}
}
}
};
public static class DownloadingService extends IntentService {
public static String PROGRESS_UPDATE_ACTION = DownloadingService.class
.getName() + ".progress_update";
private static final String ACTION_CANCEL_DOWNLOAD = DownloadingService.class
.getName() + "action_cancel_download";
private boolean mIsAlreadyRunning;
private boolean mReceiversRegistered;
private ExecutorService mExec;
private CompletionService<NoResultType> mEcs;
private LocalBroadcastManager mBroadcastManager;
private List<DownloadTask> mTasks;
private static final long INTERVAL_BROADCAST = 800;
private long mLastUpdate = 0;
public DownloadingService() {
super("DownloadingService");
mExec = Executors.newFixedThreadPool( /* only 5 at a time */5);
mEcs = new ExecutorCompletionService<NoResultType>(mExec);
mBroadcastManager = LocalBroadcastManager.getInstance(this);
mTasks = new ArrayList<MainActivity.DownloadingService.DownloadTask>();
}
@Override
public void onCreate() {
super.onCreate();
registerReceiver();
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (mIsAlreadyRunning) {
publishCurrentProgressOneShot(true);
}
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(Intent intent) {
if (mIsAlreadyRunning) {
return;
}
mIsAlreadyRunning = true;
ArrayList<File> files = intent.getParcelableArrayListExtra("files");
final Collection<DownloadTask> tasks = mTasks;
int index = 0;
for (File file : files) {
DownloadTask yt1 = new DownloadTask(index++, file);
tasks.add(yt1);
}
for (DownloadTask t : tasks) {
mEcs.submit(t);
}
// wait for finish
int n = tasks.size();
for (int i = 0; i < n; ++i) {
NoResultType r;
try {
r = mEcs.take().get();
if (r != null) {
// use you result here
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
// send a last broadcast
publishCurrentProgressOneShot(true);
mExec.shutdown();
}
private void publishCurrentProgressOneShot(boolean forced) {
if (forced
|| System.currentTimeMillis() - mLastUpdate > INTERVAL_BROADCAST) {
mLastUpdate = System.currentTimeMillis();
final List<DownloadTask> tasks = mTasks;
int[] positions = new int[tasks.size()];
int[] progresses = new int[tasks.size()];
for (int i = 0; i < tasks.size(); i++) {
DownloadTask t = tasks.get(i);
positions[i] = t.mPosition;
progresses[i] = t.mProgress;
}
publishProgress(positions, progresses);
}
}
private void publishCurrentProgressOneShot() {
publishCurrentProgressOneShot(false);
}
private synchronized void publishProgress(int[] positions,
int[] progresses) {
Intent i = new Intent();
i.setAction(PROGRESS_UPDATE_ACTION);
i.putExtra("position", positions);
i.putExtra("progress", progresses);
i.putExtra("oneshot", true);
mBroadcastManager.sendBroadcast(i);
}
// following methods can also be used but will cause lots of broadcasts
private void publishCurrentProgress() {
final Collection<DownloadTask> tasks = mTasks;
for (DownloadTask t : tasks) {
publishProgress(t.mPosition, t.mProgress);
}
}
private synchronized void publishProgress(int position, int progress) {
Intent i = new Intent();
i.setAction(PROGRESS_UPDATE_ACTION);
i.putExtra("progress", progress);
i.putExtra("position", position);
mBroadcastManager.sendBroadcast(i);
}