There's a common application behavior pattern, when you can't go next unless current action is finished. A good example is authentication: once you provided the app with you login and password, and tapped "Sign In", the app will block for a while displaying you a sort of "Wait, I'm working" indicator. Even though this task is pretty common, I didn't manage to google a "typical" solution.
Major points I keep in mind:
There are absolutely no guarantees for Activity lifetime. Activities are guaranteed to be launched when user launches them, but then they can be killed at any moment. So, in general, Activities should be thought of as interaction points where your app and the user can discuss their "desires". It's always up to user to start this discussion.
On the other hand, there are Services, components which never talk to users directly. There are guarantees for Service lifetime, so it seems to be the right place to put your processing logic to.
So, for the task I described at the very beginning, it's fair to state that:
- There should be a Sign In activity.
- This activity should be capable of running in 4 modes:
- Clean/Just started/User didn't push the button yet.
- User just pushed the button and we're talking to web API - that's the long running task and we want to display a sort of ProgressDialog.
- We managed to talk to web API and it said everything was OK. Here we're likely to finish this activity and start the more useful one - the one that user originally wanted to see, when they launched the app.
- Either we didn't manage to talk to web API or it said something was wrong with user credentials. In this case we're displaying an error message.
- There should be Service responsible for talking to web API.
So, here's a question:
How do I properly make an Activity aware of which one of 4 modes should currently be active?
What I have considered:
Activity
+AsyncTask
. Doesn't work at all asAsyncTask
is bound to specificActivity
instance and onceActivity
is recreated (device orientation change is an example),AsyncTask
is bound to "wrong" context.Activity
+IntentService
. Activity triggers the service and provides the "request context" inIntent
. OnceIntentService
is done, it usessendBroadcast()
to tell theActivity
where we are. This doesn't work because by the momentIntentService
is done,Activity
can already be paused, so it won't receive the update.Same as p.2, but using
sendStickyBroadcast()
. This solves the missed updates problem, but Google says it's a bad practice because of some king of overhead or whatever.Using
IntentService
+ContentProvider
+Loader
. Activity triggers the service and provides the "request context" inIntent
. The request context has a magic "request token". This token describes a "unique intention to do something", so that when "something is done", it's possible to tell what it was and who wanted it.IntentService
starts processing by saving the request record to the database: namely, request token and status (started). Then it starts processing. Once processing is done, it updates the record status to "done" and puts a result there. At the same time,Activity
usesLoader
to listen to this record. It understands which mode it should run in, based on the status. This solution works perfectly for all scenarios including device orientation changes, leaving the activity, incoming calls, etc, but feels like a weird overkill.
I was wondering if there's an easier solution.