android / architecture-components-samples

Samples for Android Architecture Components.
https://d.android.com/arch
Apache License 2.0
23.42k stars 8.29k forks source link

How to apply Dagger injection into services? #253

Closed pzmudzinski closed 6 years ago

pzmudzinski commented 6 years ago

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

Nauce commented 6 years ago

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;
    }

}
pzmudzinski commented 6 years ago

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.

florina-muntenescu commented 6 years ago

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

martinGele commented 4 years ago

Yup it did solve my problem

adam-hurwitz commented 4 years ago

@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?

Nauce commented 4 years ago

@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.

adam-hurwitz commented 4 years ago

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.

Nauce commented 4 years ago

@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.