android figure in front of mac holding an interesting phone

Best Practice to Instantiate Fragments with Arguments in Android

There are some ways to instantiate and pass data to fragments in android development. However, you must be careful when you do that and you should avoid the wrong approaches while you are instantiating and passing data to fragments.

The most recommended way of instantiate fragments with arguments is to have factory methods for this task.

Also, another important topic is about how to pass data to the fragments while instantiating them. In this case it is tempting to directly access the fields of your fragment and assign their values. However, this is the wrong approach. Because when your application send back to background and the other applications require more and more memory then your application and its resources will be cleared from the memory to open up space to the new ones.


package com.gunhansancar.android.fragments;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
/**
* Sample fragment to demonstrate the instantiation of fragments with arguments
*
* Created by Günhan on 28.10.2015.
*/
public class MyFragment extends Fragment {
private String name;
private int age;
private TextView mNameTextView;
private TextView mAgeTextView;
public static MyFragment newInstance(String name, int age) {
Bundle bundle = new Bundle();
bundle.putString("name", name);
bundle.putInt("age", age);
MyFragment fragment = new MyFragment();
fragment.setArguments(bundle);
return fragment;
}
private void readBundle(Bundle bundle) {
if (bundle != null) {
name = bundle.getString("name");
age = bundle.getInt("age");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_sample, container, false);
mNameTextView = (TextView) view.findViewById(R.id.nameTextView);
mAgeTextView = (TextView) view.findViewById(R.id.ageTextView);
readBundle(getArguments());
mNameTextView.setText(String.format("Name: %s", name));
mAgeTextView.setText(String.format("Age: %d", age));
return view;
}
}

view raw

MyFragment.java

hosted with ❤ by GitHub

In this example, to instantiate MyFragment you need to call;

Fragment fragment = MyFragment.newInstance("Gunhan", 28);

If android decides to recreate your activity and fragment, it is going to call no-argument constructor and the system guarantees to pass the arguments bundle to it.

After that when you want to return to your application the android system will create your activity and also your fragment with the default constructor with the passed arguments bundle.

Therefore if you directly set your fields then you are gonna lose their values after recreate operation.

Update: How to use the newly instantiated Fragment in your activity?


FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = MyFragment.newInstance("Gunhan", 28);
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();

view raw

activity.java

hosted with ❤ by GitHub

fragment_container could be any layout in your activities main layout xml. It will attach Fragment’s layout on this container layout.
UPDATE 2: DEMONSTRATING HOW TO INSTANTIATE FRAGMENT IN FRAGMENT PAGER ADAPTER

You can find the example project on https://github.com/gunhansancar/FragmentPagerExample

Related posts


Posted

in

by

Comments

