7

I'm implementing an Android fragment. I understand that the framework can automatically destroy and recreate the fragment, and that recreating a fragment calls its default constructor, as opposed to a constructor that has arguments. There are many posts (such as the accepted answer to this question) that show how to provide arguments to the fragment by implementing a static newInstance method.

What I don't understand is who calls newInstance. My first impression was that - since one can define arbitrary arguments for this newInstance method - I should add an explicit call to it somewhere in the app, and that the name newInstance is just a naming convention. But then I would be creating a second fragment in addition to the one created by framework calling the default constructor, which confuses me.

So is the above assumption incorrect, and the newInstance method is really somehow an overload of Java's built-in method for instantiating a class? In that case, I don't see how I can define a newinstance method that takes an arbitrary argument list. Or is that possible in Java, and I just don't know Java well enough?

Community
  • 1
  • 1
Dabbler
  • 9,733
  • 5
  • 41
  • 64
  • 2
    yes you need to call `newInstance()` in your code, and the empty constructor is used by framework in case of a **recreate** is needed. – Yazan Mar 20 '16 at 12:11
  • 1
    P.S: is there any case you used a Fragment without instantiating it? i.e without using `...= new ...` ? – Yazan Mar 20 '16 at 12:13
  • Actually, yes - it's without using new. In the XML for the activity's layout, I have a element, and as far as I can tell, that's how the fragment is instantiated. I don't have an explicit call anywhere. Not sure how that works, anyway. – Dabbler Mar 20 '16 at 12:55
  • 1
    correct, this case framework will use the empty constructor, without params, in case you have used `new` either directly or using `newInstance()` then the empty constructor is needed to recreate the fragment (if it got destroyed), `newInstance` is not an overridden method, you can name it whatever you want. – Yazan Mar 20 '16 at 13:03

3 Answers3

10

You can name the function however you like: newInstance, getInstance, newFragment. It doesn't matter, it is only a helper method. Important is that you put all your arguments with fragment.setArguments(args). Android system will remember those arguments and will use them when fragment will be recreated.

public static MyFragment newInstance(int arg) {

    Bundle args = new Bundle();
    args.putInt("ARG", arg);

    MyFragment fragment = new MyFragment();
    fragment.setArguments(args);
    return fragment;
}
Arkadiusz Konior
  • 1,139
  • 13
  • 12
  • "Android system will remember those arguments and will use them when fragment will be recreated." Maybe this is the information I'm missing. So first I create the fragment via newInstance. And when Android subsequently recreates the fragment, it first calls the default constructor and then calls setArguments with the arguments it stored? – Dabbler Mar 20 '16 at 12:51
  • As noted above, in the XML for the activity's layout, I have a element, and as far as I can tell, that's how the fragment is instantiated. So how does that work, then? Maybe I'm not creating the activity correctly in the first place. – Dabbler Mar 20 '16 at 13:01
  • 1
    First question: yes. Check Fragment setArguments javadoc: "The arguments supplied here will be retained across fragment destroy and creation" Regarding xml fragment creation check: http://stackoverflow.com/questions/13034746/if-i-declare-a-fragment-in-an-xml-layout-how-do-i-pass-it-a-bundle There are two methods of creating fragments: by code (then you call newInstance()) and with XML tag. With XML tag fragment is created automatically by system using default constructor. So in this case you need to pass your parameters differently. newInstance method will never be called in this case. – Arkadiusz Konior Mar 21 '16 at 18:16
  • Thanks, Arkadiusz! – Dabbler Mar 21 '16 at 18:34
7

newInstance is an Android design pattern because of the fact that Fragment Should not have any other Constructor beside the default Constructor

Thus you define an Helper function in order to pass Arguments to the Fragment

You don't have to use it but Let's say you have 2 Activities that both starts FragmentA

If you will not Use the helper function You will need to duplicate the code to instantiate the Fragment.

What I don't understand is who calls newInstance

Usually you will use the instantiate method from places that creates Fragments...Activity, Adapter and such.

SectionPagerAdapter example:

    @Override
    public Fragment getItem(int position) {
        // getItem is called to instantiate the fragment for the given page.
        // Return a PlaceholderFragment (defined as a static inner class below).
        return PlaceholderFragment.newInstance(position + 1);
    }

Where PlaceholderFragment.newInstance(int position) is

public static PlaceholderFragment newInstance(int sectionNumber) {
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

In that case, I don't see how I can define a newinstance method that takes an arbitrary argument list.

You can pass arbitrary argument list but you need to know the value Type because Bundle has only putX() methods, where X is the type of the parameter

royB
  • 12,779
  • 15
  • 58
  • 80
3

Fragment.newInstance(args1,args2...) is used as static construction method. The benefits of static construction method is Needless to say. But in Fragment, doing this can help us to save arguments, and we can get these arguments in onCreate() Method for when accidentally your app boom,and Android help you to restore your fragment with Constructor without arguments.

public static StudyFragment newInstance(ArrayList<DailyWordBean.DataBean> list) {
    Bundle args = new Bundle();
    args.putSerializable("data", list);
    StudyFragment fragment = new StudyFragment();
    fragment.setArguments(args);
    return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        list = (ArrayList<DailyWordBean.DataBean>) getArguments().getSerializable("data");
        Log.e("study", list.size() + list.get(0).getWordContent());
    }
}

Remember to use Parcelable instead of Serializable.

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135