views:

1019

answers:

1

Hello all,

I have a semi-complicated problem and hoping that someone here will be able to help me.

On a click event I create a thread and start a long-running operation based on this method. After the long-running task is completed, it does a callback to another method, which does a post to the handler:

@Override
public void contentSearchModelChanged(Model_ContentSearch csm, ArrayList<Class_Reminder> newRemindersList) {
    remindersList = newRemindersList;
    mHandler.post(mUpdateDisplayRunnable);
}

Which calls a Runnable:

// post this to the Handler when the background thread completes
private final Runnable mUpdateDisplayRunnable = new Runnable() {
  public void run() {
    updateDisplay();
  }
};

Finally, here is what my updateDisplay() method is doing:

private void updateDisplay() {
    if (csModel.getState() != Model_ContentSearch.State.RUNNING) {
        if(remindersList != null && remindersList.size() > 0){
                r_adapter = new ReminderAdapater(Activity_ContentSearch.this, remindersList, thisListView);
                thisListView.setAdapter(r_adapter);
                r_adapter.notifyDataSetChanged();
        }
    }
}

This works beautifully when I do this normally. However, if I change the orientation while the long-running operation is running, it doesn't work. It does make the callback properly, and the remindersList does have items in it. But when it gets to this line:

r_adapter.notifyDataSetChanged();

Nothing happens. The odd thing is, if I do another submit and have it run the whole process again (without changing orientation), it actually updates the view twice, once for the previous submit and again for the next. So the view updates once with the results of the first submit, then again with the results of the second submit a second later. So the adapater DID get the data, it just isn't refreshing the view.

I know this has something to do with the orientation change, but I can't for the life of me figure out why. Can anyone help? Or, can anyone suggest an alternative method of handling threads with orientation changes?

Bara

+2  A: 

The problem is that when you change orientations a new activity is spun up from the beginning (onCreate). Your long running process has a handle to the old (no longer visible) activity. You are properly updating the old activity but since it isn't on screen anymore, you don't see it.

This is not an easy problem to fix. There is a library out there that may help you though. It is called DroidFu. Here is a blog post that (much more accurately than I) describes the root cause of what you are seeing and how the DroidFu library combats it: http://brainflush.wordpress.com/2009/11/16/introducing-droid-fu-for-android-betteractivity-betterservice-and-betterasynctask/

Edit: (Adding code for tracking active activity)

In your application class add this:

private Activity _activeActivity;
public void setActiveActivity(Activity activity) {
    _activeActivity = activity;
}
public Activity getActiveActivity() {
    return _activeActivity;
}

In your Activities, add this:

@Override
public void onResume() {
    super.onResume();
    ((MyApplicationClassName)getApplication()).setActiveActivity(this);
}

Now you can get the active activity by calling MyApplicationClassName.getActiveActivity();

This is not how DroidFu does it. DroidFu sets the active activity in onCreate but I don't feel that is very robust.

Jere.Jones
Hmm... that seems very helpful, but there doesn't seem to be a lot of documentation? I would hate to change my implementation to using DroidFu and not know how to resolve a problem because it isn't the "typical" way of doing things.
Bara
Yeah, it appears to be an old abandoned library but the premise seems sound. You could do this without using the library. Basically the Application object keeps track of the active Activity (using onResume). Then, when you are ready to notify your adapter, you get the active Activity from the Application, check to see if it is an instanceof your expected activity and if so, cast and notify.
Jere.Jones
What exactly is the code for tracking the "active Activity"? That is, should I be doing something like CurrentActivity act = new CurrentActivity(); then passing act to the Application class?
Bara