1

I've developed an app, but a user is stating the app is crashing on them in a specific part, the code I suspect to be the culprit can be found below, but to provide you with some context. I have a fragment manager that acts as a wizard, and on the last fragment is a submit button that, when clicked, "processes" the data from all pager fragments and then redirects to a confirmation fragment, all of this happening after the submit button is clicked is done within an Async Task. Also, if it is of any consequence, I am running a progress dialog while the AsyncTask is running.

//Called from Async Task's "onPostExecute()"
private void callback()
{
    FragmentManager fragmentManager = getActivity()
            .getSupportFragmentManager();

    fragmentManager.popBackStack("fraud_report", FragmentManager.POP_BACK_STACK_INCLUSIVE);

    Fragment f =  new CompletedFragment();

    Bundle args = new Bundle();

    args.putString("id", reportToSubmit.getReferenceId());

    f.setArguments(args);

    fragmentManager.beginTransaction()
            .replace(R.id.content_frame, f, "completed").commit();

    fragmentManager.executePendingTransactions();


}
DeveryDay
  • 161
  • 15
  • You mention that a user is reporting this. Do you mean you can't reproduce the error yourself? A stack trace would be really helpful. – Matt O May 07 '14 at 13:30
  • We haven't been able to. They are testing on a Note 2 using Android 4.3. We are actually waiting on the stack trace information ourselves (we've set up logging in the test app to record this now). Unfortunately as of right now all I have to go in is what I mentioned above. – DeveryDay May 07 '14 at 13:36

1 Answers1

1

I have a guess as to what this could be, especially if it is hard to reproduce. You issue is that you are doing a FragmentTransaction after an asynchronous callback, during which any number of things can happen. Therefore you cannot guarantee where you will be in the Activity Lifecycle. The problem is Fragments and State Loss.

http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html

https://stackoverflow.com/a/17527246/1856960

Say for example, the user starts the AsyncTask, then ends the Activity, starts another one, or hits the home button. All of this will put the Activity in the background and may trigger a call to onSaveInstanceState. You cannot commit a Fragment Transaction after onSaveInstanceState is called. This is because the FragmentManager and all the state associated with it has already been bundled in onSaveInstanceState, so if the Activity is destroyed and re-created, you will lose your Fragment Transaction.

As for fixing the issue, I wish I had a good suggestion for you. You can look into FragmentTransaction#commitAllowingStateLoss but this should only be used if you are sure the potential downfalls will not negatively impact your application. See the links above for more information.

Community
  • 1
  • 1
jacobhyphenated
  • 2,105
  • 2
  • 17
  • 27
  • And unfortunately, at this point, I don't think we may be able to get around using commitAllowingStateLoss based off the information in the articles. Pretty much every fragment within our application is static and doesn't need to retain state, but I'll keep looking into this until we get more data back about the user's error. Thanks. – DeveryDay May 07 '14 at 13:51
  • TBH, I have had to use commitAllowingStateLoss several times, just because the only other solution was re-architecting everything we did for async tasks. If the user navigates back to the activity before it is destroyed, there won't be a problem. It will only be an issue if the Activity is destroyed and that last Fragment Transaction is lost. Try to see if there is a way to mitigate this when the Activity re-creates itself. – jacobhyphenated May 07 '14 at 13:53
  • So what if all of this is contained inside of a single Activity? I am curious. Is worrying about Activity destruction even a worry at that point? – DeveryDay May 07 '14 at 13:56
  • If the user hits the home button or something, putting your Activity in the background, at any time, the OS can destroy the Activity to reclaim memory. When the user navigates back, it will be re-created (calling onCreate, etc.). In your case, the Fragment Transaction that popped the back stack and added the CompletedFragment will be lost. The Fragment manager will restore to whatever state in was in when `onSaveInstanceState` was called. Note that this will not happen every time, `onSaveInstanceState` would have to be called before `callback`. It is a race with the OS. – jacobhyphenated May 07 '14 at 14:01
  • So then perhaps I am trying to solve the wrong problem. The flow goes like: User fills info on each view pager screen, moves to next until they get to the last and hit submit -> Async task processes information (showing progress dialog) and when done, calls onPostExecute to then move user to CompletedFragment. IS there a cleaner way to do this then? To process the user input (which get sent to a server), then show a new success screen upon completion? It's also my intentions to destroy the view pager fragment so that if a user wants to fill in and submit a new wizard, it is reset. – DeveryDay May 07 '14 at 15:02
  • Perhaps one option would be to start a new Activity in `callback` and have that Activity display the `CompletedFragment`. That should guarantee the completion screen is shown since `startActivity` can be called from anywhere. Not exactly sure how to handle restarting the flow, it depends on how that Activity and Fragments are working together. – jacobhyphenated May 07 '14 at 15:19