Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Activity and Fragment Lifecycles and Orientation Changes

I have been having very odd issues with Fragments and orientation changes that have been causing force closes and not following a logical pattern.

I created a simple Activity and Fragment lifecycle debugging application which simply implements every step of the Activity lifecycle and Fragment lifecycle by reporting the call to the logcat.

Here are the TestActivity and TestFragment classes:

TestActivity

public class TestActivity extends Activity {
    Context ct = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.e("ACT", "onCreate called");

        ct = getApplicationContext();

        FrameLayout fl = new FrameLayout(ct);
        fl.setId(1000);

        TestFragment tf = new TestFragment();
        getFragmentManager().beginTransaction().add(fl.getId(), tf, "").commit();

        setContentView(fl);
    }

    @Override
    protected void onStart() {
        Log.e("ACT", "onStart called");
        super.onStart();
    }

    @Override
    protected void onResume() {
        Log.e("ACT", "onResume called");
        super.onResume();
    }

    @Override
    protected void onPause() {
        Log.e("ACT", "onPause called");
        super.onPause();
    }

    @Override
    protected void onStop() {
        Log.e("ACT", "onStop called");
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        Log.e("ACT", "onDestroy called");
        super.onDestroy();
    }

    @Override
    protected void onRestart() {
        Log.e("ACT", "onRestart called");
        super.onRestart();
    }
}

TestFragment

public class TestFragment extends Fragment {
    Context ctFrag = null;

    @Override
    public void onAttach(Activity activity) {
        Log.e("FRAG", "onAttach called");
        super.onAttach(activity);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.e("FRAG", "onCreate called");

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        Log.e("FRAG", "onCreateView called");

        ctFrag = ((TestActivity) getActivity()).ct;

        TextView tv = new TextView(ctFrag);
        tv.setText("My test TextView");

        return tv;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        Log.e("FRAG", "onActivityCreated called");
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onStart() {
        Log.e("FRAG", "onStart called");
        super.onStart();
    }

    @Override
    public void onResume() {
        Log.e("FRAG", "onResume called");
        super.onResume();
    }

    @Override
    public void onPause() {
        Log.e("FRAG", "onPause called");
        super.onPause();
    }

    @Override
    public void onStop() {
        Log.e("FRAG", "onStop called");
        super.onStop();
    }

    @Override
    public void onDestroyView() {
        Log.e("FRAG", "onDestroyView called");
        super.onDestroyView();
    }

    @Override
    public void onDestroy() {
        Log.e("FRAG", "onDestroy called");
        super.onDestroy();
    }

    @Override
    public void onDetach() {
        Log.e("FRAG", "onDetach called");
        super.onDetach();
    }
}

On initialising, the Logcat output follows the expected order (start Activity, when the Fragment is attached, its lifecycle calls happen, etc):

01-29 10:12:50.270: E/ACT(3321): onCreate called
01-29 10:12:50.760: E/FRAG(3321): onAttach called
01-29 10:12:50.760: E/FRAG(3321): onCreate called
01-29 10:12:50.760: E/FRAG(3321): onCreateView called
01-29 10:12:50.770: E/FRAG(3321): onActivityCreated called
01-29 10:12:50.770: E/ACT(3321): onStart called
01-29 10:12:50.770: E/FRAG(3321): onStart called
01-29 10:12:50.770: E/ACT(3321): onResume called
01-29 10:12:50.770: E/FRAG(3321): onResume called

But the issue is, when an orientation change occurs, the Android Documentation says that:

When such a change occurs, Android restarts the running Activity (onDestroy() is called, followed by onCreate())

Which would suggest that it should shut down the Activity and its contents like the lifecycle suggests (and it does) BUT then go through the same ordered process recreating the Activity in the new orientation. This doesn't happen and it seems that the Fragment is attempted to be recreated and then a new one is created in the activity recreation.

01-29 10:17:52.249: E/FRAG(3321): onPause called
01-29 10:17:52.259: E/ACT(3321): onPause called
01-29 10:17:52.269: E/FRAG(3321): onStop called
01-29 10:17:52.269: E/ACT(3321): onStop called
01-29 10:17:52.279: E/FRAG(3321): onDestroyView called
01-29 10:17:52.299: E/FRAG(3321): onDestroy called
01-29 10:17:52.299: E/FRAG(3321): onDetach called
01-29 10:17:52.299: E/ACT(3321): onDestroy called
01-29 10:17:52.650: E/FRAG(3321): onAttach called
01-29 10:17:52.650: E/FRAG(3321): onCreate called
01-29 10:17:52.650: E/ACT(3321): onCreate called
01-29 10:17:53.020: E/FRAG(3321): onCreateView called
01-29 10:17:53.020: E/FRAG(3321): onActivityCreated called
01-29 10:17:53.030: E/FRAG(3321): onAttach called
01-29 10:17:53.030: E/FRAG(3321): onCreate called
01-29 10:17:53.030: E/FRAG(3321): onCreateView called
01-29 10:17:53.030: E/FRAG(3321): onActivityCreated called
01-29 10:17:53.060: E/ACT(3321): onStart called
01-29 10:17:53.060: E/FRAG(3321): onStart called
01-29 10:17:53.060: E/FRAG(3321): onStart called
01-29 10:17:53.060: E/ACT(3321): onResume called
01-29 10:17:53.060: E/FRAG(3321): onResume called
01-29 10:17:53.060: E/FRAG(3321): onResume called

Obviously there are plenty of solutions to solve this, but my question is why does this happen? Why is a Fragment reference maintained and recreated when it is supposed to be part of that Activity which is supposedly completely destroyed and recreated? I can justify that by Fragments intentionally being separate to activities. But what was causing the issues, is why is the original Fragment attached and recreated before the Activity is? It just doesn't seem to follow the logical lifecycle that the rest of the Android process does.

like image 736
btalb Avatar asked Jan 30 '13 02:01

btalb


People also ask

What is difference between activities and fragments?

Activity is an application component that gives a user interface where the user can interact. The fragment is only part of an activity, it basically contributes its UI to that activity. Fragment is dependent on activity. It can't exist independently.

What will happen if an activity with retained fragment is rotated?

Fragments — Scenario 3: Activity with retained Fragment is rotated. The fragment is not destroyed nor created after the rotation because the same fragment instance is used after the activity is recreated.

What is the difference between activity life cycle and fragment lifecycle?

A fragment can be reused in multiple activities, so it acts like a reusable component in activities. Lifecycle methods are hosted by OS. The activity has its own life cycle. Lifecycle methods in fragments are hosted by hosting the activity.

How Fragment life cycle is related with activity life cycle?

A fragment life cycle is closely related to the lifecycle of its host activity which means when the activity is in the pause state, all the fragments available in the activity will also stop. Fragments added to the Android API in Android 3.0 which API version 11 to support flexible UI on large screens.


1 Answers

This happens because the activity calls onSaveInstanceState(Bundle) before being destroyed. By default, the activity is saving the states of its fragments in this method.

Later, when the activity is re-created, the old fragments are re-created in the activity onCreate(Bundle savedInstanceState) method.

You might want to check the source code here and here to better understand this behaviour.

like image 187
Kéza Avatar answered Oct 13 '22 16:10

Kéza