Search code examples
androidandroid-fragmentsonbackpressed

Fragments: Replacing a fragment causes it to reinstantiate on back pressed


I am using one activity all fragment approach. Now I have a fragment A and when I go to fragment B I replace A in container with B with

.replace(R.id.master_fragment_container_above_toolbar, fragment)
                .addToBackStack("")
                .commit();

As you can see, I have added fragment to backStack. But now when I press back on device, it re-instantiates the fragment A and thus takes time to go back.

Is there another way to do it? I don't want to use add(), if I add multiple fragments to container, it will take up a lot of memory.


Solution

  • The short answer - there's no silver bullet in your case. You'll have to use replace() and it will re-create fragment's View on going back. That's "by design".

    The good news is that there're a few tricks you can do to make it less dramatic for UX.

    • Cache whatever you can. If you load content from web - write it into the local SQLite db. And fill the screen from Local Storage while refreshing the data from the server.

    • In onCreateView() avoid re-creating Adapters once they are already exist. If user is getting back to FragmentA from the FragmentB, FragmentA will recreate its View. But it doesn't mean, that local variables are null at this point. So I do it like this:

      public class FeedFragment extends Fragment {
      
          private  FeedAdapter adapter;
      
          ......
      
          @Override
          public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
              View rootView = initialize(inflater, container, R.layout.fragment_with_recyclerview);
              ....
              if (adapter == null) {
                  adapter = new FeedAdapter();
              }
      
              RecyclerView recyclerView = (RecyclerView)rootView.findViewById(R.id.recyclerView)
              recyclerView.setAdapter(adapter);
              recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
      
              return rootView;
          }
      }
      

      It matters, because filling TextViews, etc. are so fast operations, that you don't really care about it, while huge ListView or RecyclerView can be much more expensive;

    • Use image-caching and good image-loading tool. I'd recommend Picasso from Square. It has automatic memory and disk caching;

    • Try to logically decouple your app. Let's take a Gmail-style application, as an example: It has NavigationDrawer, which is switching user through the root fragments (i.e. no need to keep navigation stack). Once you open mail-thread - it replaces the root fragment with MailThread fragment. There you can consume the content.

      But once you click on "Compose email" - you are redirecting to a separate Activity - as you move from consuming content to composing content state (I.e. significant change in user's behaviour: new user scenario == new activity).

      Many top developers go even further and having Activities for pretty much everything. Twitter (and it's satellite products) as an example. But personally, I prefer to keep it in balance (as opening new activity is an expensive operation from perf. point of view).