I have this piece of code:
public static DateDialogFragment newInstance(Context context, DateDialogFragmentListener listener) {
DateDialogFragment dialog = new DateDialogFragment();
mContext = context;
mListener = listener;
/*I dont really see the purpose of the below*/
Bundle args = new Bundle();
args.putString("title", "Set Date");
dialog.setArguments(args);
return dialog;
}
Pretty self explanatory, but what I don't understand is what the point of giving it a Bundle. I never really make use of it I guess. The Android Doc's explanation of this (for Fragments) is here:
http://developer.android.com/reference/android/app/Fragment.html#setArguments(android.os.Bundle)
What exactly does a construction argument mean? Since its never used, I fail to see the use of it. Any explanation much appreciated. Thanks.
It is simply a generic mechanism for you to attach data values that you might want to use to configure the Fragment or otherwise read during operation, similar to how you might pass extras in a Bundle to a new Activity via it's Intent.
I do agree, however, that since a Fragment can be instantiated using its constructor, where an Activity cannot, the usefulness of the API may seem compromised because you can just as easily configure a Fragment using setter methods and member variables inside of newInstance() before returning the instance. For example, your code could implement a method on the Fragment called setTitle() that you could call, and you wouldn't need to pass that as an argument. However, arguments do provide a nice way of storing this information as key/value data if that model fits your application.
One key distinction about the arguments of a Fragment is that they are retained as part of the saved instance state. So if your UI rotates or some other change requires the Fragment to be recreated, the arguments Bundle attached will be retained and handed back to the new instance.
HTH
Related
What is the aim of using a Factory to construct our fragments?
Android Studio provides some boilerplate code generation, and provides the factory method when creating a fragment.
So what is the purpose of that? What is the gain?
Using static factory method is not just for the fragment, I use it to create intent, adapter and other classes as well. By this approach you simply have the control of the object creation.
One of the good advantage is you simply increase the cohesion by encapsulating bundle keys.
class FragmentFoo extends Fragment{
}
When you want to send a bundle to this fragment from outside
// Activity Foo
Bundle bundle = new Bundle();
bundle.putString("name","Foo");
Fragment fragment = new FragmentFoo();
fragment.setArgs(bundle);
To extract this name, you need to use "name" key in fragment as well, if you use it as hardcoded, you may have some errors,typos. So you can use a constant in order to make sure you don't make typo. But in this case you need to put it somewhere both can see it. Some creates another class in order to keep all contants which is very ugly and hard to maintain, some puts the keys in the fragment and make it public and use it everywhere,
by static factory method, you can simply keep everything in fragment and no need to expose. Whoever needs to use this fragment will have a clear idea what it requires and also will not need to know what keys are. Just send the required params will be enough for them.
class FragmentFoo extends Fragment{
private static final String KEY_NAME = "name";
private String name;
public static Fragment newInstance(String name){
Bundle bundle = new Bundle();
bundle.putString(KEY_NAME, "name");
Fragment fragment = new FragmentFoo();
fragment.setArgs(bundle);
return fragment;
}
}
Static factory methods allow us to initialize and setup a new Fragment without having to call its constructor and additional setter methods. Providing static factory methods for your fragments is good practice because it encapsulates and abstracts the steps required to setup the object from the client.
http://www.androiddesignpatterns.com/2012/05/using-newinstance-to-instantiate.html
A Fragment must have a constructor with no arguments. That's because Android will call the default constructor to recreate the fragment. If you're not passing in arguments, then you should not need either a constructor or a static factory method (AKA, virtual constructor).
If you create a fragment via template then remove all code in fragment then add constructor and hypothetical arguments which you want to pass to fragment
public class BlankFragment extends Fragment {
public BlankFragment(int arg)
{
}
}
you will get this pop up message
Avoid non-default constructors in fragments...
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().
The Android system instantiates fragments with the no args constructor, which means you need to add a second constructor with no args for the Android System to re-initialize the fragment on state change events like when phone goes into landscape mode and fragment is destroyed then recreated by Android (not your host activity)
public class BlankFragment extends Fragment {
public BlankFragment()
{
}
public BlankFragment(int arg)
{
}
}
Now if you tilt your phone and go to land scape mode, Android will re-init the fragment from the empty constructor, but you won't get your arguments passed to it.
You can still get the bundle on a state change, but you now have two constructors when you could have a single static factory method.
I'm wondering if calling setArguments on a Fragment immediately after its instantiation creates any problems.
For example, say we have the following snippet:
Fragment myFragment = new CustomFragment();
Bundle args = new Bundle();
args.putBoolean("amIAnArg", true);
myFragment.setArguments(args);
This code seems to work fine, although it looks like the code should create a race condition since a Fragment's arguments can only be set before the onAttach method is called.
Are there any issues with setting a Fragment's arguments in this way?
Just like an Activity, Fragments have a specific lifecycle, and are not "created" like simple Java objects. When you commit a FragmentTransaction, it's asynchronous and isn't immediately attached or created. It's queued on the main thread to occur at a later time. Only then will it go through its lifecycle methods (e.g. onCreate(), onAttach()).
You should set the arguments this way, and should do so before committing the FragmentTransaction -- however, you could technically do it right after committing the transaction with no ill effects. As others have stated, what you're doing is the suggested newInstance() factory method for fragments [1]. For example:
private static final String ARG_IS_ARG = "is_arg";
public static CustomFragment newInstance(boolean isArg) {
CustomFragment result = new CustomFragment();
Bundle args = new Bundle();
args.putBoolean(ARG_IS_ARG, isArg);
result.setArguments(args);
return result;
}
[1] http://developer.android.com/reference/android/app/Fragment.html
There should be no problem. I am working on a project right now that uses this exact format in several spots.
This format is in the Android Developers example project as well (find 'Arguments'):
http://developer.android.com/reference/android/app/Fragment.html
Similar to what #kcoppock said, in most cases you will be instantiating the Fragment on the UI thread, and then Android queues up the arguments you pass to the Fragment on the UI thread as well. There's no race conditions because the operations take place at different times on the same thread.
For more information, check out this blog post on Activities and Fragments: http://www.zerotohired.com/2015/02/passing-data-between-activities-and-fragments-in-android.
Looking at the documentation of DialogFragment, one sees the static newInstance method to initialize a new alert dialog fragment. My question is, why not use a constructor to do so, like this:
public MyAlertDialogFragment(int title) {
Bundle args = new Bundle();
args.putInt("title", title);
setArguments(args);
}
Isn't this exactly the same or does it differ somehow? What's the best approach and why?
If you create a DialogFragment that receives objects through the constructor, you will have problems when android recreates your fragment.
This is what will happen:
your code creates the dialog calling the constructor you have created and passing some arguments as dependencies.
your dialog runs, and uses the dependencies that you passed though the constructor
the user closes the app
time passes, and android kills the fragment to free memory
the user opens the app again
android will recreate your dialog, this time using the default constructor. No arguments will be passed!
Your dialog will be in a undesired state. It may try to use instance variables that you expected to pass through the constructor, but as they are not there you'll get a null pointer exception.
To avoid this, you need not to rely on the constructor to establish the dependencies, but in in Bundles (arguments and saved instances). That may force you to implement Parcelable in some classes, which sucks.
EDIT: you can reproduce Android killing the app (step 4) by enabling the "don't maintain Activities" in the Development settings. That's the way to easily test it.
Android relies on Fragments having a public, zero-argument constructor so that it can recreate it at various times (e.g. configuration changes, restoring the app state after being previously killed by Android, etc.).
If you do not have such a constructor (e.g. the one in the question), you will see this error when it tries to instantiate one:
Fragment$InstantiationException: Unable to instantiate fragment
make sure class name exists, is public, and has an empty constructor that is public
Arguments given to it by Fragment.setArguments(Bundle) will be saved for you and given to any new instances that are (re)created. Using a static method to create the Fragment simply provides an easy way to setup the required arguments whilst maintaining a zero-argument constructor.
If you overload the constructor with MyAlertDialogFragment(int title), the Android system may still call the default MyAlertDialogFragment() constructor if the Fragment needs to be recreated and the parameter is then not passed.
Because when android is recreating a fragment, it always uses the empty constructor, and by using newInstance() you can set data that fragment uses when recreating, for example when the screen is rotated
for example:
public static FragmentExample newInstance(Parcelable uri) {
FragmentExample fragmentExample = new FragmentExample();
Bundle bundle = new Bundle();
bundle.putParcelable("Uri", uri);
fragmentExample.setArguments(bundle);
return fragmentExample;
}
I have seen different ways to create a Fragment. Can somebody clear me up whats the difference between the methods and what is the best way and when to use which.
new MyFragment() I myself use this, because it seemed natural to me.
MyFragment.instantiate(Context context, String fname, Bundle args) This looks like a custom static method to create a Fragment but i've never seen it used.
My.Fragment.newInstance() This one is in an Android Developer Example.
What's each options purpose?
The most difference is when to use each sample:
1- Creates a new fragment object each time called.
2- Same as calling the empty constructor, but, if you set the fragment to setRetainInstance(true), this will not work if you use the empty constructor.
3- My.Fragment.newInstance(), method to get the single instance from a static fragment, If you have a static class extending fragment, you can create a Method to return a new fragment or the current fragment, its the singleton pattern.
4- Inflate from xml, same as calling the empty contructor, the Android FrameWork take care of attachement to the view, but the instance will be kept with the hole activity lifecycle, needs more memory and cannot be reused multiple time.
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.