When I go from Fragment A to B, Fragment B's onResume() is called first and then Fragment A's onPause() is called. Which is different from Activity lifecycle.
How to solve this?
Check out - https://androidlearnersite.wordpress.com/2017/02/27/fragment-lifecycle-during-fragment-transaction/ ..
It explains fragment lifecycle during fragment transaction with latest appcompat version.
As per latest appcomat version, if setAllowOptimization() is set to true then during replacing fragments onResume() is called before onPause() otherwise onPause() is called before onResume().
setAllowOptimization() is used to set whether or not to allow optimization operation within and across transactions.
Related
I've a fragment A. I add() it with tag like this:
fragmentTransaction.addToBackStack(special_tag);
Then I simply add() fragment B on top of fragment A. After that, I decide to remove fragment B and go back to fragment A using:
activity.fragmentManager.popBackStackImmediate(special_tag, 0)
When I reach the fragment A, it seems that fragment doesn't re-run it's lifecycle methods: onAttach(), onResume(), onCreate() ect.
Can someone explain this behavior and maybe suggest an alternative?
(I need to "refresh" the data when I come back to fragment A second time)
What is causing this result?
Is there a clean solution/work-around?
Update
Fragment B is GuidedStepFragment and does not have a .replace() function. I found that it has finishGuidedStepFragments(), but it behaves the same (it does not call fragment life cycle functions)
Situation (again):
Fragment A (Simple fragment) -> .add(Fragment B) (GuidedStepFragment) -> popBackStackImmediate() or finishGuidedStepFragments()
I add Fragment B like this:
GuidedStepFragment.add(activity.fragmentManager, fragmentB.createInstance())
Using fragmentTransaction.add(Fragment) doesn't remove Fragment A. What is actually happening is that Fragment A is still running behind Fragment B. Since Fragment A never stopped running, it's lifecycle has no need to retrigger.
Consider using fragmentTransaction.replace(Fragment) and replace the fragment in the container (fragment A) with fragment B. If you pop that transaction from the back stack, then Fragment A will reattach and follow your expected lifecycle.
Update
Since you seem to be using GuidedStepFragments from the leanback library, this is a little tricky. GuidedStepFragment actually performs replace(...) under the hood, but you're adding fragment B to a different container so the original behavior I mentioned doesn't apply.
I'm not super familiar with leanback (since it's usually only used for android tv), but I do know that you can at least do the following. If you keep track of your backstack size, when all of the GuidedStepFragments have been popped, you will have returned to your original fragment. For example, let's assume your backstack starts at zero:
activity.fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (activity.fragmentManager.getBackStackEntryCount() == 0){
// handle your updates
}
}
});
// the next line of code will add an entry to the backstack
GuidedStepFragment.add(activity.fragmentManager, fragmentB.createInstance());
// eventually when back is pressed and the guided fragment is removed, the backstack listener should trigger
Hi I have a question about retaining fragment when Activity is recreated.
I heard one way is to use setRetainFragment(true) in the onCreate method.
Question is - how is this different from keeping track of private Fragment property in Activity so that I always have the same Fragment object throughout the activity's lifetime? Thanks!
setRetainInstance(true): The Fragment's state will be retained (and not destroyed!) across configuration changes (e.g. screen rotate). The state will be
retained even if the configuration change causes the "parent" Activity to be destroyed. However, the view of the Fragment gets destroyed!
Lifecycle Calls:
onPause() -> onStop() -> onDestroyView() -> onDetach()
onAttach() -> onCreateView() -> onStart() -> onResume()
setRetainInstance(false): The Fragment's state will not be retained across configuration changes (default).
Lifecycle Calls:
onPause() -> onStop() -> onDestroyView() -> onDestroy() -> onDetach()
onAttach() -> onCreate() -> onCreateView() -> onStart() -> onResume()
Important: setRetainInstance(true) does not work with fragments on the back stack. setRetainInstance(true) is especially useful for long running operations
inside Fragments which do not care about configuration changes.
If you mean a private property it means a property within the class so each time the activity is recreated a new instance with a new private fragment is created for Example at t=t1 the instance of Activity A is created so it contains all its private variables and in t=t2 a new instance of the Activity A is created and so when you set setRetainFragment(true)the Android framework under the hood retain your fragments without recreating them and preserves it state.
You can refer to this link for further information Understanding Fragment's setRetainInstance(boolean)
Is there any way, how to clear backStack of support FragmentManager without calling onCreateView() in stored fragments?
I understand fragment lyfe cycle and calling onDestroyView() and onCreateView() when it is popped.
http://developer.android.com/guide/components/fragments.html#Creating
Also I know how to pop all fragments from backstack with
mFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
or
for(int i = 0; i < mFragmentManager.getBackStackEntryCount(); ++i) {
mFragmentManager.popBackStack();
}
but both ways are calling onCreateView() and other lyfe cycle methods until to onDestroyView() and onDestroy().
But is there any way, how to clear this backstack with calling only from onDestroyView() and not from onCreateView() (inside of fragments)?
Or is there any way how to do replace transaction with clearing previous fragments?
For example, I want clear backstact before I do transaction:
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragmentManager.beginTransaction().replace(R.id.content, fragment).commit();
I haven't found a simple solution to this issue. I'm almost certain there is no feature of FragmentManager or Fragment that allows you to control which lifecycle method are called when a fragment is popped from the stack. I'll outline two possible approaches. Each has some undesirable aspects.
The first approach assumes the fragments you are popping out of the backstack are children of an activity. If they are children of a fragment, the method still applies, just a different type of parent object.
Add a boolean member mClearingBackStack to the activity (or parent fragment) with a getter method. Set the boolean only when you are starting a complete clear of the backstack. In the fragment lifecycle methods where you want to disable processing, get the flag and modify the processing accordingly. For onCreateView() through onDestroyView(), the fragment will be attached and the host activity available with getActivity(). Cast it to whatever your activity class is to use the flag's getter method.
Because popBackStack() is asynchronous, clearing the flag must be done only after the stack unwinding completes. I haven't tried it, but I think posting a Runnable after calling popBackStack() to clear the flag should work. Because the Runnable needs to go at the end of the queue, View.post() must be used instead of Activity.runOnUiThread(). An alternative is to call executePendingTransactions() to wait for the stack unwinding to complete.
The second approach is cleaner, if your design can accommodate it. Create a place-holder fragment that is a child of your activity and parent to all your other fragments. For all the fragment transactions you have now, use the new fragment's FragmentManager, obtained using getChildFragmentManager(). When you want to clear all those transactions, instead of popping the child fragment manager's stack, remove or replace the parent fragment in the activity's fragment manager. When the parent fragment is removed, all of its children are destroyed and go through the teardown steps, onDestroyView(), onDestroy(), etc. but not all the steps that would occur if its backstack were unwound. This approach is much simpler and more maintainable than the first, but requires you to rework your fragment hierarchy. An additional problem with this approach is that you must add some code to handle the Back action with a fragment hierarchy. The problem and various solutions are described here.
In a Fragment's Lifecycle, the onAttach() method is called before the onCreate() method. I can't wrap my head around this. Why would you attach a Fragment first?
TL;DR:
In order to not break the design consistency amongst different UI components in android,the onCreate() method will have similar functionality across all of them.
When linking Containers to Contents like Window to Activity and Activity to Fragment a preliminary check needs to be done to determine the state of container.
And that explains the use and position of onAttach() in the fragment lifecycle.
Too short;Need longer:
The answer is in the archetype code itself,
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
Another example would be in Jake Wharton's ActionBarSherlock library.
Why would you want to use a method like onCreate() which is has the same purpose in an activity ,service.
The onCreate() is meant to handle issues with respect to that particular context creation.It does not make sense if onCreate() is used to check the state of its container.
The second reason that I can come determine is that a fragment is designed to be activity independent.The onAttach() provides an interface to determine the state/type/(other detail that matters to the fragment) of the containing activity with reference to the fragment before you initialize a fragment.
EDIT:
An activity exists independently and therefore has a self sustaining lifecycle.
for a fragment :
The independent lifecycle components(same as any other components):
onCreate()
onStart()
onResume()
onPause()
onStop()
onDestroy()
The interaction based components:
onAttach()
onCreateView()
onActivityCreated()
onDestroyView()
onDetach()
from the documentation:
The flow of a fragment's lifecycle, as it is affected by its host
activity, (...) each successive state of the activity determines which
callback methods a fragment may receive. For example, when the
activity has received its onCreate() callback, a fragment in the
activity receives no more than the onActivityCreated() callback.
Once the activity reaches the resumed state, you can freely add and
remove fragments to the activity. Thus, only while the activity is in
the resumed state can the lifecycle of a fragment change
independently.
However, when the activity leaves the resumed state, the fragment
again is pushed through its lifecycle by the activity.
answering another question which came up in the comments:
Caution: If you need a Context object within your Fragment, you can call getActivity(). However, be careful to call getActivity() only when the fragment is attached to an activity. When the fragment is not yet attached, or was detached during the end of its lifecycle, getActivity() will return null.
The design philosophy states that a Fragment is designed for reuse. A fragment (by design) could(and should) be used across multiple activities.
The onCreate by definition is responsible to create a fragment.
Consider the case of orientation,your fragment could be:
- using different layouts in different orientations.
- applicable only in portrait orientation and not landscape
- To be used only on tables and mobile phones.
All these situations would require a check before the fragment is initialized from the android perspective(onCreate()) and the view inflated(onCreateView()).
Also consider the situation of a headless fragment.The onAttach() provides you the interface required for preliminary checks.
Because onAttach() assigns hosting activity to the Fragment. If it had been called after onCreate() then there would be no context for your fragment (getActivity() would return null) and you would not be able to do anything in onCreate() method without that context anyway.
Another fitting reason is that Fragment's lifecycle is similar to Activity's lifecycle. In Activity.onAttach() activity gets attached to its parent (a window). Similarly in Fragment.onAttach() fragment gets attached to its parent (an activity), before any other initialization is done.
This is related to retained fragments. Following Fragment setRetainInstance(boolean retain) documentation:
If set, the fragment lifecycle will be slightly different when an activity is recreated:
onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being re-created.
onAttach(Activity) and onActivityCreated(Bundle) will still be called.
Take a look at the source code (android.support.v4.app.FragmentManager, v21):
void moveToState(Fragment f,
int newState,
int transit,
int transitionStyle,
boolean keepActive) {
...
f.onAttach(mActivity);
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
if (f.mParentFragment == null) {
mActivity.onAttachFragment(f);
}
if (!f.mRetaining) {
f.performCreate(f.mSavedFragmentState); // <- Here onCreate() will be called
}
...
}
Example
Case 1: not retained fragment or setRetainInstanceState(false)
Application is started. Fragment is added dynamically using FragmentManager or inflated from XML via setContentView().
onAttach() called after Activity super.onCreate() call - Activity is already initialised.
MainActivity﹕ call super.onCreate() before
MainActivity﹕ super.onCreate() returned
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#1be4f2dd
MainFragment﹕ onCreate() getActivity=com.example.MainActivity#1be4f2dd
Configuration changed. Activity recreates fragments from saved state, fragments are added/attached from inside Activity super.onCreate() call:
MainActivity﹕ call super.onCreate() before
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#2443d905
MainFragment﹕ onCreate() getActivity=com.example.MainActivity#2443d905
MainActivity﹕ super.onCreate() returned
Case 2: setRetainsInstanceState(true)
Application is started. Fragment is added dynamically using FragmentManager or inflated from XML via setContentView(). Same as above:
onAttach() called after Activity super.onCreate() call - Activity is already initialised.
MainActivity﹕ call super.onCreate() before
MainActivity﹕ super.onCreate() returned
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#3d54a168
MainFragment﹕ onCreate() getActivity=com.example.MainActivity#3d54a168
Configuration changed.
Fragment onCreate() not called, but onAttach() still called - you need to know, that hosting Activity has changed. But still fragment is already created, so no onCreate() called.
MainActivity﹕ call super.onCreate() before
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#d7b283e
MainActivity﹕ super.onCreate() returned
Two Points from Android developer Site hints at why onAttach() is called before onCreate() in case of Fragment Life cycle.
A fragment must always be embedded in an activity. Now this means for Fragment to EXIST, there has to be a "living" Activity.
Also, When you add a fragment as a part of your activity layout, it lives in a ViewGroup inside the activity's view hierarchy.
So Fragment must FIRST "attach" itself to activity to defines its own view layout
onCreateis Called to do initial creation of a fragment.
It is obvious that you will create something only when its pre-condition of creation is in place (and the pre-condition is A fragment must always be embedded in an activity, and it must be attached to its Activity)
I really need your help here.
I have two Fragments:
1. Fragment A
2. Fragment B
One Interface
1. onSkillsSelectedListener ( method onDoneClicked )
Fragment A implements onSkillsSelectedListener and when "DONE" button is called in Fragment B, i call onDoneClicked() , and Fragment B is destroyed.
Now method onDoneClicked in Fragment A is called before even OnResume in Fragment A is called so i cannot make any changes in my Layout.
How to fix this issue?
The fragments onResume() or onPause() will be called only when the Activities onResume() or onPause() is called. They are tightly coupled to the Activity.
http://developer.android.com/guide/components/fragments.html#Lifecycle
You need to find another way to implement this.
I FIX IT
Just changed the way i called my Fragment B
from replace to add
So now Fragment A is not even Paused and callback is working!
Cheers!