24

Background

Starting with Honeycomb (API 11) , Android has a feature to allow the broadcastReceiver run in an async way, providing it about 10 seconds before it assumes it can kill its process, using a method called "goAsync" :

This can be called by an application in onReceive(Context, Intent) to allow it to keep the broadcast active after returning from that function. This does not change the expectation of being relatively responsive to the broadcast (finishing it within 10s), but does allow the implementation to move work related to it over to another thread to avoid glitching the main UI thread due to disk IO.

The problem

I've searched in many places and didn't find any sample or tutorial of how to use it.

Not only that, but the method returns a PendingIntent instance which I'm not sure what to do with it:

Returns a BroadcastReceiver.PendingResult representing the result of the active broadcast. The BroadcastRecord itself is no longer active; all data and other interaction must go through BroadcastReceiver.PendingResult APIs. The PendingResult.finish() method must be called once processing of the broadcast is done.

The question

How do you use this method?

What is the PendingIntent that is returned by it, and what should I do with it?

android developer
  • 106,412
  • 122
  • 641
  • 1,128

2 Answers2

41

You can find short explanation here.

Use goAsync() if you want to handoff the processing inside of your BroadcastReceiver's onReceive() method to another thread. The onReceive() method can then be finished there. The PendingResult is passed to the new thread and you have to call PendingResult.finish() to actually inform the system that this receiver can be recycled.

For example:

final PendingResult result = goAsync();
Thread thread = new Thread() {
   public void run() {
      int i;
      // Do processing
      result.setResultCode(i);
      result.finish();
   }
};
thread.start();
azizbekian
  • 53,978
  • 11
  • 145
  • 225
Broatian
  • 791
  • 8
  • 10
  • and it's guaranteed that the thread will be able to live for at least 10 seconds (if I want to) , right? Also, did you try this code? what should be the resultCode? what is it used for? – android developer Mar 30 '14 at 07:33
  • 3
    The total time to return from the onReceive method is 10 seconds. That means your thread can run a maximum of 10 seconds minus the time you spent in the onReceive method before starting the thread.Haven't tried the code as it's there – Broatian Mar 30 '14 at 07:40
  • 8
    setResult is not needed it's only used to pass data between receivers during an ordered broadcast. You can leave it out. – Broatian Mar 30 '14 at 07:51
  • 1
    I don't get the setResultCode purpose and how to use it. How would you pass it forward ? Also, did you try this code? – android developer Mar 30 '14 at 08:00
  • I just tested this code and it works. As to how to use setResult(). The thing to take home from this is that the PendingResult gives you an interface to what you would usually be able to do inside of your BroadcastReceiver. That means setResult of the PendingResult does the same as if you called setResult inside of your onReceive method. – Broatian Mar 30 '14 at 08:17
  • but what is its (setResult) purpose? can you please give an example of when and how to use it? – android developer Mar 30 '14 at 08:29
  • 2
    An ordered broadcast goes through every registered broadcast receiver in sequence. The order is defined by the priority attribute of the intent filter. Each receiver can then use methods like setResult() to pass data to the following receiver or even stop the broadcast altogether. Action. ACTION_NEW_OUTGOING_CALL is an ordered broadcast. You can use setResult to modify the extra EXTRA_PHONE_NUMBER which will change the phone number that will be dialed. – Broatian Mar 30 '14 at 09:14
  • I see, ok , so if we stay with your example, in case you wish to change the phone number, you change the extras, but what would you do with the setResult method? – android developer Mar 30 '14 at 09:38
  • 1
    The setResult method takes a int, String and a Bundle. In case of the phone number you pass the bundle containing the phone number this way. It is very off topic from the original question though. The question was about how to use goAsync and less about how BroadcastReceiver work in general. Please mark as solved if that part was answered. – Broatian Mar 30 '14 at 16:36
  • 1
    oh, i didn't know it has overloading method. ok thanks. for all your trouble and time, you get "V" and "+1" . :) – android developer Mar 30 '14 at 18:37
  • AsyncTask is deprecated. What is the modified solution now? – user1090751 Oct 05 '20 at 14:47
4

In kotlin you can write an extension function on BroadcastReceiver:

/**
 * Run work asynchronously from a [BroadcastReceiver].
 */
fun BroadcastReceiver.goAsync(
    coroutineScope: CoroutineScope,
    dispatcher: CoroutineDispatcher,
    block: suspend () -> Unit
) {
    val pendingResult = goAsync()
    coroutineScope.launch(dispatcher) {
        block()
        pendingResult.finish()
    }
}

After that inside your broadcast receiver you can do the following:

class AlarmBroadcastReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        // Code here runs on the main thread

        goAsync(GlobalScope, Dispatchers.Default) {
            // The code here will run on the background by the default dispatcher on the global scope
            // If your code here touches the IO, then you can use Dispatchers.IO instead
        }
    }
Mahmoud
  • 1,672
  • 1
  • 18
  • 23
  • Why the need for parameters? It's always the same parameters. Instead, it should only have the parameter of what to do there. Also, I don't think you should use coroutines here. If there are some threads in its thread pool that are being used, it will wait for them to finish first. – android developer Apr 07 '20 at 08:54
  • For params maybe you have a managed coroutine scope you want to use in this case. Using global scope isn't recommended because it doesn't support structured concurrency. Using coroutine simplifies the code, you can execute the code immediately without waiting using a specific dispatcher. – Mahmoud Apr 08 '20 at 07:15
  • I don't understand. You said it's not recommended, but that's what you used. Does GlobalScope always create a new thread? Sorry but I'm very noob with this topic... It seems like over-complexity for such an easy thing to do... – android developer Apr 08 '20 at 08:27
  • Yeah, It's not recommended because it doesn't support structured concurrency. https://medium.com/@elizarov/the-reason-to-avoid-globalscope-835337445abc I used it here as an example. – Mahmoud Apr 08 '20 at 13:52
  • 1
    So you wrote code, and now you say it's not recommended to use it? – android developer Apr 08 '20 at 18:30
  • 2
    I would suggest wrapping the block in a `try finally` to ensure `finish()` is always called in the end. – Kirill Rakhman Aug 04 '20 at 09:51
  • 1
    Just a note to future googlers, Ian Lake uses this with `GlobalScope` in the Muzei app so I think it's probably ok https://github.com/muzei/muzei/blob/b66437955dc71d544194170bb7cb710d123ca69c/extensions/src/main/java/com/google/android/apps/muzei/util/BroadcastReceiverExt.kt – Daniel Wilson Jan 22 '21 at 12:15