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 Fragment
s 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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With