22 responses to “Best Practice to Instantiate Fragments with Arguments in Android”

  1. Luke Allison Avatar
    Luke Allison

    Just wondering in what situation one would need to add this to the manifest: android:configChanges=”orientation|keyboardHidden|screenSize” . I had someone help me write an application and they felt the need to override the orientation handling but it has broken all other fragments in the app.

  2. Ragtime Ragtime Avatar
    Ragtime Ragtime

    It returns Name: null and Age: 0

    1. Günhan Avatar

      Hi Ragtime,

      Do you mind to share your code sample maybe you are missing a point.

      1. Ragtime Ragtime Avatar
        Ragtime Ragtime

        I used your code, nothing different :/

        1. Gunhan Avatar

          Are you sure that you are using exactly the same for instance you should instantiate the fragment ‘Fragment fragment = MyFragment.newInstance(“Gunhan”, 28);’ this way. not like new MyFragment(). This piece of code is 100% working so you should identify what you are missing or send me a sample code so that I can find your missing piece.

          1. Arnold Brady Avatar
            Arnold Brady

            Code is not completed, where is the main activity code?
            where I have to type this:

            Fragment fragment = MyFragment.newInstance(“Gunhan”, 28);

            This part is missing in your tutorial.

          2. Gunhan Avatar

            @arnoldbrady:disqus hey,
            The example above is not a complete fragments tutorial. It only covers for “How to instantiate a fragment” part.

            To add fragment to an activity programatically you need to use a FragmentManager and you need to supply a container layout for this fragment.

            FragmentManager fragmentManager = getFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

            Fragment fragment = MyFragment.newInstance(“Gunhan”, 28);
            fragmentTransaction.add(R.id.fragment_container, fragment);
            fragmentTransaction.commit();

            fragment_container could be any layout in your activities main layout xml. It will attach Fragment’s layout on this container layout.

  3. Olushi Luqman Opemipo Avatar
    Olushi Luqman Opemipo

    clean and elegant solution. thanks for this

  4. Drew Seta Avatar
    Drew Seta

    Hey, I did the code just like it is here but I’m using it in a viewpager. When I try to go back to the previous fragment, the app crashes. Can anyone help me with it? Also, I read that you aren’t suppose to create fragment constructors with arguments. They are suppose to have no arg constructor for a default and thats it.

    1. Gunhan Avatar

      Yep that’s right. You should not use a constructor with arguments for fragments instead use the method described in the blog post.
      For your crash, if you share the crash report etc. it might be helpful to figure out what’s wrong. Otherwise it is impossible to find the error from here.

      1. Drew Seta Avatar
        Drew Seta

        Hi, thanks for the quick response. So I got the code working, however when I put it into the viewpager the text feilds are null. the code is on the following github repo. I only added the files you need to help. I can upload the full project if you need. Can you please help. I’m making an app and I can really use your help! thank you!!

        https://github.com/dpr5/Fragments/tree/master

        1. Gunhan Avatar

          Hey @disqus_qsOyn7WIb7:disqus I’ve created an example repository for you.
          I hope it will be helpful to you.
          https://github.com/gunhansancar/FragmentPagerExample

          1. Drew Seta Avatar
            Drew Seta

            Hi,
            WOW! Thank you for the extra time and work you put into this. You really went above and beyond for this. I really appreciate this. I haven’t had a chance to check out the code fully but I did run it with some errors I will fix soon. I have some stuff I’ve been really busy with.

            That leads me to ask if you know any good API tutorials for Android Studio? I have a project I have to do and I need your help or anyway to get a good tutorial on this. I have most of the code written and it works with one API but not with the one I want to use. The XML is written a bit differently with the one I need to use so that’s why I can’t figure it out. Can you please help me with it?

          2. Gunhan Avatar

            Hey @disqus_qsOyn7WIb7:disqus, your question is not crystal clear but If you’re trying to integrate your Android project with a RESTful or XML backend API, then I can recommend you to take a look at http://square.github.io/okhttp/ that’s the one I use for all my projects. It can use JSON or XML without any problems. Just search for “Okhttp Xml” There should be plentiful of examples.

          3. Drew Seta Avatar
            Drew Seta

            Ok so is it possible for you to review my code and tell me where I am going wrong. I think I have most of the code, 95% done, I’m just screwing up somewhere in the syntax. I think it is in the “Chat” file. I’m still learning how to parse XML API data and I think I wrote something wrong there. This code was used for another API request and it worked fine, but this one doesn’t even though I updated the get methods.

            If you have the time the github link is as follows and I uploaded only the needed files.

            https://github.com/dpr5/API-assistance/tree/master

            The API link is in the “Chat” file on there. If you can, please help me out, I would understand if you can’t, no pressure. Its for a job interview so I’m really trying my best with this.

          4. Gunhan Avatar

            The example you shared seems working fine to me. The API in the example produces JSON responses but you asked XML. If you need to convert this one to XML parser you need to add to your RetrofitBuilder the following: “.addConverterFactory(SimpleXmlConverterFactory.create(new Persister(new AnnotationStrategy())))” and in your data/model/domain object you need to mark it with “org.simpleframework.xml.Root” and “org.simpleframework.xml.Element” annotations. It’s not possible to example it in comments since it requires much more code. I would possibly prepare another article about it but it takes time to prepare a good post with good examples.

          5. Drew Seta Avatar
            Drew Seta

            ok I’ll try it. I ran this code and it doesn’t produce any result in the emulator. I tried this code with another api link following a tutorial and it worked for that so I don’t know why its not working and producing an output with this new link.

          6. Drew Seta Avatar
            Drew Seta

            btw, is there an email I can contact you through that will be better than this thread? I’m just an innocent indie developer trying to get a better understanding of android so any god guidance here and there will be awesome from someone with more experience. lol

  5. adamkx7x Avatar
    adamkx7x

    I have a big problem with this approach 🙁 everything was working great before I’ve upgraded Gradle to 3.0.x now whenever my data is updated and I’m recreating fragments in my viewpager the bundle passed on newInstance method is not received in getArguments() in onCreateView but instead I receive the first passed data in bundle !! this is very odd! can you take a look at my code ? http://bit.ly/2nTmDYS feel free to test it

    1. Gunhan Avatar

      Hey @adamkx7x:disqus I checked out your project. First of all, I didn’t oserve getArguments() null in onCreateView for your HomeFragment. I tried rotate device and going to other pages on the viewpager but getArguments always full.
      Also what do you mean by ‘first passed data in bundle’?
      My other notes: your presenter implementation is incorrect. you shouldn’t create your presenter every time activity is recreated. For instance: when you show a progressview or in your naming style ‘showWait’ meanwhile if the activity is recreated because of a configuration change, you lose your your state. Because lets imagine network call takes 5 seconds, and in the 3rd second your activity is recreated. And you create your presenter all the time. Then it will start with ‘without any wait view’. To fix it either you need to keep your presenter in a application level container or more recently android team released a new library called ‘android architecture components’ inside you can find ‘ViewModel’ base class. It solves this problem. Take a look at it.

      Second issue is: you are holding reference to the destroyed Fragments because you create them in your activity and keep reference and pass it to the adapter which is the incorrect approach. Because Pager destroys the fragment by its will and it is already caching the Fragments why do you keep the references to the fragments?

      Third: Do not hold any data or logic in your activity if you are using presenters. Like ‘weatherData’,’days’, ‘isCelsius’, dataformatters etc.

      I know this become a long response and not directly solving your question but you should also address these as well. If you can pinpoint where exactly your data is not coming I might be more helpful.

      1. adamkx7x Avatar
        adamkx7x

        Thank you for your great advice! I’ll definitely try them out 🙂 back to original problem: I didn’t say that you’ll receive null in getArguments(), lets say that you search for London (or app search for some city via GPS after first launch) and after that you would search for any other city e.g. Berlin you will see in LOG.d that new instances for fragments gets data for Berlin but their getArguments() in onCreateView gets data from Londyn. which droves me crazy xD

        1. Gunhan Avatar

          @adamkx7x:disqus OK i debugged your solution again. I understand the problem now.

          So the problem is on your ‘createFragments’ method, you assume that you’ve cleared the previously created fragments by calling ‘clear’ method of the support fragment manager. However you’re just cleared the references. In the Android system they’re still alive at this point. And then even though you’ve created another adapter with a newly created list of Fragments, ‘FragmentPagerAdapter’ still uses the old ones.
          Why? Because in its implementation, it doesn’t call ‘getItem(int position)’ method as you expected. Instead it first tries to find the fragment by using its ‘tag’ attribute.

          So long story short update top part of your ‘createFragments’ method by using the following:

          fragments.clear();
          List allFragments = getSupportFragmentManager().getFragments();
          if(allFragments != null) {
          for (Fragment fragment : allFragments) {
          FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
          trans.remove(fragment);
          trans.commitNowAllowingStateLoss();
          }
          }

          Even though this solves your current problem, I think you should update your codebase, by not destroying and creating fragments all time. Please take a look at the new ‘ViewModel’ from google. You can reuse the same ViewModel in your Activity and Fragments that way data sharing would be much easier. Maybe I should write a simple article about it. It might be helpful to other people as well.

Leave a Reply

Your email address will not be published. Required fields are marked *