2

I'm trying to update my simple android app to use Dagger2. I think I've managed to understand some of the basics and the basic stuff (activities, view models, helpers) are being created through it.

Now, there's still a small gotcha: the app has a button that, when pressed, must schedule a job service. I can refactor my jobservice so that its dependencies are passed through its constructor, but how do I instantiate it from my activity? In other words, how do I replace this code:

    val serviceComponent = ComponentName(getApplication(), DbUpdaterJob::class.java)
    var jobInfo = JobInfo.Builder(DB_UPDATER_JOB_ID, serviceComponent)
            .setRequiredNetworkType(networkType)
            .setPersisted(true)
            .build()

    val scheduler = getApplication<Application>().getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
    val res = scheduler.schedule(jobInfo)

so that instantiation is relegated to dagger?

Thanks!

Kishan Viramgama
  • 893
  • 1
  • 11
  • 23
Luis Abreu
  • 4,008
  • 9
  • 34
  • 63
  • A Service can never be instantiated by Dagger. Its instantiated by the framework. It can inject itself at a later time. – Gabe Sechan Sep 13 '18 at 16:54
  • but isn't there anything like daggerappcompatactivity to ease the instantiation of inner dependencies of my dbupdaterjob class? – Luis Abreu Sep 13 '18 at 17:02

2 Answers2

5

After some digging, it ended up being easier than I thought...So, I've created a new module for my job binding:

@Module
abstract class JobBindingModule {
    @ServiceScoped
    @ContributesAndroidInjector
    internal abstract fun provideDbUpdatedJob(): DbUpdaterJob
}

Btw, ServiceScoped is a new annotation which looks like this:

@Scope
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE, ElementType.METHOD)
annotation class ServiceScoped

I've added the module to my dagger component module collection. After that, I've replaced constructor parameter injection with property injection:

class DbUpdaterJob: JobService() {
    @Inject
    lateinit var dbManager: NewsManager

And, finally, I've overrided the onCreate method to inject the required dependencies:

override fun onCreate() {
    super.onCreate()
    AndroidInjection.inject(this)
}

And it seems to be working...

Luis Abreu
  • 4,008
  • 9
  • 34
  • 63
0

You need to define the providers:

@Provides
fun provideServiceComponent(app: Application): ComponentName =
        ComponentName(app, DbUpdaterJob::class.java) // assuming you have only one ServiceComponent


@Provides
fun provideJobInfo(
        serviceComponent: ComponentName
): JobInfo =
        JobInfo.Builder(DB_UPDATER_JOB_ID)
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
                .setPersisted(true)
                .build()
@Provides
@Singleton
fun provideJobScheduler(app: Application): JobScheduler = app.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler

and then inject

@Inject constructor(
        jobInfo: JobInfo,
        scheduler: JobScheduler
) {
    val res = scheduler.schedule(jobInfo)
            ...
}

It gets more complicated if you want to be able to create different ComponentName or JobInfo etc. In that case you need to use @Named injections.

kingston
  • 11,053
  • 14
  • 62
  • 116
  • I wrote this here without being able to compile so I hope I haven't made mistakes – kingston Sep 13 '18 at 17:03
  • ah I read now that you want to do it in an activity. It does not look right to me but anyway in that case you need to inject the fields instead of the parameters – kingston Sep 13 '18 at 17:07
  • My problem here is that I've changed the my JobService so that it receives another dependency through its constructor. I can do what I said, but I'll end up getting the same execption...I'm thinking that I'll need to pass the reference through a property (instead of injecting the dependency through the constructor - which made more sense) – Luis Abreu Sep 14 '18 at 08:30