I wonder if there is any advantage of fragments, on screen rotation.
Generally fragments get destroyed followed by activity. Is there something that fragments retain while doing so?
onDestroy() method is called both in the activity and fragments.
I can try to figure out advantage of Fragment on Screen rotation.
Realtime app problem is:
Android is the potentially frequent destruction and reconstruction of an Activity. The most common time this occurs is when the user rotates the device between horizontal and portrait orientations (Screen rotation).
This crashing usually occurs because device orientation changes cause the Android framework to tear down the displayed Activity along within any contained Views, and then to fully reconstruct the Activity/View hierarchy. Any references to the Activity or to the Views within the Activity suddenly become invalid. Similarly any references within the Activity or Views that were set as a result of a user action or similar are now lost.
There are a number of ways to deal with this issue but one of the easiest is to take advantage of Fragments.
Things to keep in mind:
Fragments won’t automatically resolve this issue because, by default, when the Activity is torn-down in response to an orientation change the Fragment contained within the Activity is also torn down along with any contained Views.
The solution lies in an underused method: Fragment.setRetainInstance with a value of true.
why?
Calling setRetainInstance with a value of true causes Android to preserve the Fragment across the teardown/reconstruction cycle of an Activity. Along with the Fragment, the Views or other object references contained within the Fragment or Views remain.
With setRetainInstance(true) called on a Fragment instance.when an orientation change occurs, Android…
Holds a reference to the Fragment instance
Tears down the old Activity instance
Creates a new Activity instance
Attaches the preserved Fragment instance to the new Activity instance
you must add
android:configChanges="keyboardHidden|orientation|screenSize"
in parent Activity also calling
setRetainInstance(true)
in onCreate of fragment
Related
In my program, I have the following hierarchy:
Activity
Fragment
ViewPager + FragmentStatePagerAdapter
Fragment containing video
The fragment that is nested immediately in the activity is initialized using setRetainInstance(true). This led to a crash whenever switching orientation. Used the solution described in the bug report: https://code.google.com/p/android/issues/detail?id=42601#c10.
Still, the app would crash whenever I would switch orientation. I found another bug report + solution: https://code.google.com/p/android/issues/detail?id=42601#c32.
After applying this solution I was able to rotate the device without the app crashing. However, the deeply nested fragment did not retain its state. As this fragment contains a video that should continue playing in spite of any orientation change, this is a must-have. I found a third bug report here describing this is a known issue in android support library versions 20 an up: https://code.google.com/p/android/issues/detail?id=74222#c17
Now, it does seem that the deepest fragment is retaining some kind of state. At least it is not destroyed, since the audio of the video keeps playing throughout and after the orientation change. However, the fragment is not restored after the orientation change. I simply see a white rectangle where the video should be.
As you can see, this is getting ridiculous. I have already had to use three hacky solutions to bugs in the ViewPager class, and it is still not working properly. If anybody has any idea what else I can try, I will be very much obliged.
Eventually, I did not find a solution to the problem. I have solved the problem by retaining the state of the nested fragments in the parent fragment, which does correctly retain state. To do this, I did the following:
Add a HashMap to the parent fragment
Gave every fragment a unique ID string that could be restored whenever the fragment was recreated
Created a state object for all nested fragments in their onCreate method and added it to the HashMap in the parent fragment. Or, if ((ParentFragment)getParentFragment()).stateMap.contains(id), restore the existing state.
Moved all member variables of the nested fragment to the StateObjects
just need to figure this out. We all know that fragments are attractive in such situations where configuration changes like screen orientation change occur.
This is because Fragments are retained across Activity destruction and recreation and attached to the newly created Activity almost every time if there's a call to the setRetainInstance(true) from the onCreate() Fragment method.
So why it is not the same for the DialogFragment? I mean even if there's no call to the setRetainInstance(true) the dialog is retained across Activity's configuration changes.
Could someone explain me this little difference between a Fragment and a DialogFragment?
Thanks!
I have an interface that has two dynamic parts, one of which is more complicated than the other. I had to spend some time with Bundles and onSaveInstanceState(), etc., in order to preserve the state of the first part when the orientation is changed.
Then I turned to do the same with the second, simpler part, and right away noticed that I did not have to do certain things. The layout for this part is inflated in onCreate(), and it contains TextViews and EditTexts whose content can change. This content is retained, but dynamic changes to the layout are not.
When onCreate() is called by restarting the app from the home screen, the changes are not retained.
I have to implement continuity properly via the Bundle anyway, since what is not retained is the state of an object created in onCreate() corresponding to the view. That, and the fact that the behavior is not consistent (restart from homescreen vs. orientation change) make it hard to see this as a "feature", since it implies inflating a "fresh" layout from XML may not always provide a genuinely fresh layout that corresponds exactly to R.layout.whatever.
I'm also guessing this can happen in the context of an Activity's visible lifetime, not just in onCreate(). So what are the rules here?
This content is retained, but dynamic changes to the layout are not
Retaining user-mutable content of common widgets, like the text entered into an EditText, is automatic, from the built-in implementation of onSaveInstanceState(). However, "views... removed from and added to it" (from a previous edition of your question) is definitely not retained by onSaveInstanceState(), and if you re-inflate the layout, such changes definitely will be lost.
When onCreate() is called by restarting the app from the home screen, the changes are not retained.
Well, that will depend upon whether or not the process is still around and whether or not this activity was on the back stack.
So what are the rules here?
If the user is returning to a specific instance of your activity (configuration change, or starting a fresh process from the recent-tasks list), your saved instance state is applied to the new activity instance that is created.
If the user is starting a fresh process without returning to a specific instance of your activity (e.g., home screen launcher), there is no saved instance state to apply.
I am using fragments in my app. Below is the screenshot of my app. Everything looks fine on activity launch. But, when I change the screen orientation, the layout overlaps itself while scrolling. Any ideas what is wrong?
EDIT (Solution):
I found out what was wrong. A new fragment was added on orientation change. I was previously using fragmentTransaction.add(...), I replaced it with replace method.
For future vistor's understanding...
You are most likely adding your fragment in your activity's onCreate().
When your device is rotated, your activity is destroyed and re-created, but it also recreates any added fragments.
With these two things in mind...
onCreate is called and you add a fragment to your activity. You have one fragment.
You rotate the screen.
The activity is destroyed and re-created. Your fragment is destroyed and recreated. You still have one fragment.
Your activity calls onCreate() as a part of its recreation, which adds a fragment to your activity. You now have two fragments.
If one or both of your fragments have transparent backgrounds, both may display at once, as is likely the case here.
Either use replace instead of add (which you have done), or better, only add the fragment in onCreate() if savedInstanceState is null. If it is null it means it's the first onCreate() call.
Being frustrated about fragment behavior, I started doing some testing.
I have one activity and 2 fragments. Fragment A is declared inside the xml layout of the activity and Fragment B is added (only if it's not present) in the layout of the activity in activity's onCreate() method. I've added logging in all of the main lifecycle methods for the activity and the 2 fragments and tested the behavior when switching orientation back and forth. Here are my findings:
Fragment B (the dynamically-added fragment) behaves as expected:
a) after an orientation change, the savedInstanceState bundle contains what has been previously saved in onSaveInstanceState()
b) if setRetainInstance(true), during an orientation change, onDestroy() is not called and also the subsequent onCreate() is not called. The fragment's fields are preserved during the orientation change
Fragment A (the fragment defined in the xml layout) doesn't behave as expected:
a) after an orientation change, the savedInstanceState bundle is always null although onSaveInstanceState() has been properly called
b) if setRetainInstance(true), during an orientation change, onDestroy() is not called as expected BUT, contrary to what is expected, onCreate() is also called when the fragment is being reattached. And also, the fragment's fields are not preserved.
To sum up, for fragments declared inside xml layouts and using ACL v4, saving state during orientation changes does not work and setRetainInstance(true) does not work.
My question is if someone tested this functionality on Android 3.0+ and can say if fragments work correctly when using fragments from Android SDK.
One workaround to this problem would be to always dynamically create my fragments. Did anyone find a different workaround?
Revision 4 of ACL fixed these issues.