I have a BroadcastReceiver (or, more specifically, an AppWidgetProvider) in which I need to do some network operations in the onReceive() method... not particularly long-running (should finish within 10s, and if not then I'm happy(*) to time them out).
At present I'm executing an AsyncTask from onReceive() so that the network operations don't block the UI thread, but I see that a BroadcastReceiver also provides a goAsync() method for apparently much the same purpose.
I'm struggling to find an explanation as to what exactly the differences are between using AsyncTask and goAsync() in this context... when would you use one, and when the other?
(*) I mentioned that my network operations and subsequent processing would typically finish within the 10s (**) that I understand you have before the BroadcastReceiver is killed (after returning from onReceive()). However, ideally it would be good to allow the operations to continue beyond the 10s in particularly challenging conditions (unreliable network, slow device). Is either of the above methods any better than the other in this respect? At least for goAsync(), the docs suggest that you should still wrap things up within the 10s. But for AsyncTask is it ever possible to spawn the AsyncTask and have the doInBackground() method run beyond the 10s... will it have an existence beyond the lifetime of the BroadcastReceiver if it is declared as a static inner class of the BroadcastReceiver?
(**) EDIT: the 10 second thing is in the docs for BroadcastReceiver.onReceive()... specifically:
When it runs on the main thread you should never perform long-running operations in it (there is a timeout of 10 seconds that the system allows before considering the receiver to be blocked and a candidate to be killed).
AsyncTask in receiver is a bad practice, try to avoid it because the system can kill your process and onReceive no guarantee its return.
goAsync() returns a PendingResult object,
the system considers the receiver as alive until you call the PendingResult.finish() on it.
goAsync:
if you call goAsync before the BroadcastReceiver::onReceive return, when a new broadcast comes after a while, android system will call the same receiver, just the same receiver instance where you call goAsync
AsyncTask:
if you call AsyncTask before the BroadcastReceiver::onReceive return, when a new broadcast comes after a while, the android system may call a new receiver even though the process is still running, you may not suppose the receiver is the same as the previous one where you call AsyncTask
Related
Is it correct that there's some overlap in the responsibilities of BroadcastReceiver and IntentService? By that, I mean that it would be redundant to set up a BroadcastReceiver that triggers an IntentService that performs some task that needs to be done. Since both respond to intents it's simpler to have just the IntentService respond directly to the intent without BroadcastReceiver serving as an intermediary.
Is this interpretation correct? Is it always correct? If not, please give an example of when it's incorrect.
Thanks much!
I mean that it would be redundant to set up a BroadcastReceiver that triggers an IntentService that performs some task that needs to be done
Not only is that not necessarily redundant, it is a very common pattern.
Is this interpretation correct?
No.
please give an examples of when it's incorrect.
Either an Intent is used with sendBroadcast() or with startService(). If an Intent is used with sendBroadcast(), it is not possible for a service to respond to it directly. Similarly, if an Intent is used with startService(), it is not possible for a receiver to respond to it directly. So, in cases where somebody else wrote the code to use the Intent, you have to match up with how they used it. You cannot unilaterally "change the channel" that the Intent is used on.
Beyond that, onReceive() of a BroadcastReceiver is always called on the main application thread. You do not want to take any meaningful amount of time here, as it will freeze your UI if your UI happens to be in the foreground. And, once onReceive() returns, a manifest-registered receiver's instance is discarded, and your process may be terminated shortly thereafter. Hence, it is not safe for a BroadcastReceiver to fork its own background thread, as Android will ignore that thread and terminate your process. A common pattern for responding to system-sent broadcasts is to use a BroadcastReceiver, then have it delegate the work to an IntentService. In one fell swoop, you solve both problems:
IntentService does its heavy lifting in onHandleIntent(), called on a background thread
because it is a Service, having it running while doing that work will signal to the OS to let your process live a little while longer
Now, if you are the one creating the Intent and you are the one dispatching that Intent and you are the one consuming that Intent, and you want to have your code use startService() directly to invoke an IntentService, that is perfectly fine.
I've a BroadcastReciever on a separate class, i register that receiver on one of the activities, and this broadcast receiver trigger when there is an Internet connectivity. Inside onReceive() of the receiver, i execute a method for getting token from the server.
But when i gone through the documentation i found that; "When it runs on the main thread you should never perform long-running operations in it (there is a timeout of 10 seconds that the system allows before considering the receiver to be blocked and a candidate to be killed)."
Please help me with the correct way of doing it.
IMO , it is perfectly fine to call a WebService inside the onReceive method of a broadcast receiver. I have done it in many of my applications and till now I have never faced any problem.
Infact in majority of applications, which require frequent updates from server the BroadcastReceiver component is used as the onReceive method runs in the Worker thread/task.
To be on the safe side you can set your WebService timeout to less than 10 secs.Another implementation can be that you can create a background/worker thread, send it a token from the onReceive of your BroadcastReceiver and inside that thread you can call your WebService.
No, The Android System may be kills the your BroadcastReceiver, in case of in-sufficient memory. Because user never or not recently interact with the process of Application.
A process that is currently executing a BroadcastReceiver (that is, currently running the code in its onReceive(Context, Intent) method) is considered to be a foreground process and will be kept running by the system except under cases of extreme memory pressure.
That means A Process holds only BroadcastReceiver, then it may be considered as low priority under cases of extreme memory pressure.
This means that for longer-running operations you will often use a Service in conjunction with a BroadcastReceiver to keep the containing process active for the entire time of your operation.
Intent intent = new Intent(mContext, MyService.class)
intent.setData(Uri.parse(your_url));
mContext.startService(intent);
a) if my code calls startService() twice, will the Intents be processeced in the order I called them, or might they get switched randomly?
b) will the first run through onStartCommand() complete before the second call starts, or might they run in parallel?
I have read the android services guide and the reference. While they clearly show how the IntentService worker threads are serialized, I've found little information on how the Intents are delivered and processed.
if my code calls startService() twice, will the Intents be processeced in the order I called them, or might they get switched randomly?
The behavior for Service in this regard is undocumented, last I checked. However, they should be called in the order they were issued, based on what seems to happen in practice.
will the first run through onStartCommand() complete before the second call starts, or might they run in parallel?
That is up to you. By default, onStartCommand() on a Service is called on the main application thread, and therefore only one command is processed at a time. If you choose to fork threads from onStartCommand() to process commands, those threads may run in parallel.
While they clearly show how the IntentService worker threads are serialized, I've found little information on how the Intents are delivered and processed.
IntentService maintains its own thread. You implement onHandleIntent() instead of (or possibly in addition to) onStartCommand(). Since there is only one thread, only one onHandleIntent() will be executed at a time.
I'm trying to update the state of a UI on receipt of a push notification. In order to do this, I need to start an AsyncTask that performs some network operations and then updates the UI based on the result.
According to the documentation for BroadcastReceiver, performing asynchronous operations within a receiver is unsafe because the process executing it may be killed as soon as onReceive() returns, assuming there are no other "application components" in that process.
Is the BroadcastReceiver running in its own process, or in the same process as the containing Activity? Since I only care about the completion of the task as long as there is a UI to update, I'm not worried about the AsyncTask dying if the activity is closed. Assuming the BroadcastReceiver is in the same process as the activity, does this make it okay/safe to launch the task I've described from within the receiver?
Edit:
To clarify, I am registering the receiver in the activity's onResume() and unregistering it onPause(), so it should only be receiving intents when the activity is already active.
Broadcast receiver is not running on it's own process, it's running on UI thread.
Your process will be killed after onReceive method returns only if there is no other activity or service in your app is running.
If your broadcast receiver is an instance of an inner class and only receive when your activity is active, then your process will not be killed after onReceive method returns.
If inside your AsyncTask, you need a context, then I think a service is better. If not, there is no problem using AsyncTask.
Before Honeycomb (API11), you had to use a service.
Since Honeycomb (API11), you can use 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.
What I would recommend doing is startActivity(intent) from the broadcast receiver. Thats all. Inside the intent I would provide the event information you speak of, you can just set a parameter in the bundle. You can then examine this inside the Activity onStart() or onCreate() whichever gets called. If the flag is there, then from the Activity kick off the AsyncTask.
No need to use a service at all, with all the binding and communication limitations from service-activity.
Remember you can also startActivityForResult() as well. I think that you don't want to do anything except pass and forward inside a broadcast receiver.
BTW, Activities don't need to have UI's. There can be faceless activities.
Why is it suggested generally to pass a pending intent for an Intent Service when using alarm manager? The same thing can be done in the onreceive() function of the broadcast receiver called by the alarmmanager. What is the advantage with using a service(Intent Service)?
If everything that you need done can be completed in onReceive of a BroadcastReceiver, then you should use that, not an IntentService.
If you want to do anything after the BroadcastReceiver, then you should use the IntentService. For example, if you want your BroadcastReceiver to start a Service, and you want the service to gain a WakeLock, then you should be using an IntentService instead.
The reason is that AlarmManager only guarantees that the onReceive of a BroadcastReceiver will be run, even if you use RTC_WAKEUP. So, it is slightly possible that if you use the BroadcastReceiver/Service combination, then the CPU will fall asleep before the Service can acquire the WakeLock - this is, unless you acquire a WakeLock in the BroadcastReceiver and you acquire one in the service, perhaps via a static WakeLock. But this is... messy, I suppose.
Btw, I have never implemented an IntentService. I just use the BroadcastReceiver and Service combo and have never had a problem reported. All the information I provided are things I read from other SO posts (primarily from CommonsWare)
EDIT:
The 50ms time frame I read from something CommonsWare posted on StackOverflow, and CommonsWare seems to be a rather reliable source of knowledge for Android.
I looked it up and, The docs do say:
(there is a timeout of 10 seconds that the system allows before
considering the receiver to be blocked and a candidate to be killed).
And they also say:
If this BroadcastReceiver was launched through a tag, then the object is no
longer alive after returning from this function.
You should not do anything that takes close to 10 seconds, just to be safe.
If you do anything that has to wait for a response, the BroadcastReceiver will die because the onReceive will likely finish running before you get the response back.
Though, I suppose the reason for the 50ms time frame is so you don't risk causing an ANR or any lag. Because if you use a Service, then you can start a new Thread, and it will not block. You would not be able to start a new Thread in a BroadcastReceiver because the code after the thread would continue to run, the BroadcastReceiver would die, and then the Thread would die, too.