I have a quick question.
If I add a Fragment by:
getFragmentManager().beginTransaction().add(...)
will it be always accessible by:
getFragmentManager().findFragmentByTag(...)
assuming I will never call beginTransaction().remove ?
(I will only use hide() and show() transactions to manipulate its visibility)
I don't see why not , according to the documentation , it will be accessible , however make sure you don't call .replace() either.
Because .replace() is a like a sequence of .remove().add()
Also transactions do not get added to the back stack by default. You can:
transition.addToBackStack("TAG");
After that you can use its identifier without problem, if you want to refer to it later.
Sure, Fragment is always accessible as long as it is attached to the activity. Fragment gets detached as soon as onDetach is called.
onDetach Called when the fragment is no longer attached to its activity. This is called after onDestroy(), except in the cases where the fragment instance is retained across Activity re-creation (see setRetainInstance(boolean)), in which case it is called after onStop().
For visibility, you are using hide() and show(),
hide() and show() is only relevant for fragments which are already attached to activity.
In short, onDetach never gets called during hide() and show().A attached fragment never get detached from the activity during hide() and show().So ,it will be accessible.
Related
Before Google architecture component and LiveData I have not paid attention to onActivityCreated() callback.
I read about this here in SOF as well in documentation, and i still cannot understand the behavior.
From one of SOF answers:
As the name states, this is called after the Activity's onCreate() has
completed.
In which conditions onActivityCreated() called and when onActivityCreated() not called?
is it possible that onCreateView() called but onActivityCreated() not called?
It common practice to attach LiveData observers in onActivityCreated(), so i guess there are significant difference between onActivityCreated() and onCreateView()?
Although looking on the diagram from official Android docs seems like it onActivityCreated() is called always after onCreateView()(in terms of execution, not order) and there no diffrence?
Something confusing here.
UPDATE:
onActivityCreated() deprecated.
EDIT: According to Ian Lake on Twitter (see https://twitter.com/ianhlake/status/1193964829519667202), the fact that FragmentActivity attempts to dispatch onActivityCreated in onStart is irrelevant, because no matter what happens, the FragmentManager dispatches it anyway when going from onCreate to onStart.
case Fragment.CREATED:
// We want to unconditionally run this anytime we do a moveToState that
// moves the Fragment above INITIALIZING, including cases such as when
// we move from CREATED => CREATED as part of the case fall through above.
if (newState > Fragment.INITIALIZING) {
fragmentStateManager.ensureInflatedView();
}
if (newState > Fragment.CREATED) {
fragmentStateManager.createView(mContainer);
fragmentStateManager.activityCreated(); // <--
fragmentStateManager.restoreViewState();
So what I said below is actually wrong.
Apparently using onActivityCreated inside a Fragment is equivalent to using onViewCreated.
But this also means you shouldn't rely on onActivityCreated to know if your Activity was actually created, because it is called more times than when the Activity is actually created.
Fragments are confusing.
ORIGINAL ANSWER:
Is it possible that onCreateView() called but onActivityCreated() not called?
UPDATE: No, it's not possible.
ORIGINAL: Yes, in a FragmentPagerAdapter they use FragmentTransaction.detach / FragmentTransaction.attach, which causes the View to be destroyed, but the Fragment stays alive (stopped, but not destroyed).
In this case, .attach() runs onCreateView, but not onActivityCreated.
It's common practice to attach LiveData observers in onActivityCreated(), so i guess there are significant difference between onActivityCreated() and onCreateView()?
UPDATE: it doesn't matter, although onViewCreated is still clearer.
ORIGINAL: It's actually a bad practice, and should be done in onViewCreated, providing getViewLifecycleOwner() as the lifecycle owner.
Although looking on the diagram from official Android docs seems like it onActivityCreated() is called always after onCreateView() and there no diffrence?
UPDATE: Despite that the FragmentActivity only tries to dispatch it once, all Fragments always go through onActivityCreated, because that's just how the FragmentManager works.
ORIGINAL: It is not always called after onCreateView, in fact, it's more-so called "before onStart, but only once".
/**
* Dispatch onStart() to all fragments.
*/
#Override
protected void onStart() {
super.onStart();
mStopped = false;
if (!mCreated) {
mCreated = true;
mFragments.dispatchActivityCreated(); // <---
}
mFragments.noteStateNotSaved();
mFragments.execPendingActions();
// NOTE: HC onStart goes here.
mFragments.dispatchStart();
}
UPDATE: but apparently this doesn't really matter to the FragmentManager, because it goes CREATED -> ACTIVITY_CREATED -> STARTED either way.
Fragments are recreated automatically when their parent Activity or Fragment is recreated. If the child fragments were related to state that is not retained between instances of their parent, when should I remove them?
In the parent fragment's onDestroy(): unreliable since onDestroy() might not be called.
In the parent fragment's onCreate(): presumably the children have not yet been created at this point.
Some other lifecycle method that is guaranteed to be called after the children have been recreated and added. Is onViewStateRestored(...) the right place for this?
In case my question isn't clear, here's an example:
An Activity has a Fragment which contains an asynchronous operation. The fragment would normally cancel this task in onDestroy(). But if the fragment is destroyed without onDestroy() being called, it may later find itself recreated with the background task uninitialized. In that case, it should remove its old progress dialog. When should it test for this condition?
Edit: When the user swipes the app out of recents, all its components are destroyed without calls to onDestroy(). But in that case, the fragment hierarchy is apparently obliterated along with the rest of the app. When the app is restarted, the fragment is not automatically recreated, so I don't have to worry about removing it.
When the fragment is destroyed because its host activity is put in the background and "don't keep activities" is turned on, the fragment is automatically recreated. But in that case, it seems I can count on onDestroy() being called.
My concern is what happens when the app is killed to free memory. Hopefully it will behave like swiping from recents, where the fragment hierarchy is not restored. That would render my whole question moot. Can anyone confirm what happens in that case?
The docs seem to indicate onpause will always be called
https://developer.android.com/guide/components/fragments.html
or if you know the activity is going to be destroyed onDetach() must be called since if the activity is destroyed the fragment can't be attached.
A lot of threads ask the question how to run code or get the visibility of the fragment.
But I would like to know "why" the Fragments: onResume() and onStart() are 'not' visible to the user, although the documents state it should be visible to the user.
From: http://developer.android.com/reference/android/app/Fragment.html#Lifecycle
onStart() makes the fragment visible to the user (based on its containing activity being started).
onResume() makes the fragment interacting with the user (based on its containing activity being resumed).
http://developer.android.com/reference/android/app/Fragment.html#onResume()
and onStart() for that matter, clearly describe:
Called when the fragment is visible to the user and actively running. This is generally tied to Activity.onResume of the containing Activity's lifecycle.
While I clearly see no fragment, till after onResume has completed. Soo the question remains: 'why' is the fragment only 'visible' after onResume. and not from onStart as per doc?
Oh and the expand on this: I use no ViewPager.. Just a simple Activity-Fragment model.
Hope anyone has some intel on this...
[UPDATE*] I have added a sample project with one activity and one fragment showing the issue. For anyone to try ;-)
https://www.dropbox.com/s/08noqvmq7sjwppb/fragmentUserVisibilityTest.zip
Perhaps, you have a long running operation in your onStart() which happens in the main thread. In this case you won't see anything until this operation ends. Which happens by the time onResume is called. Consider using AsyncTask for long operations.
onStart() gets called when a fragment is about to be shown, onResume() is called when a fragment is about to be interactible. That's the same as for an Activity.
onStart() and onResume() would have been of no use, if they had been called after a fragment becomes visible/interactible.
I want to use setRetainInstance(true) on my FragmentActivity so that onCreate() will not be called every time the screen rotates. I just want to adjust the layout to the screen adjusting without reestablishing my location services connection and notifying the user. How should this be done?
Start by reading the Fragment documentation about what setRetainInstance does (And does not).
In summary:
public void setRetainInstance (boolean retain)
Added in API level 11 Control whether a fragment instance is retained
across Activity re-creation (such as from a configuration change).
This can only be used with fragments not in the back stack. 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.
With that in mind, make sure your "location services connection" and such are located in a place where they will not die (they probably shouldn't be in either Activity or Fragment anyway).
This paragraph is taken from a book I'm currently reading "Professional Android 4 Application Development"
If an Activity is destroyed and restarted to handle a hardware
configuration change, such as the screen orientation changing, you can
request that your Fragment instance be retained. By calling
setRetainInstance within a Fragment’s onCreate handler, you specify
that Fragment’s instance should not be killed and restarted when its
associated Activity is re-created.
I think that's clear enough.
Regards!
I have a fragment that does a startActivityForResult() call in OnCreateView if there is no internet. In the NoInternet Activity there is a retry button that finish()es the activity (So I can supposedly check for connection again). When the activity finishes, OnCreateView of the fragment is never called (because OnCreate() of the host Activity is never called) and I end up not cheking if there is internet again.
Now a simple way around this is to check if there is internet in the OnStart() of the main activity (that hosts the fragment).
But I was wondering: Is there a way to force OnCreateView after finish()ing an Activity that was started with startActivityForResult()?
I think your problem is the understanding of the lifecycle of Fragments. If you call startActivityForResult() on a Fragment, it will be executed on the fragment's containing Activity. Now the Activity goes in the Paused and then Stopped state and also will the Fragment. If you finish the "ResultActivity" the states Started and Resumed will be passed through on the Activity and the Fragment.
So, how you said already, just move your connection checking and your startActivityForResult() call into onStart().
There is no need to create a new view for Fragment. If you want to alter the View programmetically you can do that in onStart() and onResume().
But don't break the lifecycle just for a method call which could be easiely invoked in another callback.
I'm not sure exactly how you're using the fragments/activities in question but it sounds like the fragment view of the main activity is not being destroyed when you call startActivityForResult() for the new activity. This behavior is normal under some cases which means which your fragment would only be stopped or paused. So, doing your network check in the OnStart() or OnResume() would be the correct usage.