If my Fragment implements an interface and another class, say the activity holds a reference to the fragment.
IFragmentInterface myFragmentReference;
At what point in the fragments lifecycle is it no longer available? And can this be detected by checking if the myFragmentReference is null? (I am aware there is a FragmentManager class).
I'm not exactly sure when the reference becomes invalid, but looking at the Android developer page on Fragments (http://developer.android.com/guide/components/fragments.html) it would appear to be in onDetach.
You can't detect this by checking if myFragmentReference is null, because the reference isn't null... it's just pointing to a Fragment that no longer exists.
The best way to handle this is to use something like the following whenever you need to do something with the fragment:
FragmentManager fm = getFragmentManager();
IFragmentInterface myFragmentReference = (IFragmentInterface) fm.findFragmentByTag(getTag());
You'll need to set a tag when adding the fragment to use findFragmentByTag().
The Fragment itself is not usable as a Fragment anymore after onDestroy() is called. Your reference stays valid as long as you keep it. You can access all the methods from your interface, but you can just not use it as a Fragment anymore.
Related
In the documentation of the android.support.v4.app.Fragment class (not the framework's class) for the getActivity() method stated that the returned value may be null if the Fragment is associated with a Context.
How an Fragment can be associated with a Context? Is not the FragmentManager the only way to attach an Fragment to something? But the FragmentManager can be obtained only from Activity. Or not?
How an Fragment can be associated with a Context?
You must have heard of FragmentHostCallback. If you haven't check out the link.
In a simple way, it is an integration point with a Fragment Host. When I say Fragment Host, It is an object that can hold Fragments. For example an Activity. In order to host a fragment - one must implement FragmentHostCallback.
However, I haven't come up with any ideas about how Fragment can be implemented in non-activity objects. Will see in future may be...
So that way, getActivity() will return null on non-activity objects.
PS,
Always go for getContext() if you are requiring context rather than activity
As I understand, you need the context inside of a fragment.. If so have you checked the method getContext() method inside of a fragment?
Also getActivity() can be null if you are referencing it when the fragment is not attached to an activity. Have a check of the fragment lifecycle to learn more.
Hope I helped
For some reason my understanding was that a headless Fragment lives for the duration of your application. With this understanding, in my attempt to persist an object between startActivityForResult() I put the object in a Headless Fragment like this
private HeadlessFragment modelFragment;
modelFragment = (HeadlessFragment)
getSupportFragmentManager().findFragmentByTag(Constants.HEADLESS_FRAGMENT_TAG);
if (modelFragment == null){
modelFragment = new HeadlessFragment();
}
modelFragment.setInvoice(invoice);
I can confirm that the custom object was set, however when I go to the next activity and try to get the same object by calling findFragmentByTag with same tag the object is null.
Does a Headless Fragment survive between two Activities life cycle? I did set setRetainInstance(true) on that Headless Fragment. I was hoping that I will not have to implement Parceable on my custom object.
For some reason my understanding was that a headless Fragment lives for the duration of your application.
No. Fragments are owned by activities and are not application-wide constructs.
I can confirm that the custom object was set, however when I go to the next activity and try to get the same object by calling findFragmentByTag with same tag the object is null.
There are at least two reasons for this:
First, at least in the code that you are showing, you never add the fragment to the FragmentManager via a FragmentTransaction. As such, the activity that created the fragment will not be able to find the fragment via findFragmentByTag(), because the FragmentManager does not know about it.
Second, each activity has its own FragmentManager, and fragments from one activity are not accessible in another activity.
I was hoping that I will not have to implement Parceable on my custom object.
Then don't pass the object. Pass the information (e.g., a key or ID) by which the other activity can retrieve the object (from a singleton POJO cache, by querying the database, etc.).
Or, do not make them separate activities, but have them as separate (regular) fragments in one activity.
Or, implement Serializable, though Parcelable executes more quickly.
I tried to use putFragment to save reference for fragments for using it in future (and not recreate) before replace.
BaseFragment last = (BaseFragment) mContext.getSupportFragmentManager().getFragments().get(mContext.getSupportFragmentManager().getFragments().size() - 1);
mContext.getSupportFragmentManager().putFragment(mContext.getBundle(), last.getType().toString(), last);
And before creating new fragment i check bundle for fragment existing:
BaseFragment f = (BaseFragment) getSupportFragmentManager().getFragment( mBundle, type.toString());
if(f != null)
return f;
else
// create new fragment
FragmentType is just my enum:
public static enum FragmentType{
PROJECTS,
BALANCE
}
But for all fragments (for all keys in bundle) it generates same integer value.
So getFragment method returns wrong fragment. Where is the problem?
I saw this post with same issue. But it still is not resolved...
I needed it for storing fragment state while replacing it. I tried it after this answer.
Use cases of these methods are described in this question: get/putFragment()
Also, don't use getFragments(); It is a method annotated by #Hide and is not supposed to be used, because android can destroy an recreate activities and fragments at any given time, so the list is not always correct.
While the methods do give acces to the 'pointer' of the fragment, It is meant to only restore state.
Java is a language with a garbage collector, and android builds on top of that with objects that do not give us control over their lifecycle.
These methods (put, get) are meant to be used inside onSaveInatanceState and onRestoreInstanceState to save the "state" of the fragment so that it (the state) can be restored later.
Are you using them in that context?
If not, you are essentially trying to override the way the OS handles the fragments' lifecycle, which is asking for trouble.
In other words, don't try to get back the same instance, the system just cannot guarantee that to you.
There is an interesting discussion on this issue over at the android developers
I am using the Fragment class with the Android support library v7. In my Activity's onCreate() method I create a bunch of fragments and store them into properties of my activity.
this.firstFragment = new FirstFragment();
this.secondFragment = new SecondFragment();
// and so on
I use the navigation drawer pattern to switch between the fragments of my app. To change the active fragment I use following code.
// Replace the current content of the fragment holder with the selected section fragment.
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.container, selectedFragment).commit();
This results in a call to onDestroy() of the removed fragment. Is it safe to reuse the fragment after it's onDestroy() has been called, or should I recreate the fragment every time it is shown to the user.
This is a question of time vs. memory consumption, as at least one of the fragments needs some time to get created.
onDestroy functionality is to destroy all the used variables and consumed memory. All those will be flagged as a Dummy data to enable the garbage collector to remove them from the memory whenever it is needed.
Calling the Fragment again after calling onDestroy will pass through the lifecycle again from the beginning through onCreate and all the variables and local object will be re-initialized again.
Is it Safe? Yes, it is safe.
You are thinking deeper to handle the lifeCycle of the Fragment that is already being handled by the OS.
If you want to prevent the Fragment from being destroyed you can create it as a static object.
The Fragments developer guide seems to tell no, according to the lifecycle diagram.
If you want to keep your fragment for a later use, i suggest you use FragmentTransaction.detach(Fragment) or FragmentTransaction.hide(Fragment) instead of FragmentTransaction.remove(Fragment).
I have an app that has one main Activity that swaps out numerous Fragment's. Well it doesn't matter what Fragment you are on, after low memory kills the Activity and you try to return to the app, it boots you back to the "start" Fragment that the Activity first calls. (Note: Almost all of these are actually ListFragment's)
So here are my questions:
Should I be using onSaveInstanceState() in EACH Fragment? And if so, am I saving the Data in the Fragment OR the Fragment itself? Or do you use onSaveInstanceState() only once in the Main Activity. (If this is even the course to take)
Note: I have setRetainInstance(true) but I don't think I am handling that correctly, if that is the solution. These are all put as the last line of onActivityCreated().
The answer depends a lot on how you are managing fragments.
I'll assume you are not using the Fragment backstack, and that you have called setRetainInstance(true) on EACH fragment.
You need to use a tag when you attach the fragments.
In Activity#onSaveInstanceState() you need to remember which fragments are visible.
In Activity#onCreate you need to find the existing Fragments by tag for each fragment, then create new instances of any Fragments you can't find. Now you can use the information from the saved instance state to make the appropriate Fragments visible (show or add or replace as necessary depending on how your code manages the fragments.)
Edit in response to questions/comments:
activty.getFragmentManager().findFragmentByTag(tag); finds an existing fragment
in a Fragment transaction: add(fragment, tag), replace(id, fragment, tag), etc. lets you specify the tag. You can also put it in a layout file using the attribute
class=".myFrag$tag"
The actual fragment object including its contents still exist when you use setRetainInstance.
Note: If you don't want to use tags, you may also use the fragment manager's putFragment/getFragment methods to put the fragment into the instance state bundle.
Finally you can simply let the fragment save itself by calling FragmentManager's saveFragmentInstanceState but I've had trouble using this correctly.