Question

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:

  1. 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.

  2. 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:

  1. There should be a Sign In activity.
  2. 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.
  3. 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:

  1. Activity + AsyncTask. Doesn't work at all as AsyncTask is bound to specific Activity instance and once Activity is recreated (device orientation change is an example), AsyncTask is bound to "wrong" context.

  2. Activity + IntentService. Activity triggers the service and provides the "request context" in Intent. Once IntentService is done, it uses sendBroadcast() to tell the Activity where we are. This doesn't work because by the moment IntentService is done, Activity can already be paused, so it won't receive the update.

  3. 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.

  4. Using IntentService + ContentProvider + Loader. Activity triggers the service and provides the "request context" in Intent. 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 uses Loader 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.

Was it helpful?

Solution

first of all, you are certainly on the "right direction" if you are taking in mind all the points you mentioned.

the problem you describing is very common, and concerns to most of applications

there is problem with some of your assumptions:

Activity + AsyncTask. Doesn't work at all as AsyncTask is bound to specific Activity instance and once Activity is recreated (device orientation change is an example), AsyncTask is bound to "wrong" context.

no one said AsyncTask must been executed from an Activity!!

it's also a good practice using AsyncTask from android Service derived class (not IntentService..) . that's possible because Service methods are executed from the main thread.

actually, that is the preferred solution to my believe.

Activity + IntentService. Activity triggers the service and provides the "request context" in Intent. Once IntentService is done, it uses sendBroadcast() to tell the Activity where we are. This doesn't work because by the moment IntentService is done, Activity can already be paused, so it won't receive the update.

if the activity already paused/destroyed - you don't have any reason to update it anyway!!!!!

instead, you can just strore this information persistently / static singeltone class, and when this activity resume - it will retreive it back, and display the relevant state.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top