1

I have a problem that causes me some problems when a user (or another app, like the phone-application) pushes my application to the background.

My application does following:

  1. A User can enter some information that is supposed to be pushed to a server.
  2. When the user clicks "Send" i open a managed ProgressDialog and start an AsyncTask that performs the server communication.
  3. When server communication is complete the AsyncTask reports back to my Activity where i perform a dismissDialog().
  4. Directly after dismissDialog(), I will show another managed dialog using showDialog() that will inform the user about whether the submission was ok or if it failed.

This all works perfectly without any issues; however, when a call happens to come while the AsyncTask is running I get (seemingly random) one of these results:

  1. The activity holding the managed dialog is dismissed completely and the previous view from the stack is presented when I come back.
  2. The activity holding the managed dialog is still on screen, but it is grayed out without showing a dialog. The only way to fix this is to rotate the phone at which point it shows the "Submission sent"-dialog exactly the way it should and everything is ok after that.

All this happens without any warning messages so I get absolutely no clues as to why Android is behaving this way.

I know a way around this and that is to cancel the AsyncTask (so no dialogs are shown at the end). However, in this very use-case the requirements are that the app has to try to complete the server transaction so that there is as little confusion as possible (i.e. the user wondering if it was really sent or not).

Has anybody else had this issue and knows a way around?

SiCN
  • 1,052
  • 8
  • 17

2 Answers2

1

I see recommendations to hold a reference to the asynch task in onRetainNonConfigurationInstance

What to do with AsyncTask in onPause()?

Or implement a bus:

https://github.com/commonsguy/cwac-bus/tree

EDIT: The complexity of your challenge is two fold:

1) saving and restoring state of your app on a kill such as when there is an incoming phone call

https://sites.google.com/site/jalcomputing/home/mac-osx-android-programming-tutorial/saving-instance-state

2) somehow continuing the asyncTask on kill instead of canceling it onPause

https://sites.google.com/site/jalcomputing/home/mac-osx-android-programming-tutorial/asynch

Both of these are significant challenges alone, and trying to fix both at the same time would give me a headache. In fact, I am getting a headache just thinking on it :) One clue is that you say the dialog returns on orientation change. This MAY be due to the fact that using the standard architecture for dialogs, the OS handles saving and restoring the state of dialogs for you on orientation change.

[EDIT] See CommonsWare

@Override
public Object onRetainNonConfigurationInstance() {
    task.detach();
    return(task);
}

and

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);      
    bar=(ProgressBar)findViewById(R.id.progress);   
    task=(RotationAwareTask)getLastNonConfigurationInstance();  
    if (task==null) {
        task=new RotationAwareTask(this);
        task.execute();
    }
    else {
        task.attach(this);
        updateProgress(task.getProgress());
        if (task.getProgress()>=100) {
            markAsDone();
        }
    }
}

where task is an instance of

static class RotationAwareTask extends AsyncTask<Void, Void, Void> {

I see no reason why this would not work for all types of soft kills, but on a hard kill, well, you get killed. Dead is dead :)

Community
  • 1
  • 1
JAL
  • 3,319
  • 2
  • 20
  • 17
  • The link you provided seems to deal mostly with the orientation change. That is not quite my issue, it's just a solution ;) As I wrote, orientation change is the only way I can get the "invisible" dialog to show up. However, the other case (where the activity is destroyed completely and silently) is my bigger headache. – SiCN Jun 07 '11 at 20:20
  • If you drill down I saw: http://osdir.com/ml/AndroidDevelopers/2009-08/msg00587.html I will update my answer. – JAL Jun 07 '11 at 20:32
  • I actually want to be so bold and say that the last link you provided is not accurate. It says that there is nothing that stops you from showing a confirmation dialog in onPostExecute(). In fact, my code looks more less exactly like the code that has been provided and does not generate the "Force close"-message on orientation change while in background. Try it. Start the task and hit the "home"-button, then wait a while, and go back. You will be greeted by a grayed out screen. The only way this doesn't happen is to cancel the AsyncTask, the exact think I am hoping to avoid. – SiCN Jun 07 '11 at 21:01
  • ... ran out of letters. Anyway, your answers do give me an important insight and that is that my solution may simply not work the way I believe it should. In your opinion, would rewriting the server invocation code to a Service help? The service could instead send a result broadcast to the Activity which is put on queue until the Activity awakes? I am starting to suspect (even though the debugger indicates otherwise) that the AsyncTask may be killed in Android when the app goes to background and that it never properly executes the onPostExecute()-code. – SiCN Jun 07 '11 at 21:08
  • @sicn I have not needed to code a service yet. I do believe that you would create a service and then bind your application to the service. I see two kinds of kill, soft and hard. I BELIEVE you can persist the async task on a soft kill using onRetainNonConfigurationInstance. I will edit my answer – JAL Jun 08 '11 at 02:05
  • So, I seem to have solved it. My solution differs from yours as I am using a Queue which I drain onResume() but all your links and tips were excellent pointers as to how to solve the issue, therefore I'll accept your answer. Thanks! – SiCN Jun 08 '11 at 13:46
0

Without looking at your code it is slightly difficult to say what the problem is. However, here is something you could use to help get around the problem. You can override the onPause() method of your Activity.

This is taken directly from the Android Acitivy javadoc:

onPause() is where you deal with the user leaving your activity. Most importantly, any changes made by the user should at this point be committed (usually to the ContentProvider holding the data)

Tanner Perrien
  • 3,133
  • 1
  • 28
  • 35
  • Well, the application (in fact, a quite large one) does do a lot in onPause(), however, in my particular case I don't see how this is going to help? I tried before to suppress the dialog if onPause() was triggered, but then I ran into other issues (i.e. the progress dialog still being "stuck"). – SiCN Jun 07 '11 at 20:17
  • @sicn I was just thinking that you could trigger a flag in the `onPause()` method. Then in your `AsyncTask` you could check this flag before proceeding, and set a second flag to alert the `onResume()` to pick up where this task left off. In the `onResume()` method you could check the second flag and decide at that point if you should show a dialog. Just a thought. – Tanner Perrien Jun 07 '11 at 20:36
  • thats what i did before, didn't help though. as i wrote, the progress dialog has already been started and then that one will be stuck instead. :-/ – SiCN Jun 07 '11 at 20:43