google / dagger

A fast dependency injector for Android and Java.
https://dagger.dev
Apache License 2.0
17.35k stars 2k forks source link

Hilt: Cannot start service outside of application #2015

Closed LandonPatmore closed 3 years ago

LandonPatmore commented 3 years ago

Kotlin version: 1.3.72 Hilt (android, compiler, and plugin) version: 2.28.3-alpha

I'm trying to run a service in the background. I am using an application just so I can leverage Hilt for DI, but it will have no UI.

When I tried to run: adb shell am startservice test.testflight/.TestFlightService

I got: Starting service: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=test.testflight/.TestFlightService }

But my application crashed with:

07-30 23:41:01.169 3359-3359/test.testflight E/AndroidRuntime: FATAL EXCEPTION: main
    Process: test.testflight, PID: 3359
    java.lang.RuntimeException: Unable to create service test.testflight.TestFlightService: java.lang.IllegalStateException: Hilt service must be attached to an @AndroidEntryPoint Application. Found: class android.app.Application
        at android.app.ActivityThread.handleCreateService(ActivityThread.java:2887)
        at android.app.ActivityThread.-wrap4(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:148)
        at android.app.ActivityThread.main(ActivityThread.java:5422)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
     Caused by: java.lang.IllegalStateException: Hilt service must be attached to an @AndroidEntryPoint Application. Found: class android.app.Application
        at dagger.hilt.internal.Preconditions.checkState(Preconditions.java:83)
        at dagger.hilt.android.internal.managers.ServiceComponentManager.createComponent(ServiceComponentManager.java:65)
        at dagger.hilt.android.internal.managers.ServiceComponentManager.generatedComponent(ServiceComponentManager.java:58)
        at test.testflight.Hilt_TestFlightService.generatedComponent(Hilt_TestFlightService.java:54)
        at test.testflight.Hilt_TestFlightService.inject(Hilt_TestFlightService.java:49)
        at test.testflight.Hilt_TestFlightService.onCreate(Hilt_TestFlightService.java:28)
        at android.app.ActivityThread.handleCreateService(ActivityThread.java:2877)
        at android.app.ActivityThread.-wrap4(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:148) 
        at android.app.ActivityThread.main(ActivityThread.java:5422) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

My application class looks like:

package test.testflight

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

/**
 * This is only used for Hilt graph creation.  This will run as a service in the background without a UI.
 */
@HiltAndroidApp
class TestFlight : Application() {}

My service class looks like:

package tests.testflight

import android.app.Service
import android.content.Intent
import android.os.IBinder
import dagger.hilt.android.AndroidEntryPoint
import sharedfunctions.logging.LogManager

@AndroidEntryPoint
class TestFlightService : Service() {

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // always try to restart the service IF its killed by the OS
        // (we should not run into this but just in case)
        LogManager.d(TAG, "Test Flight Service started")
        return START_STICKY
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    companion object {
        private const val TAG = "TestFlightService"
    }
}
bcorso commented 3 years ago

This is likely do to having the backup agent enabled for you application in your AndroidManifest.xml (https://github.com/google/dagger/issues/748#issuecomment-611663561), which replaces your actual application with android.app.Application, as you can see from your error message:

 Caused by: java.lang.IllegalStateException: Hilt service must be attached to an @AndroidEntryPoint Application. Found: class android.app.Application

Unfortunately, I think the only real fix currently is to set allowBackup="false" in your AndroidManifest.

LandonPatmore commented 3 years ago

Funny enough, I already have allowBackup="false" set in my manifest file.

bcorso commented 3 years ago

Hmm, the allowBackup case is the only time I've seen Android ignore the actual application class in the manifest. Just to check, have you specified the application name in the manifest?

Other than that, I'm not sure but it sounds like it's not specific to Hilt since the root of the issue is that Android is not using the application your specifying in your manifest.

LandonPatmore commented 3 years ago

The name is specified in the manifest. It was a project started from the Android Studio create a project page. It targets api 23, but is compiled against 29. Could that be any hint?

bcorso commented 3 years ago

Sorry, I'm not really sure. I think the first thing to figure out is why Android is not using your application class, which shouldn't be related to Hilt. You might have luck asking on StackOverflow to see if anyone has run into similar issues.

I'm going to close this for now, but if you find more evidence that suggests this is related to Hilt we can reopen it.

LandonPatmore commented 3 years ago

@bcorso I pulled a bug dumb...I accidentally removed the android:name from the AndroidManifest file without realizing it... -_-

tomasvalek commented 11 months ago

How can I throw this error? I have allowBackup="true" and app class with @HiltAndroidApp