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.
Related
I have one activity, with many fragments on it.
Since I sometimes need the activity (for example to get getFragmentManager, etc.), I call the activty's singleton instance from fragment.
It means :
in my activity there's a static variable called instance.
However, sometimes I get crashes in fragments because MyActivity.getInstance() is null.
1. How is it possible? Could it be because the activity is singleton?
2. the activity-as-singleton - could it lead to certain problems(like **memory leak** ?, etc..)
3. Can I count on getActivity always? I think sometimes that too returned null.
haven't come across such situation but rather than accessing resources like getFragmentManager() in fragment for fragment transaction, try Interface pattern and give callback to your activity and let activity handle all of this.
i am trying to alter the activity content from fragment. for that i want to pass the activity handle inside fragment and do the required changes.
If i can do that why there is more difficult way of interface etc.
You can call getActivity() from the Fragment.
If you want to get your activity specifically you can cast it
((MyActivity) getActivity()).someMethod()
This will tightly couple your Fragment to your activity and prevent you using the fragment in a different activity easily so be careful.
Also you need to be careful with lifecycles and such as a fragment can become detached from an activity causing a NullPointerException from time to time. So it is recommended to wrap this in a null check
I wonder the difference between two ways of transfering data from activity to fragment.
One is using getArgument() and setArgument(). I can transfer data using these methods at Fragment's contruction time.
Another is using getActivity() method. Like this way
((HostActivity)getActivity()).getXXX()
After declaring getter method of data Fragment may use, call this method in fragment through getActivity() and Type casting.
I think second one is easier and convenient. Because get/setArgument() can be called only Fragment's contruction time.
So, How to apply these 2 way to sending and getting data between Activity and Fragment?
A Fragment represents a behavior or a portion of user interface in an
Activity. You can combine multiple fragments in a single activity to
build a multi-pane UI and reuse a fragment in multiple activities.
Because fragment can reuse in multiple activity, if you use getActivity() with type casting, you must check instanceOf activity before call method. And each of activity use that fragment, you must implement method getXXX().
Use newInstance method in fragment, you only pass require parameter for it.
If you create fragment for individual activity, you can apply 2 ways transfer data.
The fragment has an independent lifecycle from activity with specific threads, functions and handlers. So you can use getters/setters Activity variables like a global variables and bundle data (arguments) to independent fragment variables.
I want to pass an object to fragments
when the object shouldn't or can't recreate in a fragment (e.g. LruCache).
Is it possible without using static variable?
since passing variable may not always same object
Edit:
To make it clear I don't want the object to be null when activity recreated
For passing object to fragment you should use interface have a look at this link.
You can pass object via constructor and getter and setter also but in case of app resume object might be null that will cause NullPointerException on app resume.
Edited
If you are working with LruCache and want to share its data within fragments. I will suggest you to create a fragment without a onCreateView() method, means it will be invisible fragment. Implement a LruCache object in fragment and have getters and setters to get and set desired objects from LruCache.
Fragments have a characteristics of re-usability in contrast to a simple class. You can find a fragment by its tag.
For example: To add a fragment-
activity.getFragmentManager().beginTransaction().add(fragment, TAG).commitAllowingStateLoss();
and to get a fragment-
activity.getFragmentManager().findFragmentByTag(TAG);
In contrast to normal class Fragment class is removed from memory only when device is in sort of memory. Whenever fragment is null you will have to re-initialize it.
To know more about its implementation details visit this link
Short version: I would like to know how i can recreate a fragment state (e.g. after screen-rotation) when this fragment contains a reference to an object that cannot be serialized or duplicated and needs to stay in memory.
Long version:
In my app i use a ViewPager connected to a custom FragmentPagerAdapter that instantiates a number of fragments which display data (a schedule) contained in a ListView. All this is contained inside a parent fragment. When instantiated by the adapter, each page fragments is passed a reference to one single object (the "ScheduleManager") that does several things:
Contains the data to be displayed
Holds a reference to a Context object (in order to access SharedPreferences)
Holds a reference to the parent fragments LoaderManager so it can reload the data
Implements OnClick- and ActionMode callback listeners (to be able to create and handle an action mode that works across all pages; Page Fragments add the object as a listener to their ListViews)
Defines a callback interface to notify listeners of state changes or when data is reloaded (Page Fragments register themselves as listeners).
Essentially, the ScheduleManager holds everything together and implements the main logic of this ("schedule") part of my app, i.e. loading and providing the data, and the means to modify and relaod it via an ActionMode. I don't know whether this is good design...
My question is how am I supposed restore the Fragments instance state under these circumstances? I cannot serialize the ScheduleManager to a bundle, since it would loose its references to the Context and the LoaderManager (otherwide, of course I would use setArguments / getArguments). Also, all the page fragments must have a reference to the same instance of the ScheduleManager, otherwise the shared action mode won't work. Aside from that I don't want to duplicate the entire schedule data each time a fragment is restored. I want to keep this object in memory and let the page fragments reclaim a reference to it when they are restored.
I guess I could let the containing activity hold the ScheduleManager and have the PageFragment query it for a reference. But I would prefer to keep the everything inside the parent fragment self-contained and modular if this is possible (there can be different schedules for different items). While writing this though I get the feeling there will be no way around this.
Of course, when the parent fragment is recreated it can also recreate the ScheduleManager and feed to it the references it needs (Context + LoaderManager). The problem is there can be no more than one instance of ScheduleManager for each instance of the parent fragment, so how to make the nested page fragments reconnect to it.
Here is what I ended up doing:
public void onCreate(Bundle savedInstanceState)
{
Fragment fragment = this.getActivity().getFragmentManager().findFragmentByTag("SCHEDULE");
if (fragment instanceof ScheduleMasterFragment)
{
ScheduleMasterFragment master = (ScheduleMasterFragment) fragment;
this.scheduleManager = master.getScheduleManager();
}
else
{
throw new RuntimeException("SchedulePageFragment must be " +
"the child of a ScheduleMasterFragment with Tag set to 'schedule'");
}
super.onCreate(savedInstanceState);
}
The ScheduleManager is the reference I wanted to keep. At least this way I keep all the code inside the fragment itself, except for getScheduleManager in ScheduleMasterFragment which makes sense because the ScheduleMasterFragment somehow is the owner of the ScheduleManager instance.
Any better solutions are still welcome...