0

Tl;dr How to know when an IntentService has finished downloading upon returning to the Activity which listens to its result using a BroadcastReceiver?


I'm moving to implementing data downloading to IntentServices, and notifying when the task has finished using BroadcastReceivers.

I generally start the service in my Activity:

IntentFilter intentFilter = DownloadDataService.startDownloadData(this);
getLocalBroadcastManager().registerReceiver(mBroadcastReceiver, intentFilter);

The part that starts the Service:

/**
 * Starts the service and returns an IntentFilter to subscribe a BroadcastReceiver on. 
 * When the task has finished, a broadcast for returned IntentFilter is sent,
 * containing downloaded data.
 */
public static IntentFilter startDownloadData(final Context context) {
    Intent intent = new Intent(context, DownloadDataService.class);
    intent.setAction(ACTION_DOWNLOAD_DATA);
    context.startService(intent);

    return new IntentFilter(ACTION_DOWNLOAD_DATA);
}

And of course, onHandleIntent(Intent) (simplified):

@Override
protected void onHandleIntent(final Intent intent){
    Data data = downloadData();
    Intent intent = new Intent(ACTION_DOWNLOAD_DATA);
    intent.putExtra(DATA, data);
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

This all works well, and I can keep states in my Activity to know for example after an orientation change whether I was waiting for a download data result:

@Override
public void onResume(){
    super.onResume();
    if (mState == State.DOWNLOADING) {
        Log.v(TAG, "Registering receiver for existing download data");
        IntentFilter intentFilter = DownloadDataService.getIntentFilter();
        getLocalBroadcastManager().registerReceiver(mBroadcastReceiver, intentFilter);
    }
}

Great, now I can also handle orientation changes. Only one problem left:

  • Activity starts the DownloadDataService
  • User moves away from the Activity
  • DownloadDataService broadcasts its done message (which is not received by the Activity due to unregisterReceiver in onStop())
  • User moves back into the Activity
  • Activity still thinks it's waiting for the DownloadDataService, and does nothing.

How can I compensate for this?


Note that I do not have any persistence like databases for storing the downloaded data. The Activity retrieves the data from the broadcasted Intent.

Note #2: There is this answer to the question of how to know whether a Service is running. Although this might work, it is explicitly stated that that method is for debugging or implementing service management type user interfaces.

Community
  • 1
  • 1
nhaarman
  • 98,571
  • 55
  • 246
  • 278

2 Answers2

1

use sendStickyBroadcast to send a sticky broadcast. This broadcast is held by the system.

Arno van Lieshout
  • 1,570
  • 1
  • 13
  • 19
  • I was just about to suggest that in a comment when you posted your answer. – Squonk Apr 29 '14 at 11:36
  • Ah, yes. But this doesn't work with `LocalBroadcastManager`, right? – nhaarman Apr 29 '14 at 11:38
  • Plus, sticky broadcasts are discouraged [as well](https://groups.google.com/forum/#!topic/android-developers/8341SaXhvmY). – nhaarman Apr 29 '14 at 11:44
  • @NiekHaarman : I suppose it depends on if the data is sensitive. If it's not then it wouldn't bother me to use a sticky broadcast with a standard BroadcastReceiver. If the data is sensitive then I'm not sure you have many other choices. The only way I can think of is to maintain a download state in SharedPreferences indicating the Service is downloading or not (perhaps with a last download time) and to persist the data somewhere. Used in combination with a BroadcastReceiver you'd either get the data in real-time or later. – Squonk Apr 29 '14 at 12:12
  • Or maybe register the broadcast reciever in an application derived class. Or you could let the dowload service register a flag in the shared preferences and check that when you resume the Activity – Arno van Lieshout Apr 29 '14 at 14:19
0

I wasn't really convinced by using SharedPreferences, static variables and other 'hacky' solutions.

I did find however that you can supply a ResultReceiver - which is parcelable - which you can use to notify your task is finished. It receives a Handler to specify the thread the result is handled on.

The advantage of this, is that you can save the ResultReceiver during onSaveInstanceState. Using some clever tricks you can certainly make this work. I have created an experimental library which facilitates this tactic: https://github.com/nhaarman/Ergo

nhaarman
  • 98,571
  • 55
  • 246
  • 278