Mert Akcakaya
Mert Akcakaya

Reputation: 3149

Android Fragment instantiation: newInstance()

I read many articles and StackOverflow answers but still cannot understand why we need a factory method to create an instance of a Fragment.

The following Fragment classes both work fine.

Fragment with two constructors:

public class CtorFragment extends Fragment {
    private static final String KEY = "the_key";

    public CtorFragment() {
        // Android calls the default constructor so default constructor must be explicitly defined.
        // As we have another constructor, Android won't create a default constructor for us.
    }

    public CtorFragment(String s) {
        // Set the arguments.
        Bundle bundle = new Bundle();
        bundle.putString(KEY, s);

        setArguments(bundle);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View fragment = inflater.inflate(R.layout.fragment_my, container, false);
        TextView textView = (TextView) fragment.findViewById(R.id.textView);

        // Use getArguments() to get the String argument set by the constructor with parameter.
        textView.setText(getArguments().getString(KEY));
        return fragment;
    }
}

Fragment with a static factory method:

public class StaticFragment extends Fragment {
    private static final String KEY = "the_key";

    public static StaticFragment newInstance(String s) {
        StaticFragment fragment = new StaticFragment();
        // Set the arguments.
        Bundle bundle = new Bundle();
        bundle.putString(KEY, s);

        fragment.setArguments(bundle);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View fragment = inflater.inflate(R.layout.fragment_my, container, false);
        TextView textView = (TextView) fragment.findViewById(R.id.textView);

        // Use getArguments() to get the String argument set by the constructor with parameter.
        textView.setText(getArguments().getString(KEY));
        return fragment;
    }
}

Can you please explain why everyone (including Google) "strongly recommends" the one with the static factory method? Is there something critical that me and others coming from a non-Android background missing?

Is it that we have to define two methods (constructors) instead of one (static factory method) which causes all the fuss?

Upvotes: 1

Views: 7046

Answers (2)

CommonsWare
CommonsWare

Reputation: 1007544

still cannot understand why we need a factory method to create an instance of a Fragment

"Need" is a strong word. You do not "need" a factory method. You do need:

  • A public zero-argument constructor, ideally empty; and

  • An organized means to set up a fragment, in a way that survives configuration changes

Can you please explain why everyone (including Google) "strongly recommends" the one with the static factory method?

If you have no explicit constructors on a Java class, you automatically get an empty public zero-argument constructor, which is what the framework needs to create a fragment.

If you create a constructor that takes arguments (e.g., CtorFragment(String s)), then you also have to remember to create the public zero-argument constructor (CtorFragment()). You might remember to do this. Many inexperienced programmers will not.

Writing a factory method achieves the same objective as the non-zero-arguments constructor, without clobbering the automatically-created zero-argument constructor.

If you are an experienced Java programmer, and you do not like factory methods, and you can read stack traces and otherwise remember to add the public zero-argument constructor, you are welcome to create additional constructors and use them.

Upvotes: 6

ישו אוהב אותך
ישו אוהב אותך

Reputation: 29814

The purpose of the newInstance is mainly to set initial value for the Fragment. If we using a default constructor for the fragment, we can't set the arguments as a means for sending the initial value.


Here is the more details explanation, quoting from https://stackoverflow.com/a/9245510/4758255:

If Android decides to recreate your Fragment later, it's going to call the no-argument constructor of your fragment. So overloading the constructor is not a solution.

With that being said, the way to pass stuff to your Fragment so that they are available after a Fragment is recreated by Android is to pass a bundle to the setArguments method.

So, for example, if we wanted to pass an integer to the fragment we would use something like:

public static MyFragment newInstance(int someInt) {
    MyFragment myFragment = new MyFragment();

    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    myFragment.setArguments(args);

    return myFragment;
}

And later in the Fragment onCreate() you can access that integer by using:

getArguments().getInt("someInt", 0);

This Bundle will be available even if the Fragment is somehow recreated by Android.

Also note: setArguments can only be called before the Fragment is attached to the Activity.

This approach is also documented in the android developer reference: https://developer.android.com/reference/android/app/Fragment.html

Upvotes: 3

Related Questions