I have a fragment that is always visible. I don't understand why I should use bundles to pass data to it from activity.
Most of the questions here recommend this method of passing data:
Bundle bundle=new Bundle();
bundle.putString("name", "From Activity");
Fragmentclass fragobj=new Fragmentclass();
fragobj.setArguments(bundle);
I prefer creating Fragment object in OnCreate function of activity and then use this object to display fragment(FragmentTransaction.add). As I have refence to this fragment I can create create function showName() in it and call it from activity like that:
myFragment.showName("name");
Is there anything wrong with this approach?
The Android documentation states:
Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state. It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated; instead, arguments can be supplied by the caller with setArguments(Bundle) and later retrieved by the Fragment with getArguments().
That's why it's better to use a bundle and set the parameters of the Fragment this way, it's easier for the system to restore its values when the fragment is re-instantiated.
Now, I wouldn't use myFragment.showName("name"); because you don't know if the lifecycle of the fragment has already finished (attached to the activity and inflated the views), so instead, I would call the showName("name") in the onActivityCreated or onViewCreated callbacks.
Applications should generally not implement a constructor. The first place application code can run where the fragment is ready to be used is in onAttach(Activity), the point where the fragment is actually associated with its activity. Some applications may also want to implement onInflate(Activity, AttributeSet, Bundle) to retrieve attributes from a layout resource, though should take care here because this happens for the fragment is attached to its activity.
There's nothing wrong with this approach for setting one off data values, you just need to be careful to make sure that the view that you want to set your name on actually exists at the point that the showName method is called.
Part of the reason that using Bundles to pass information is popular is that they can hold all types of data using keys and also they can easily be used to pass view states around during device rotation. Ultimately it's a matter of preference and exactly what your use case is.
When app is in background, the Fragment can recreated (eg: by change the theme (light/dark), language, ...).
So if you dont pass data use Bundle to Fragment, your Fragment will not have this data when it recreated
Related
In Android Fragment it has onSaveInstanceState() which allows to save some data for restore the state when os recreates the fragment.
onSaveInstanceState(outState: Bundle) {
outState.putString("dataStr", dataStr)
}
What about the data are already in fragment's argument? Seems after the fragment is recreated the previous arguments are also restored.
// set the argument
Bundle bundle = new Bundle();
bundle.putString("dataStr", dataStr);
AFragment aFragment = new AFragment();
aFragment.setArguments(bundle);
//get the argument inside the AFragment
Bundle bundle=getArguments();
Does it mean if anything is in fragment's argument then they don't need to be saved through onSaveInstanceState()?
The question: since the argument can be updated inside the fragment, is it a alternative for saving the data in the argument instead of saving through the onSaveInstanceState()?
Or any difference with using these two?
onSaveInstanceState() / onRestoreInstanceState() is only explicitly called by Android when the Activity/Fragment needs to be recreated (especially on configuration changes), imagine that a user started to type something and then changed the orientation of his screen, if you didn't handle the saving state he will loose his inputs.
Fragment Args on the other hand are used to instantiate fragments, the args will be available even if the Fragment is recreated BUT it will be the initial state.
So to summarize if you want to create a new fragment with some args (like a User Name...) you need to use args.
But if you want to save the current state (User Inputs...) in case of fragment recreation you need to handle it through the SaveInstance method.
For further understanding of how Save/Restore Works I recommend this article
savedInstance : saveInstanceState when you are frequently moving back and forth between activities and remember this thing when you close app mean it get removes from memory, information will also lost.
Bundle :
Bundle uses a Map for holding the extras you’re putting into. so in this way there’s no difference between a Map and Bundle. but when you’re going to send data to another Activity(or any other IPC components such as Service, BroadcastReceiver,…) the Map will be marshalled into byte[] via a Parcel, and will send to destination. in destination the data will go into the same flow in reverse and the byte[] will be unmarshalled into Map and you can have extras by providing the same keys.
So Bundle is like a Map which can also be marshalled/unmarshalled to/from byte[]. one important note is that you cannot use this byte[] to persist your bundle, as it’s designed ONLY to transfer to components, and will not work if underlying data structure changes.
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.
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.
More to the point, in onCreate/onCreateView I am already calling FragmentManager.findFragmentByTag() to lookup any existing instance of my fragment, and it seems to find it.
So what is the point of putFragment/getFragment? Does it save something extra or cause additional lifecycle stuff to happen? Is it just an alternative to findFragmentByTag() that does more or less the same thing? Because it seems to me that the fragment is being automatically saved for me without needing to use FragmentManager.putFragment().
So what is the point of putFragment/getFragment?
According to the current implementation, what putFragment(Bundle bundle, String key, Fragment fragment) do is put the index of a fragment into the bundle with the parameter key. And then getFragment(Bundle bundle, String key) get the fragment at the same index which can be retrieved from the bundle with the same key. A Fragment has its index in the FragmentManager only after it is added to it, so putFragment() can be called on a Fragment only after it is added.
Does it save something extra or cause additional lifecycle stuff to
happen?
It save the index of a Fragment only, no more things else, nor do it cause any additional lifecycle stuff.
Is it just an alternative to findFragmentByTag() that does more or
less the same thing?
Yes, I thik so.
According to the current implementation, what putFragment/getFragment does can be achieved with findFragmentByTag() too. But the function of putFragment/getFragment are quite limited because you can't use them without the bundle parameter, means you must call putFragment() in onSaveInstanceState().
Seems like putFragment/getFragment its just a safe way of storing fragments and its states inside fragment manager without displaying it.
For example you have 2 fragments that stored in your activity fields. You displaying one of them and than replace it another after that you change orientation of screen. Fields in your activity are reinited but fragment that currently displayed saved its state and other don't. But if you store fragments inside fragment manager you will have two fragments with actual states.
Hi I was looking at the following Fragments example on the android site.
http://developer.android.com/guide/components/fragments.html#Example
I would like to know why certain methods are performed.
Why for instance, in the detailsFragment is the following method performed:
public static DetailsFragment newInstance(int index) {
DetailsFragment f = new DetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
Could you not also simply instantiate the DetailsFragment and use a setter method to set index instead. Bypassing the whole setArguments.
What's the point of using setArguments in the first place? Could you not just use setters and getters?
You can use getters and setters, but by passing in a bundle you don't need to write that code, since it's already there. Also, I believe that these arguments are automatically passed in again if the screen orientation changes, which also makes life easier.
Essentially, setArguments and getArguments is just a design pattern that Google suggests you follow:
Every fragment must have an empty constructor, so it can be
instantiated when restoring its activity's state. It is strongly
recommended that subclasses do not have other constructors with
parameters, since these constructors will not be called when the
fragment is re-instantiated; instead, arguments can be supplied by the
caller with setArguments(Bundle) and later retrieved by the Fragment
with getArguments().
http://developer.android.com/reference/android/app/Fragment.html
I take that to include setters which are needed for your Fragment to operate as well. Then again - there's nothing forcing you to do it this way, and as you know - it's not the only way things could be made to work.
Just to add to Matthew's answer: he quoted correctly that Fragments need to have an empty constructor, so that the framework can re-instantiate them when needed.
It is fine to use getters and setters, but as the framework may destroy and re-create your Fragment, you must ensure to not lose those parameters.
This must be done via Fragment.onSaveInstanceState(). The saved stated will be passed back to you as the parameter savedInstanceState in Fragment.onCreate(), Fragment.onCreateView() and several other methods.
Using Fragment.setArguments() is (in most cases, I assume) easier, at the framework will automatically preserve the arguments and thus will do most of the work for you.
Setters may be the way to go for parameters you supply to the Fragment initially and which the fragment may adjust itself over time. Dealing with the savedInstanceState alone may be easier in this case than dealing with savedInstanceState and arguments - where you have to make a decision which is the valid parameter.
public void setArguments (Bundle args)
Supply the construction arguments for this fragment. This can only be
called before the fragment has been attached to its activity; that is,
you should call it immediately after constructing the fragment. The
arguments supplied here will be retained across fragment destroy and
creation (may be the text in bold was missing from official
documentation previously)
Fragments.setArguments(Bundle args)
In addition setters can be misused. If updateSomeOtherStuff() will change some view this will crash.
public class MyFragment extends Fragment {
void setData(someData){
this.someData = someData;
updateSomeOtherStuff()
}
}
If one passing in a bundle it is not possible to misuse the setter and you will always know that this will be set within the lifecycle methods.