Using Dagger 2 to inject into service

31,448

Solution 1

I wrote the code from the top of my head, so there could be a typo or two.

You do it just the same as when injecting stuff into activities.

  1. Declare a component,
  2. add the inject method to that component,
  3. add a module providing your service
  4. create that components builder
  5. add your module to the builder
  6. inject your service with the component

Your module and component would look something like this (maybe add some scope)

@Module
class ServiceModule {

    MyService mService;

    ServiceModule(MyService service) {
        mService = service;
    }

    @Provides
    MyService provideMyService() {
        return mService;
    }
}

@Component(modules=ServiceModule.class)
interface MyServiceComponent {
    void inject(MyService service);
}

Then in onCreate just create your component and inject your alarm.

@Inject
private SomeAlarm alarm;

public void onCreate() {
    DaggerMyServiceComponent.builder()
            .serviceModule(new ServiceModule(this))
            .build()
            .inject(this);

    alarm.doStuff();
}

This is assuming that your alarm can be constructor injected by having an @Inject annotated constructor like this:

class SomeAlarm {
    @Inject
    SomeAlarm(MyService service) {
        /*constructor stuff*/
    }
}

Else you would just also add the alarm creation to your module.

Solution 2

I know this question already has an answer but there are an other way to do this

first make your application extend HasServiceInjector like this:

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

}

then create a ServiceBuilderModule this will perform injection over services:

@Module
abstract class ServiceBuilderModule {

@ContributesAndroidInjector
abstract MyService contributeMyService();

}

then register the new module to your 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);

 }

then override the onCreate method of your service and add AndroidInjection.inject(this) like below code :

public class MyService extends Service {

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

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

}

code in kotlin is exact conversion of the code above. hope this helps some coders from now on.

Share:
31,448
Ofek Agmon
Author by

Ofek Agmon

Updated on February 29, 2020

Comments

  • Ofek Agmon
    Ofek Agmon over 4 years

    I have an app which is basically a service that runs all the time and alarms the user when something happens.

    When the service creates the alarm, it needs to give it his context so that the alarm can do callbacks to the service when something happens.

    For example:

    public MyService extends Service{
        private SomeAlarm alarm;
    
        @Override
        public void onCreate() {
            super.onCreate();
            alarm = new SomeAlarm(MyService.this);
        }
    }
    

    How can I inject the SomeAlarm class into the service, and give the SomeAlarm the service context as a variable?

  • Ofek Agmon
    Ofek Agmon about 8 years
    thanks for your answer. the code wont compile, saying "Builder() has private access in Builder" when I am trying to inject the alarm
  • David Medenjak
    David Medenjak about 8 years
    @OfekAgmon This is why I added the first line in my answer. It is DaggerMyServiceComponent.builder().
  • Ofek Agmon
    Ofek Agmon about 8 years
    thanks, also I needed to take out the "new" before the DaggerMyServiceComponent.builder()
  • Paula Kristin
    Paula Kristin over 7 years
    I hope it is ok to still ask a question but for me, everytime I make a new DaggerComponent I get the error of cannot find symbol class of my old dagger components during compilation. Anyone knows why?
  • franta kocourek
    franta kocourek over 7 years
    @PaulaKristin you have to compile your project (modules or whatever) first. That Dagger* class is created by Dagger compiler. So you have to provide a way of adding Dagger's compiler output to your classpath.
  • user1510006
    user1510006 almost 5 years
    Is it possible to do the injection on the onHandleIntent instead of the onCreate method for intent service ?
  • David Medenjak
    David Medenjak almost 5 years
    @user1510006 In theory you can inject at any given time, but it should only happen once, and before you try to use any of the injected objects (or you'll get a NPE). Doing it right away in onCreate ensures all of these, onHandleIntent can be called multiple times, so you'd have to add addtional checks
  • AdamHurwitz
    AdamHurwitz over 4 years
    When injecting Dagger inside of a Service class does it matter whether the init call is made before or after the super.OnCreate() call? i.e. For activities, it's important to be made before in case fragments are created from that activity. I'd expect a Service to be similar.
  • David Medenjak
    David Medenjak over 4 years
    @AdamHurwitz I don't think it will matter for most cases since there is not much going on in onCreate() in services
  • AllDayAmazing
    AllDayAmazing over 4 years
    Anyone figured out a way to do Service injection without field injection?
  • David Medenjak
    David Medenjak over 4 years
    @AllDayAmazing What would you like to do? You can use method injection which is similar to field injection, but since the Framework creates the Service you can't use constructor injection. Other than that you can always add a provision method to a component and grab your dependencies manually, using Dagger as a kind of service locator
  • AllDayAmazing
    AllDayAmazing over 4 years
    @DavidMedenjak thanks! Basically what I came up with - just was wondering if anyone knew of some hidden APIs or something that I was forgetting to make it possible to avoid field injection cleanly