Search code examples
androidandroid-fragmentsdependency-injectiondagger-2

Cannot return null from a non-@Nullable @Provides method error


I have an module named ViewModelModule. This is it:

@Module(includes = RepositoryModule.class)
public class ViewModelModule {

    StepListFragment stepListFragment;

    StepViewFragment stepViewFragment;

    @Provides
    public StepListFragment getStepListFragment() {
        return stepListFragment;
    }

    @Provides
    public StepViewFragment getStepViewFragment() {
        return stepViewFragment;
    }

    public ViewModelModule(StepListFragment stepListFragment) {
        this.stepListFragment = stepListFragment;
    }

    public ViewModelModule(StepViewFragment stepViewFragment) {
        this.stepViewFragment = stepViewFragment;
    }

    @Provides
    IngredientsViewModel ingredientsViewModel(StepListFragment stepListFragment, RecipesRepository repository) {
        return ViewModelProviders.of(stepListFragment, new ViewModelProvider.Factory() {
            @Override
            public <T extends ViewModel> T create(Class<T> modelClass) {
                return (T) new IngredientsViewModel(repository);
            }
        }).get(IngredientsViewModel.class);
    }

    @Provides
    StepsViewModel stepsViewModel(StepViewFragment stepViewFragment, RecipesRepository repository) {
        return ViewModelProviders.of(stepViewFragment, new ViewModelProvider.Factory() {
            @Override
            public <T extends ViewModel> T create(Class<T> modelClass) {
                return (T) new StepsViewModel(repository);
            }
        }).get(StepsViewModel.class);
    }

}

There all my ViewModules components will be provided. But not at the same moment.

I have one component for each Fragment:

@Component(modules = {RepositoryModule.class, ContextModule.class, ViewModelModule.class})
public interface StepListComponent {
    void inject (StepListFragment stepListFragment);
}



@Component(modules = {RepositoryModule.class, ContextModule.class, ViewModelModule.class})

public interface StepViewComponent {
    void inject (StepViewFragment stepViewFragment);
}

In the first moment StepListFragment is showed and I instantiate the component as below:

DaggerStepListComponent.builder()
        .applicationModule(new ApplicationModule(this.getActivity().getApplication()))
        .contextModule(new ContextModule(this.getActivity()))
        .viewModelModule(new ViewModelModule(this)).build().inject(this);

As you see at the end of the clause I inject the fragment.

After that, when I start the other fragment, I will do the same thing. But when it calls the code above I received this error:

Caused by: java.lang.NullPointerException: Cannot return null from a non-@Nullable @Provides method

Off course the error cause is the fact that I had not instantiated the StepViewFragment stepViewFragment yet; But I don't want to use it now, so it would not cause problem.

I tried to add the @Nullable clause as below:

@Nullable
@Provides
public StepListFragment getStepListFragment() {
    return stepListFragment;
}

@Nullable
@Provides
public StepViewFragment getStepViewFragment() {
    return stepViewFragment;
}

but then I get a compile time error:

    Error:(15, 10) error: StepListFragment is not nullable, but is being provided by @android.support.annotation.Nullable @Provides 
com.github.alexpfx.udacity.nanodegree.android.baking_app.step.master.steps.StepListFragment com.github.alexpfx.udacity.nanodegree.android.baking_app.step.master.di.ViewModelModule.getStepListFragment()
    at:     com.github.alexpfx.udacity.nanodegree.android.baking_app.step.master.steps.StepListFragment is injected at
    com.github.alexpfx.udacity.nanodegree.android.baking_app.step.master.di.ViewModelModule.ingredientsViewModel(stepListFragment, …)
    com.github.alexpfx.udacity.nanodegree.android.baking_app.step.master.ingredients.IngredientsViewModel is injected at
    com.github.alexpfx.udacity.nanodegree.android.baking_app.step.master.steps.StepListFragment.ingredientsViewModel
    com.github.alexpfx.udacity.nanodegree.android.baking_app.step.master.steps.StepListFragment is injected at
    com.github.alexpfx.udacity.nanodegree.android.baking_app.step.master.di.StepListComponent.inject(stepListFragment)

So the question is: Is there a way to solve this by keeping the configuration using the same module, or should I separate each @Provides into its respective module? Is that a good practice?


Solution

  • Fragments should not be dependencies for your ViewModel - the ViewModel is supposed to have a greater scope than that of the Fragment.

    Please see this GitHub repo with a sample project that uses Android architecture components with Dagger2.