6

For an ordinary (non-nested fragment) I use the following approach

1) create dependencies(...) method for setting fragment's dependencies

class MyFragment extends MyFragment {
      void dependencies(Deps deps);
}

2) in MyFragment parent's activity onAttachFragment() method I just provide dependencies for fragment

class MyActivity{
    void onAttachFragment(Fragment f){
        ((MyFragment)f).dependencies(deps);
    }
}

For nested fragment there is no more onAttachFragment fragment called. Providing dependencies for fragment just for providing dependencies for nested fragment seems to be very cumbersome. So how could I provide dependencies for it?

wilddev
  • 1,904
  • 2
  • 27
  • 45
  • Use dagger2? It's been designed to manage this kind of things – Mimmo Grottoli Oct 13 '15 at 08:26
  • Mimmo Grottoli, I know about dagger2. But it's just a library to eliminate boilerplate code of dependency injection. There always should be the way to inject dependencies by constructor or special method. – wilddev Oct 13 '15 at 09:47
  • A constructor for Fragments or Activities that injects some dependency? Of course you can try, but at the end you'll find that dagger or dagger2 are the best things that you can develop by your own (at least, this is true for me) – Mimmo Grottoli Oct 13 '15 at 12:03
  • There is no way to inject dependency to fragment over constructor as Android recreates fragments by reflection. So I use just dependencies() method. The bad thing is there is no place in android where I can call this method for nested fragment to inject dependencies. Instead I have to pull dependencies in NestedFragment.onCreate method - this is an antipattern. – wilddev Oct 13 '15 at 13:12

4 Answers4

5

Just do it off the context which will be an activity. Create a getter for the dependencies on your activity. Fragments have access to the parent activity whether nested or not. Cast the context and then call the getter to get the dependencies in the nested activity.

FriendlyMikhail
  • 2,857
  • 23
  • 39
  • For extra credit create an interface ComponentProvider and have your activity implement it, which exposes a single method getComponent. The getContext should be cast to the interface rather than a specific activity allowing reuse of fragments across activities. – FriendlyMikhail Oct 20 '15 at 11:47
2

If MyFragment depends upon MyNestedFragment, and MyNestedFragment depends upon Deps; it follows that MyFragment also depends upon Deps. Of course, no instance of MyNestedFragment exists when Activity.onAttachFragment() is called, so you will have to wait until after you have inflated the layout in MyFragment.onCreateView() before supplying MyNestedFragment with its dependencies.

public class MyActivity {

    ...

    void onAttachFragment(Fragment f){
        ((MyFragment)f).dependencies(deps);
    }

    public static class MyFragment extends Fragment {

        private Deps deps;

        void dependencies(Deps deps) {
            this.deps = deps;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);

            // <fragment> element in fragment_main layout has
            //  android:tag set to nested_fragment
            ((MyNestedFragment)getChildFragmentManager()
                .findFragmentByTag("nested_fragment"))
                .dependencies(this.deps);

            return rootView;
        }
    }

    public static class MyNestedFragment extends Fragment {

        void dependencies(Deps deps) {
            ...
        }
    }

    ...
}

If all of this seems a bit messy, that's because Fragments are not POJOs you can just wire up in some arbitrary manner. Their lifecycles must be managed by nested FragmentManagers. If you create your fragments programmatically rather than using the <fragment> element, you will have a bit more control over their lifecycle at the cost of more complexity.

If you want to treat Android like an IoC container, then RoboGuice may be what you are looking for:

public class MyActivity extends roboguice.activity.RoboFragmentActivity {

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // This only needs to be called once for the whole app, so it could
        // be in the onCreate() method of a custom Application subclass 
        RoboGuice.setUseAnnotationDatabases(false);

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
    }

    public static class MyNestedFragment extends Fragment {

        @Inject
        private Deps deps;

        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            // this isn't necessary if you extend RoboFragment
            roboguice.RoboGuice.getInjector(activity).injectMembers(this);
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

            //This would not even be possible in the previous example
            // because onCreateView() is called before dependencies()
            // can be called.
            deps.method();

            View rootView = inflater.inflate(R.layout.fragment_nested, container, false);
            return rootView;
        }
    }
}

@Singleton
public class Deps {
    public void method() {
        System.out.println("Deps.method()");
    }
}
robinj
  • 921
  • 5
  • 14
2

You try to set the dependecies, when the fragments are attached. Instead of this, try to get the dependencies from the fragment when needed. There is an example:

public class MyActivity extends Activity {

    public Deps getDepsForFragment(Fragment fragment) {
        if (fragment instanceof MyFragment) {
            return depsForMyFragment;
        } else if (fragment instanceof MyNestedFragment) {
            return depsForMyNestedFragment;
        } else {
            return null;
        }
    }
}

public class MyFragment extends Fragment {

    private Deps deps;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            MyActivtiy myActivity = (MyActivtiy) context;
            deps = myActivity.getDepsForFragment(this);
        } catch (ClassCastException e) {
            throw new ClassCastException("This fragment attached to an activity which can't provide the required dependencies.");
        }
    }
}

// this is the same as the MyFragment
public class MyNestedFragment extends Fragment {

    private Deps deps;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            MyActivtiy myActivity = (MyActivtiy) context;
            deps = myActivity.getDepsForFragment(this);
        } catch (ClassCastException e) {
            throw new ClassCastException("This fragment attached to an activity which can't provide the required dependencies.");
        }
    }
}

Of course, you can make separated method for get deps in the activity (like getDepsForMyFragment and getDepsForMyNestedFragment).

Zsolt Mester
  • 1,053
  • 9
  • 14
1

Just keep the hierarchy logics, and it should be something like this:

class MyActivity{
    void onAttachFragment(Fragment f){
        ((MyFragment)f).dependencies(deps);
    }
}

class MyFragment extends MyFragment {
      void dependencies(Deps deps) {
          //TODO: do dependencies of my fragment before
          ((MyNestedFragment)childF).nestedDependencies(deps);
          //TODO: do dependencies of my fragment after
      }
}

class MyNestedFragment extends MyNestedFragment {
      void nestedDependencies(Deps deps);
}
Iliiaz Akhmedov
  • 867
  • 7
  • 17
  • It is very strange to inject dependencies to MyFragment, because it doesn't need them. MyFragment doesn't depend on deps. – wilddev Oct 12 '15 at 22:05
  • Iliiaz Akhmedov, MyFragment doesn't depend on deps, but MyNestedFragment does! So passing deps to MyFragment is antipattern. – wilddev Oct 13 '15 at 09:36
  • Your nested fragment is a dependency of your fragment, so, it is reasonable to inject it in this hierarchy – Iliiaz Akhmedov Oct 13 '15 at 16:19