3

I want to manage fragments on my screen depending on a result of async request to local database. E.g:
1. Go to database
2. Get results
3. Once results are delivered, choose a fragment that should be shown to a user.
(Something like in the code below).

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getLoaderManager().restartLoader(0, null, new LoaderManager.LoaderCallbacks<Boolean>() {
        @Override
        public Loader<Boolean> onCreateLoader(int id, Bundle args) {
            return new MyLoader(MainActivity.this);
        }

        @Override
        public void onLoadFinished(Loader<Boolean> loader, Boolean data) {
            FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
            if (data) {
                fragmentTransaction.add(new MyFragment1(), null);
            } else {
                fragmentTransaction.add(new MyFragment2(), null);
            }
            fragmentTransaction.addToBackStack(null);
            fragmentTransaction.commit();
        }

        @Override
        public void onLoaderReset(Loader<Boolean> loader) {
        }
    });
}

private static class MyLoader extends AsyncTaskLoader<Boolean> {

    public MyLoader(Context context) {
        super(context);
    }

    @Override
    public Boolean loadInBackground() {
        try {
            Thread.sleep(1000); //Let's pretend we are looking for something in local db
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return (System.currentTimeMillis() % 2) == 0; // return true or false randomly
    }
}
}

As a result, I get:
java.lang.IllegalStateException: Can not perform this action inside of onLoadFinished

I can fool the system of cause and wrap the fragment transaction into a runnable and call this on a handler, but it seems that I will still get an exception is the loader results will be delivered between saveInstanceState() call and onStop(). I can't use "commitAllowingStateLoss" method, cause I rely on the valid stack of my fragments and I do not want it to be messed up.

Can anyone advise how to do that in a proper way?

  • 1
    You'll first want to read [this blog post](http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html), which was written by a Google dev explaining what state loss is and how to avoid it. You can also [check out my question here](http://stackoverflow.com/questions/34320147/how-can-i-avoid-an-illegalstateexception-if-i-need-to-add-a-fragment-in-onnewint) to see how I avoided this. But the short story is, fragment transactions and state loss is a HUGE pain to deal with. – NoChinDeluxe Jan 08 '16 at 15:26
  • I've been reading this article for the whole day already :). It is really clear that I should be careful not to modify fragments after onStateSave but it is absolutely not clear to me how to achieve that without hacking the whole system. I have already thought about storing my "update fragments" instructions in a queue of runnables if they happen after state is saved and execute them one by one in onStart. But this looks like a hack and I thing there should be a proper way to do that. I am sure that managing fragments as a result of async operation is a common porblem.. – Dmitry Vilner Jan 08 '16 at 17:05
  • Trust me, it's going to feel like a hack no matter what. What I ended up doing was set my fragment to be a retained instance, then I created a member variable inside the fragment that kept track of its own state of being visible to the user. Then I just set that variable from my Activity whenever I needed to capture the state of all my fragments. Then when the config change comes in (like orientation change), I just asked the fragment if it should be showing. If it said yes, then I would add it to the activity. That's kind of a version of what you mention above. Seems like it's the only way. – NoChinDeluxe Jan 08 '16 at 18:27

0 Answers0