usr-local-ΕΨΗΕΛΩΝ
usr-local-ΕΨΗΕΛΩΝ

Reputation: 26874

Android BroadcastReceiver: threading model and non-overlap

I have a few design questions about Android's BroadcastReceivers.

My scenario is the following: when a certain wifi network is connected, detect if there is a captive portal and, if so, authenticate to it. The scenario involves network I/O and takes several seconds to complete (especially when you need to wait for an SMS with the password).

By debugging the BroadcastReceiver for Wifi state change I found that it is invoked on application's main thread. This was something I didn't know since a few minutes. I'm saying this, not asking, because the question is also a consequence of my discovery.

During the wireless authentication phase, I should handle the case in which the user gets suddenly disconnected (e.g. due to hotspot failure, user walking out of range...) and reconnected, by avoiding two broadcasts to overlap and possibly cancel authentication which eventually fails.

After I discovered that the BroadcastReceiver is executed in main thread, I came to the conclusion that I need an AsyncTask to execute network I/O, but feel free to suggest other ideas.

The question is: in the event that a consecutive broadcast is fired in the middle of the long-running task, how do I stop that? Indeed, AsyncTasks can be canceled, but how can I store a shared instance of the AsyncTask in order to cancel it from a different BroadcastReceiver without complicating my life too much?

Are Android BroadcastReceivers de facto singletons or does Android create a new instance of declared receivers every time it needs to fire a new broadcast?

Upvotes: 1

Views: 864

Answers (3)

CommonsWare
CommonsWare

Reputation: 1006944

I came to the conclusion that I need an AsyncTask to execute network I/O

Probably not.

but feel free to suggest other ideas

As the others indicated, an IntentService is a better idea.

Part of the problem is that you have told us next to nothing concrete about your receivers. In particular, you have not indicated how you registered the receivers in question: the manifest, or registerReceiver()? Recommendations on your questions ideally would take that into account. The IntentService answers, and this one, assume that you are registering via the manifest.

Are Android BroadcastReceivers de facto singletons or does Android create a new instance of declared receivers every time it needs to fire a new broadcast?

If you are registering the receiver in the manifest, a new instance of the BroadcastReceiver is created for each broadcast. More importantly, there is nothing signalling to Android that your process needs to stick around once onReceive() returns in this case, which is why AsyncTask is not a good idea and IntentService a much better one.

in the event that a consecutive broadcast is fired in the middle of the long-running task, how do I stop that?

Probably you don't "stop that", because it is quite possible that what you are doing cannot be "stopped" in any meaningful sense without leaving you with an indeterminate state for your data.

If you feel really really confident that you need to "stop that", you will need to create your own Service that has some of the characteristics of IntentService (e.g., it has its background thread) but that allows you to manipulate the command queue, stop in-progress work, etc.

but how can I store a shared instance of the AsyncTask in order to cancel it from a different BroadcastReceiver without complicating my life too much?

You can't, readily, except perhaps as a static data member. But since it is rather likely that Android will get rid of your process before the task completes, using an AsyncTask is not a good idea.

Upvotes: 1

ianhanniballake
ianhanniballake

Reputation: 199880

BroadcastReceivers work perfectly with an IntentService which sequentially processes incoming intents off of the UI thread. The Services guide has an implementation sample. Your BroadcastReceiver would then only have to call the IntentService.

Upvotes: 1

rciovati
rciovati

Reputation: 28063

It is a good practice to start a Service for tasks which take some time to execute.

In your case I would use an IntentService. This particular Service executes in a background thread and it also handle an internal queue, so if you start it twice, the second tasks will be executed after the first one completes. I strongly advise you to use IntentService instead of a AsyncTask.

Upvotes: 1

Related Questions