39

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?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Ofek Agmon
  • 5,040
  • 14
  • 57
  • 101
  • if using hilt, then this could be a solution https://stackoverflow.com/questions/63766576/injecting-a-repository-into-a-service-in-android-using-hilt – Fayaz Feb 09 '22 at 14:09

2 Answers2

49

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.

David Medenjak
  • 33,993
  • 14
  • 106
  • 134
  • 2
    thanks for your answer. the code wont compile, saying "Builder() has private access in Builder" when I am trying to inject the alarm – Ofek Agmon May 05 '16 at 14:45
  • @OfekAgmon This is why I added the first line in my answer. It is `DaggerMyServiceComponent.builder()`. – David Medenjak May 05 '16 at 14:50
  • thanks, also I needed to take out the "new" before the DaggerMyServiceComponent.builder() – Ofek Agmon May 05 '16 at 14:53
  • 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? – Paula Kristin Dec 27 '16 at 20:41
  • @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. – franta kocourek Mar 13 '17 at 09:56
  • Is it possible to do the injection on the onHandleIntent instead of the onCreate method for intent service ? – user1510006 Sep 04 '19 at 21:05
  • @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 – David Medenjak Sep 04 '19 at 21:45
  • 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. – AdamHurwitz Feb 20 '20 at 05:23
  • 1
    @AdamHurwitz I don't think it will matter for most cases since there is not much going on in `onCreate()` in services – David Medenjak Feb 20 '20 at 09:03
  • Anyone figured out a way to do Service injection without field injection? – AllDayAmazing Mar 11 '20 at 20:42
  • 1
    @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 – David Medenjak Mar 12 '20 at 08:20
  • @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 – AllDayAmazing Mar 26 '20 at 05:34
20

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.

Pouya Danesh
  • 1,557
  • 2
  • 19
  • 36