14

This is the Nth question about how to store @Singleton scoped Dagger 2 Components whose lifetime should equal the application's lifetime.

In Android apps using Dagger 2 there is usually at least one Component which is @Singleton scoped and should last for all the application's lifetime: because of these requirements it is usually initialised and stored inside a custom Application class.

Since the instance of this Component must be reachable in all parts of our Application I've seen code like this:

1. Store the component in a public static variable inside the application class.

public class App extends Application {

    public static AppComponent appComponent;

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

        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this)).build();
    }
}

This way it can be accessed anywhere else with:

App.appComponent.inject(this);

2. Store the component in a private variable inside the application instance and create a static accessor for it.

public class App extends Application {

    private static AppComponent appComponent;

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

        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this)).build();
    }

    public static AppComponent getAppComponent() {
        return appComponent;
    }
}

This way it can be accessed anywhere else with:

App.getAppComponent().inject(this);

3. Store the component in a private variable inside the application instance and create a non static accessor for it.

public class App extends Application {

    private AppComponent appComponent;

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

        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this)).build();
    }

    public AppComponent getAppComponent() {
        return appComponent;
    }
}

This way it can be accessed only from class instances which hold a reference to a Context:

// From within an Activity.
((App) getApplication()).getAppComponent().inject(this);

// From within a Fragment.
((App) getActivity().getApplication()).getAppComponent().inject(this);

// From within any other class which holds a reference to a Context. 
((App) context.getApplicationContext()).getAppComponent().inject(this);

This last way makes it pretty much compulsory to pass a Context reference to any class willing to access the Component (even if that Context isn't needed by that class for any other purposes).

IMHO having to "manually inject" a Context instance only to access the injector itself sounds a bit counter intuitive.

On the other side many advise against using static variables but: why? If an object must stay in memory for the application's lifetime (which means for the whole lifetime of the JVM instance) what's the problem if it's stored in a static variable?

Others say that static stuff can't be mocked in tests and it's true, though I'm not sure I totally get this because it is the DI pattern which enables easy mocking/testing and not the injector itself, so why would we want to mock the injector itself?

What are the pros and cons of these alternatives? Are there any other possible alternatives besides the ones already mentioned here?

Marco Romano
  • 1,169
  • 7
  • 24
  • How would the second option work as far as `private AppComponent appComponent;` is non static??? – Sarath Kn Jan 05 '17 at 09:13
  • It does indeed work. Why shouldn't it? – Marco Romano Jan 05 '17 at 11:55
  • How will you return the private variable `private AppComponent appComponent;` from a static method `public static AppComponent getAppComponent()` – Sarath Kn Jan 05 '17 at 14:27
  • That method is not static. – Marco Romano Jan 05 '17 at 15:20
  • 1
    Okay.. But your second option says **create a static accessor for it.** and access it with **App.getAppComponent().inject(this);**. Thats why pointed. And also, as you said if ***That method is not static*** then the second and third options become same – Sarath Kn Jan 06 '17 at 04:59

2 Answers2

5

With 1 and 2 you are using static references. This is a good thread about why to avoid them

Why are static variables considered evil?

So the only option left is the 3rd. That is what I am using on my projects. About if you should pass the context as argument or not, depends on the architecture of your project and how you designed the Dagger dependencies. Personally I don't have that problem because I am only injecting in Activities/Fragments. Can you give me an example where you need to pass the context to inject dependencies?

Community
  • 1
  • 1
mromer
  • 1,867
  • 1
  • 13
  • 18
  • You shouldn't exclude 1 and 2 as options just because they use static references, which are considered bad practice - but there are use cases for them and component is one of them. Downsides explained in your link like "statics have a lifetime that matches the entire runtime of the program" is not a downside. For this case, that's exactly what you want and need. – Singed Apr 03 '20 at 13:15
3

I use method #2. The main problem with method #1 is that you're exposing a mutable field. If your module doesn't require a Context to construct, you could make the field final. But as a matter of style, I still prefer not to expose fields.

You should normally avoid global state, especially in Android because of the complex and sometimes unintuitive lifecycles of components and the VM itself. But Application is the exception to this rule. Exactly one instance of it exists per VM, and its onCreate() method is called exactly once, before any other component is created. This makes it an acceptable place to create and store a static singleton.

Kevin Krumwiede
  • 9,868
  • 4
  • 34
  • 82