176

I am creating an app with Fragments and in one of them, I created a non-default constructor and got this warning:

Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead

Can someone tell me why this is not a good idea?

Can you also suggest how I would accomplish this:

public static class MenuFragment extends ListFragment {
    public ListView listView1;
    Categories category;

    //this is my "non-default" constructor
    public MenuFragment(Categories category){
        this.category = category;
    }....

Without using the non-default constructor?

Marian Paździoch
  • 8,813
  • 10
  • 58
  • 103
BlackHatSamurai
  • 23,275
  • 22
  • 95
  • 156
  • 2
    possible duplicate of [Best practice for instantiating a new Android Fragment](http://stackoverflow.com/questions/9245408/best-practice-for-instantiating-a-new-android-fragment) and http://stackoverflow.com/questions/10450348/do-fragments-really-need-an-empty-constructor and http://stackoverflow.com/questions/11602433/fragment-newinstance-vc-onsaveinstancestate and probably others – CommonsWare Aug 21 '12 at 21:02
  • 3
    No, those don't help. They didn't answer my question. But thank you none the less :) – BlackHatSamurai Aug 21 '12 at 21:22
  • 37
    @BlaineOmega Actually this one in particular: http://stackoverflow.com/a/11602478/321697 definitely answers your question. On an orientation change or other event that causes the Fragment to be recreated, Android uses the default constructor as well as the Bundle passed as an argument. If you're using a custom constructor, then as soon as the fragment is recreated due to one of these events, whatever you did in the custom constructor is lost. – Kevin Coppock Aug 21 '12 at 21:25
  • 1
    Thanks, but that answers the why, but not the how. – BlackHatSamurai Aug 21 '12 at 21:28
  • That is covered by the first and second links in my original comment. – CommonsWare Aug 21 '12 at 21:35
  • @kcoppock but in my case i need to pass the reference to the `EditText` to the fragment, which cannot be pass as a bundle's extra :( so any suggestion for this scenario, i really don't to make it a inner class – Muhammad Babar May 29 '13 at 06:50
  • You can read more about the default constructor and why you should not implement other constructors here: http://developer.android.com/reference/android/app/Fragment.html#Fragment() – Mario Kutlev Jul 31 '13 at 07:19
  • @kcoppock If setRetainInstance is set true then the fragment and its data will not get destroyed by config change – inmyth Sep 30 '13 at 17:41
  • For now you can just use `@SuppressLint("ValidFragment")` on top of uour constructor and avoid the errorbut on later stage its recommended to pass stuff to `Dialog fragment` using `bundles` – Sheraz Ahmad Khilji Oct 17 '15 at 10:56

6 Answers6

281

It seems like none of the answers actually answer "why use bundle for passing parameters rather than non default constructors"

The reason why you should be passing parameters through bundle is because when the system restores a fragment (e.g on config change), it will automatically restore your bundle.

The callbacks like onCreate or onCreateView should read the parameters from the bundle - this way you are guaranteed to restore the state of the fragment correctly to the same state the fragment was initialised with (note this state can be different from the onSaveInstanceState bundle that is passed to the onCreate/onCreateView)

The recommendation of using the static newInstance() method is just a recommendation. You can use a non default constructor but make sure you populate the initialisation parameters in the bundle inside the body of that constructor. And read those parameters in the onCreate() or onCreateView() methods.

blackpanther
  • 10,998
  • 11
  • 48
  • 78
numan salati
  • 19,394
  • 9
  • 63
  • 66
112

Make a bundle object and insert your data (in this example your Category object). Be careful, you can't pass this object directly into the bundle, unless it's serializable. I think it's better to build your object in the fragment, and put only an id or something else into bundle. This is the code to create and attach a bundle:

Bundle args = new Bundle();
args.putLong("key", value);
yourFragment.setArguments(args);

After that, in your fragment access data:

Type value = getArguments().getType("key");

That's all.

Andrii Abramov
  • 10,019
  • 9
  • 74
  • 96
nistv4n
  • 2,515
  • 2
  • 19
  • 21
  • 3
    how to pass an object ? I want to pass a Context Object or any other object. – Adil Malik Feb 13 '13 at 17:42
  • 12
    Bundles can carry serialized Java objects as well as [`Parcelable`](http://developer.android.com/reference/android/os/Parcelable.html) objects. Also, you should not pass a `Context`, because that information can be accessed via the fragment's [`getActivity()` method](http://developer.android.com/reference/android/app/Fragment.html#getActivity()). – krakatoa Feb 15 '13 at 21:52
  • In fragment where to do this `Type value = getArguments().getType("key");`? – Muhammad Babar May 22 '13 at 07:53
  • 4
    @Muhammad Babar: If I were you, I would add it to the `newInstance()` method. For example: `public static FragmentName newInstance(your variables){}`. As the Android documentation recommend, do not make a constructor with parameters, because the default one (without parameters) will be called automatically after the restart of your fragment. – nistv4n May 22 '13 at 19:45
  • @MuhammadBabar onCreateView is OK – chanjianyi Jun 14 '13 at 04:19
  • You may want to wrap this whole process in a static method of the fragment to keep logic a bit more cohesive. This way you don't need to create the bundle and set the args outside of the class. Also has an example for non-primitive/String objects. See http://stackoverflow.com/a/9932244/413254. – loeschg Jan 17 '14 at 20:06
  • Use the https://github.com/mcharmas/android-parcelable-intellij-plugin plugin in Android Studio to speed up the creation of your custom parcelable implementation. – JoachimR Jul 27 '15 at 08:21
  • @AdilMalik, you can't pass an object, unless it's serialized. – Willian Soares Apr 05 '17 at 14:44
  • This is still valid for anroid studio 3.2 and it makes no sense. Why should I have to type the 'key' string twice? Seems like a clear violation of DRY to me. – RabbitBones22 Dec 12 '18 at 15:44
52

Your Fragment shouldn't have constructors because of how the FragmentManager instantiates it. You should have a newInstance() static method defined with the parameters you need, then bundle them and set them as the arguments of the fragment, which you can later access with the Bundle parameter.

For example:

public static MyFragment newInstance(int title, String message) {
    MyFragment fragment = new MyFragment();
    Bundle bundle = new Bundle(2);
    bundle.putInt(EXTRA_TITLE, title);
    bundle.putString(EXTRA_MESSAGE, message);
    fragment.setArguments(bundle);
    return fragment ;
}

And read these arguments at onCreate:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    title = getArguments().getInt(EXTRA_TITLE);
    message = getArguments().getString(EXTRA_MESSAGE);

    //...
}

This way, if detached and re-attached, the object state can be stored through the arguments, much like bundles attached to Intents.

Rukmal Dias
  • 3,242
  • 1
  • 34
  • 28
Asaf Pinhassi
  • 15,177
  • 12
  • 106
  • 130
10

If you use parameter for some class. try this

SomeClass mSomeInstance;
public static final MyFragment newInstance(SomeClass someInstance){
    MyFragment f = new MyFragment();
    f.mSomeInstance = someInstance;
    return f;
}
김동기
  • 213
  • 3
  • 6
1

I think, there is no difference between static constructor and two constructors (empty and parametrized one that stores arguments into a Fragment's arguments bundle), most probably, this rule of thumb is created to reduce probability of forgetting to implement no-arg constructor in Java, which is not implicitly generated when overload present.

In my projects I use Kotlin, and implement fragments with a primary no-arg constructor and secondary constructor for arguments which just stores them into a bundle and sets it as Fragment arguments, everything works fine.

Pavlus
  • 1,651
  • 1
  • 13
  • 24
0

If fragment uses non-default constructors after configuration changing the fragment will lose all data.