0

Having a look at this thread, I have a fundamental question.

1) Imagine I have a multi-pane layout like this one:

2) Now lets imagine that the underlying xml is like this one (for simplicity's sake most attributes are missed):

somefragment_land.xml:

<LinearLayout orientation="horizontal" ...>
    <!--our side menu-->
    <ListView id="@+id/menu" />
    <!--our details fragment container-->
    <FrameLayout id="@+id/container"/>
</LinearLayout>

3) Ok, so we have this SomeFragment class:

public class SomeFragment extends Fragment {

    public static final String TAG = "TAGTAGTAG";
    private static final String STATE_SELECTED_POSITION = "selected_position";

    private int currentSelectedPosition;

    private ListView mMenu;
    private MyAdapter mAdapter;
    private boolean isMultipaneMode;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        isMultipaneMode = getResources().getBoolean(R.bool.show_fragment_multiplane);
        if (savedInstanceState != null) {
            currentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION, 0);
        } else if (isMultipaneMode) {
            currentSelectedPosition = 0;
        }
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        int resId = isMultipaneMode ? R.layout.fragment_somefragment_land : R.layout.fragment_somefragment;
        View root = inflater.inflate(resId, container, false);
        mMenu = (ListView) root.findViewById(R.id.menu);
        mMenu.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                SomeItem item = mAdapter.getItem(position);
                showDetails(item);
            }
        });
        ///do some stuff creating adapter
        mMenu.setAdapter(mAdapter);

        if (isMultipaneMode) {
            showDetails(mAdapter.getItem(currentSelectedPosition));
        }


        return root;
    }

    @Override
    public void onDestroyView() {
        //remove details fragment
        destroyDetails();
        super.onDestroyView();
    }


    private void destroyDetails() {
        if (isMultipaneMode) {
            //schedule a transaction to remove a fragment
            //it will happen after SomeFragment is removed
            FragmentManager fm = getFragmentManager();
            Fragment fragmentByTag = fm.findFragmentByTag(FragmentDetails.TAG);
            if (fragmentByTag == null) {
                L.e(this.getClass(), "Details fragment removed");
                return;
            }
            fm.beginTransaction()
                    .remove(fragmentByTag)
                    .commit();
        }
    }


    private void showDetails(SomeItem item) {
        if (isMultipaneMode) {
            FragmentDetails details = new FragmentDetails();
            Bundle args = new Bundle();
            args.putString(FragmentDetails.ARG_ID, item.getId());
            details.setArguments(args);
            getFragmentManager()
                    .beginTransaction()
                    .replace(R.id.fragment, details, FragmentDetails.TAG)
                    .commit()
            ;

        } else {
            ActivityDetail.launch(getActivity(), item.getTitle(), item.getType());
        }
    }


    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (isMultipaneMode) {
            outState.putInt(STATE_SELECTED_POSITION, currentSelectedPosition);
        }

    }


}

So the logic is straightforward, show details in Fragment (for multipane mode) or start Details activity if we are running on a smartphone etc

What I want to know is - how much wrong is this approach in terms of Fragment management?

I imagine myself the following case:

  1. SomeFragment is added to FragmentManager

  2. user decides to go elsewhere

  3. Transaction_1 is started to remove SomeFragment

  4. this calls to onDestroyView() which schedules a transaction to remove DetailsFragment

  5. Transaction_1 is complete, however, DetailsFragment is not yet removed. It possibly holds some part of SomeFragment view hierarchy in memory

  6. Transaction_2 is started to remove DetailsFragment

  7. Transaction_2 is complete, DetailsFragment is destroyed

  8. ???

These question marks stand for some uncertainty - have I created a memory leak? Or something worse? Any off-top-of-your-head consequences of using this approach?

Community
  • 1
  • 1
Drew
  • 3,307
  • 22
  • 33
  • You don't need to remove the fragments at all. Just let them remain bound the their activity's lifecycle. – corsair992 Feb 26 '15 at 10:08
  • No, I can't let it happen. They are part of Navigation Drawer. They'll consume memory and may (possibly) cause troubles on device rotation or content changes. `DetailFragment` **has** to be controlled by either `DetailActivity` or `SomeFragment` – Drew Feb 26 '15 at 10:19
  • In that case, you may want to consider actually nesting the detail fragment inside the list fragment, or nesting both of them in a container fragment for automatic management. There is no special callback for fragments being removed from the manager - that would defeat the whole point of them being managed after all. All the destruction callbacks are called in the normal sequence of activity destruction. – corsair992 Feb 26 '15 at 10:34
  • I can't use the support library, so can't use child fragment manager. That's why I can't use **real** nested fragments. Destruction callbacks are triggered on removed fragments regardless of current activity being destructed or not. So the question is: *is the replacement described above **potentially risky**?* If there's risk of breaking things, I would maybe just put reusability aside, as switching to support library in current project's state is a nightmare. – Drew Feb 26 '15 at 10:58
  • The only risk I see - is that details fragment's layout holds a layout of a fragment that has been destructed – Drew Feb 26 '15 at 10:59
  • 1
    Well, since you're always replacing the details fragment upon the list fragment's view creation (effectively treating it like a managed view), I guess your approach can work. An alternative way would be to remove both fragments at the same point (where the fragment switching is managed). – corsair992 Feb 26 '15 at 11:09
  • Hmm, in one single transaction, right? Good point, I have to consider this. It's not very flexible, but maybe a bit more error-proof – Drew Feb 26 '15 at 11:11

0 Answers0