13

According to http://konmik.github.io/snorkeling-with-dagger-2.html i could just add

inject(Anything anything)

into AppComponent.java, but this doesn't work for me, in the articles example:

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    void inject(MainActivity activity);
    void inject(MainFragment fragment);
    void inject(MainToolbarView view);
}

If I try to inject dependencies into my fragment the injected members remain null. What obvious error am I missing here?

Relevant sources:

ApplicationComponent.java

@Singleton
@Component(
    modules = ApplicationModule.class
)
public interface ApplicationComponent {
    void injectApplication(BaseApplication application);

    Prefs providePrefs();
}

ApplicationModule.java

@Module
public class ApplicationModule {
    private final Application application;

    public ApplicationModule(Application application) {
        this.application = application;
    }

    @Provides
    Application application() {
        return application;
    }

    @Singleton
    @Provides
    Prefs providePrefs() {
        return Prefs.with(application);
    }
}

ActivityComponent.java

@AScope
@Component(
    dependencies = {
        ApplicationComponent.class
    },
    modules = ActivityModule.class
)
public interface ActivityComponent extends ApplicationComponent {
    void injectActivity(BaseActivity activity);
    void injectFragment(BaseFragment fragment);
}

ActivityModule.java

@Module
public class ActivityModule {
    private final Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    @Provides
    Activity activity() {
        return activity;
    }

    @Provides
    Context context() {
        return activity;
    }
}

BaseApplication.java

public class BaseApplication extends Application {
    private ApplicationComponent component;


    @Override
    public void onCreate() {
        super.onCreate();
        component = createComponent();

        component.injectApplication(this);
    }

    private ApplicationComponent createComponent() {
        return Dagger_ApplicationComponent.builder()
            .applicationModule(new ApplicationModule(this))
            .build();
    }

    public ApplicationComponent getComponent() {
        return component;
    }
}

BaseActivity.java

public class BaseActivity extends ActionBarActivity implements HasComponent<ActivityComponent> {
    ActivityComponent component;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        component = Dagger_ActivityComponent.builder()
            .applicationComponent(((BaseApplication) getApplication()).getComponent())
            .activityModule(new ActivityModule(this))
            .build();

        component.injectActivity(this);

    }

    @Override
    public ActivityComponent getComponent() {
        return component;
    }
}

BaseFragment.java

public class BaseFragment extends Fragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ((BaseActivity)getActivity()).getComponent().injectFragment(this);
    }
}

ListFragment.java

public class ListFragment extends BaseFragment {

    @Inject ListFragmentPresenterImpl listFragmentPresenter;

    public static ListFragment newInstance(){
        ListFragment result = new ListFragment();

        return result;
    }

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

        View rootView = inflater.inflate(R.layout.list_fragment, container, false);

        Log.d("",listFragmentPresenter.sayHello());  // NPE HERE

        return rootView;
    }
}

ListFragmentPresenterImpl.java

public class ListFragmentPresenterImpl implements ListFragmentPresenter {

    @Inject
    public ListFragmentPresenterImpl() {
    }

    @Override
    public String sayHello() {
        return "hello";
    }
}

In Dagger_ActivityComponent the generated code looks like this:

@Override
public void injectActivity(BaseActivity activity) {  
  baseActivityMembersInjector.injectMembers(activity);
}

@Override
public void injectFragment(BaseFragment fragment) {  
  MembersInjectors.noOp().injectMembers(fragment);
}

shouldn't here be a baseFragmentMemebersInjector?

Thanks!

chrystolin
  • 1,067
  • 1
  • 9
  • 17

1 Answers1

11

Inject child fragments instead of BaseFragment. In your case:

public class ListFragment extends BaseFragment {

    @Inject ListFragmentPresenterImpl listFragmentPresenter;

    public static ListFragment newInstance(){
        ListFragment result = new ListFragment();
        return result;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ((MainActivity)getActivity()).getComponent().injectFragment(this);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        View rootView = inflater.inflate(R.layout.list_fragment, container, false);
        Log.d("",listFragmentPresenter.sayHello());  // NPE HERE
        return rootView;
    }
}

And in your Component class:

@AScope
@Component(dependencies = ApplicationComponent.class,
           modules = ActivityModule.class)
interface ActivityComponent {
    void injectActivity(MainActivity activity);
    void injectFragment(ListFragment fragment);
    // Put any more injects here, if BaseFragment has
    // any other children that need to be injected, for example:
    void inject(MapFragment fragment);
    void inject(DetailFragment fragment);
}

Parent -> Child injection is not working in Dagger 2. It was covered here, here and this SO question.

Community
  • 1
  • 1
Kirill Boyarshinov
  • 6,143
  • 4
  • 33
  • 29
  • Kirill you just saved me after 2 grueling hours. This SO was difficult to find for me, as I was looking at the surface error which was a NPE. I guess it was me asking the wrong question. I'm going to try and edit the code part of your answer, as I got confused thinking I should create more Component interfaces for every Fragment. – bytehala May 12 '15 at 17:26
  • 1
    @lemuel I'm glad it helps. You are welcome for edits, however, it may be not good to create single component to inject things everywhere. In big projects better to separate them into components and subcomponents. – Kirill Boyarshinov May 12 '15 at 18:04
  • 1
    `getActivity` can return null `onCreate`, better to call it `onAttach` – mbmc Oct 28 '15 at 02:16
  • @KirillBoyarshinov i am getting null pointer exception some times in fragment at ((MainActivity)getActivity()).getComponent().injectFragment(this); – Rax Nov 30 '16 at 09:11