Architecture-components-samples: How to apply Dagger injection into services?

Created on 16 Dec 2017  路  8Comments  路  Source: android/architecture-components-samples

Fragment & Activity injection is done through registerActivityLifecycleCallbacks and registerFragmentLifecycleCallbacks, but it's unclear how to turn on injection for Services.
Any ideas?

Most helpful comment

Here's my solution:

Make Application implements HasServiceInjector and inject a DispatchingAndroidInjector for services.

public class App extends Application implements HasActivityInjector, HasServiceInjector {

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
    @Inject
    DispatchingAndroidInjector<Service> dispatchingServiceInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        AppInjector.init(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
    }

    @Override
    public AndroidInjector<Service> serviceInjector() {
        return dispatchingServiceInjector;
    }

}

Create a new module to perform injection over your services.

@Module
abstract class ServiceBuilderModule {

    @ContributesAndroidInjector
    abstract MyService contributeMyService();

}

Register your new module in your application's component.

@Component(modules = {
        AndroidSupportInjectionModule.class,
        AppModule.class,
        ActivityBuilderModule.class,
        ServiceBuilderModule.class
})
@Singleton
public interface AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(App application);

        AppComponent build();
    }

    void inject(App app);

}

And finally, override method onCreate of the service adding AndroidInjection.inject(this).

public class MyService extends Service {

    @Override
    public void onCreate() {
        AndroidInjection.inject(this);
        super.onCreate();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

All 8 comments

Here's my solution:

Make Application implements HasServiceInjector and inject a DispatchingAndroidInjector for services.

public class App extends Application implements HasActivityInjector, HasServiceInjector {

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
    @Inject
    DispatchingAndroidInjector<Service> dispatchingServiceInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        AppInjector.init(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
    }

    @Override
    public AndroidInjector<Service> serviceInjector() {
        return dispatchingServiceInjector;
    }

}

Create a new module to perform injection over your services.

@Module
abstract class ServiceBuilderModule {

    @ContributesAndroidInjector
    abstract MyService contributeMyService();

}

Register your new module in your application's component.

@Component(modules = {
        AndroidSupportInjectionModule.class,
        AppModule.class,
        ActivityBuilderModule.class,
        ServiceBuilderModule.class
})
@Singleton
public interface AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(App application);

        AppComponent build();
    }

    void inject(App app);

}

And finally, override method onCreate of the service adding AndroidInjection.inject(this).

public class MyService extends Service {

    @Override
    public void onCreate() {
        AndroidInjection.inject(this);
        super.onCreate();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

Ok, but what about integration tests? It will crash since Github example app is using different Application class which disables Dagger initialization and you are responsible for manually providing mock-objects into properties.

This is a Dagger question, not an Architecture Components question.
Closing as off-topic

Yup it did solve my problem

@Nauce, Does it matter where Dagger is injected in the Service class, before or after super.onCreate(), or is it similar to a fragment in that Dagger can be injected before or after the super call?

@AdamSHurwitz , it should be done before super.onCreate().
This code version is outdated. Take a look into Dagger classes to extend from them, like DaggerService.

Thanks @Nauce.

Is the reasoning similar to activity injection which the documentation explains:

...to avoid issues with fragment restoration. During the restore phase in super.onCreate(), an activity attaches fragments that might want to access activity bindings.

@AdamSHurwitz, you are welcome. It could be done before or after calling super.onCreate() like in Fragment (there are not lifecycle problems), but constructor injection is preferred whenever possible to ensure that no field is referenced before it has been set, which helps avoid NullPointerExceptions.

For example, a class can extend from a parent class that runs one of the dependencies before injecting them.

Was this page helpful?
0 / 5 - 0 ratings