I am getting IllegalStateException, AppModule must be set
Giannis Tsironis
1

Giannis Tsironis, this looks like the same issue that Mohamed Alouane posted in https://github.com/google/dagger/issues/899#issuecomment-337942596

So we are clear, this IllegalStateException is not reproducible in any of the branches / tags in the companion Github project as it is without any modifications.


The Issue

Assuming that the issue you are having is the same as the issue I linked above, then the issue is that you are attempting to use @Provides on a non-static instance method in your abstract AppModule. Perhaps something like the following,

@Module(includes = AndroidInjectionModule.class)
abstract class AppModule {
    @Provides
Car car() {
return new Car();
}
}
class Car {}

The above results in a crash at runtime,

Caused by: java.lang.IllegalStateException: AppModule must be set

The Fix

Making the method static will fix the issue,

@Module(includes = AndroidInjectionModule.class)
abstract class AppModule {
    @Provides
static Car car() {
return new Car();
}
}
class Car {}

Keep reading for more details.


Diving Deeper

In order to provide dependencies in abstract modules, you must either use @Binds on an abstract method or use @Provides in a static method. This was not discussed in this article (though there were 2 places where I used @Provides; [1], [2]) nor is it discussed in the official Dagger 2 user guide, which makes it a common pitfall. So I’ll discuss it here.

Let’s take a look at our Example1PresenterModule,

@Module
public abstract class Example1PresenterModule {
    @Binds
@PerFragment
abstract Example1Presenter
example1Presenter(Example1PresenterImpl example1PresenterImpl);
}

This module is abstract. We are using the @Binds annotation here on an abstract method to bind a concrete implementation (Example1PresenterImpl) of the Example1Presenter interface. This is equivalent to the following,

@Module
public abstract class Example1PresenterModule {
    @Provides
@PerFragment
static Example1Presenter
example1Presenter(Example1View view,
SingletonUtil singletonUtil,
PerActivityUtil perActivityUtil,
PerFragmentUtil perFragmentUtil) {
return new Example1PresenterImpl(view, singletonUtil,
perActivityUtil, perFragmentUtil);
}
}

In this case, we are using @Provides on a static method to provide a concrete implementation (Example1PresenterImpl) of the Example1Presenter interface.

If we remove the static method modifier,

@Module
public abstract class Example1PresenterModule {
    @Provides
@PerFragment
Example1Presenter
example1Presenter(Example1View view,
SingletonUtil singletonUtil,
PerActivityUtil perActivityUtil,
PerFragmentUtil perFragmentUtil) {
return new Example1PresenterImpl(view, singletonUtil,
perActivityUtil, perFragmentUtil);
}
}

an IllegalStateException will crash the application at runtime when attempting to access the example 1 activity,

Caused by: java.lang.IllegalStateException: Example1PresenterModule must be set

On a side note, a compile-time error (which is better than a crash at runtime) will occur if a @Module contains both non-static @Provides methods and abstract @Binds or @Multibinds declaration. For example,

@Module
public abstract class Example1PresenterModule {
    @Binds
@PerFragment
abstract Example1Presenter
example1Presenter(Example1PresenterImpl example1PresenterImpl);
    @Provides
Car car() {
return new Car();
}
}

When to use Binds vs Provides?

Using @Binds saves us from having to write a lot of boilerplate object creation code. However, in some cases it is not possible to use @Binds. For example, take a look at our BaseActivityModule,

@Module
public abstract class BaseActivityModule {
...
@Provides
@Named(ACTIVITY_FRAGMENT_MANAGER)
@PerActivity
static FragmentManager
activityFragmentManager(Activity activity) {
return activity.getFragmentManager();
}
}

Here we need to provide a reference to the FragmentManager. In order to get the Activity’s FragmentManager, we must call its getFragmentManager() method. Since abstract methods cannot have a body, we use @Provides on a static method that takes in the activity and return activity.getFragmentManager() in the body.


I will add a link to this discussion in the article to avoid future pitfalls / issues / confusion.