How to use "goAsync" for broadcastReceiver?
Asked Answered
M

3

29

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?

Madeup answered 30/3, 2014 at 6:56 Comment(0)
D
47

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();
Diuretic answered 30/3, 2014 at 7:24 Comment(11)
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?Madeup
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 thereDiuretic
setResult is not needed it's only used to pass data between receivers during an ordered broadcast. You can leave it out.Diuretic
I don't get the setResultCode purpose and how to use it. How would you pass it forward ? Also, did you try this code?Madeup
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.Diuretic
but what is its (setResult) purpose? can you please give an example of when and how to use it?Madeup
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.Diuretic
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?Madeup
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.Diuretic
oh, i didn't know it has overloading method. ok thanks. for all your trouble and time, you get "V" and "+1" . :)Madeup
AsyncTask is deprecated. What is the modified solution now?Smokechaser
L
11

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
        }
    }
Laney answered 7/4, 2020 at 5:2 Comment(7)
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.Madeup
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.Laney
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...Madeup
Yeah, It's not recommended because it doesn't support structured concurrency. medium.com/@elizarov/… I used it here as an example.Laney
So you wrote code, and now you say it's not recommended to use it?Madeup
I would suggest wrapping the block in a try finally to ensure finish() is always called in the end.Carven
Just a note to future googlers, Ian Lake uses this with GlobalScope in the Muzei app so I think it's probably ok github.com/muzei/muzei/blob/…Pellet
H
0

Better use it with try/catch block

fun BroadcastReceiver.goAsync(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.() -> Unit
) {
    val pendingResult = goAsync()
    CoroutineScope(SupervisorJob()).launch(context) {
        try {
            block()
        } finally {
            pendingResult.finish()
        }
    }
}
Heda answered 5/5, 2023 at 23:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.