Issue
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.
Solution
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.
Answered By - Kéza
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.