Inject abstract class (base activity) with HILT

Viewed 7770

I just begin to try to use Hilt on my very simple project. For now it was all on Dagger2 but I would like to migrate to Hilt.

I have an activity :

@AndroidEntryPoint
class MainActivity : BaseActivity() { 
    // SOME STUFF
}

And a BaseActivity like this :

abstract class BaseActivity : AppCompatActivity() {
    // SOME STUFF
}

Also, I have a class using the baseActivity to display a dialog. For example :

@FragmentScoped
class TestComponentImpl @Inject constructor(
    private val baseActivity: BaseActivity
) : TestComponent {

    override fun displayDialog() {
        MaterialDialog(baseActivity).show { ... 
    }
}

However, when I try to compile, I have this error :

BaseActivity cannot be provided without an @Provides-annotated method.

So, my question is : How to constructor inject an abstract class. I tried a lot of things but without success, like this in a Hilt module :

    @Provides
    @Singleton
    fun provideBaseActivity(): BaseActivity{
        return BaseActivity() // Of course, it can't work cause it's an abstract class
    } 

OR (like I did with Dagger) :

    @Provides
    @PerActivity
    fun appCompatActivity(baseActivity: BaseActivity) = baseActivity as AppCompatActivity

Also, I'm just a beginner with Hilt so, maybe I miss something. I will keep on search :)

Thanks for your time and your answers :)

EDIT :

I searched on my own during few days and this seems to work but doesn't seem really good ...

@Singleton
@Provides
fun provideBaseActivity(baseActivity: BaseActivity): AppCompatActivity {
    return baseActivity
}

But only if I put my baseActivity like this :

open class BaseActivity Inject constructor() : AppCompatActivity() 

However, after that, if I try to use my injected baseActivity in my TestComponentImpl like this (like I was doing with Dagger before Hilt):

override fun displayError() {
    Snackbar.make(
        baseActivity.findViewById(android.R.id.content),
        "My error text",
        Snackbar.LENGTH_LONG
    ).apply {
        show()
    }

I've got another error :

java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference

So I don't think my baseActivity injection is fully well done. I'm keeping to investigate ;)

2 Answers

A bit hacky (type-casting) but should work (untested, writing off the top of my head so feel free to let me know if something isn't working as expected):

Create such Module:

@Module
@InstallIn(ActivityComponent::class)
object BaseActivityModule {

  @Provides
  fun provideBaseActivity(activity: Activity): BaseActivity {
    check(activity is BaseActivity) { "Every Activity is expected to extend BaseActivity" }
    return activity as BaseActivity  
  }
}

The last cast as BaseActivity is probably unnecessary. Kotlin compiler should handle this.

@Boysteuf @Bartek Lipinski

Facing the below error :

error: [Dagger/MissingBinding] android.app.Activity cannot be provided without an @Inject constructor or an @Provides-annotated method.
  public abstract static class SingletonC implements EzeApp_GeneratedInjector,
                         ^
      android.app.Activity is injected at
          com.ezetap.consumerpaymentsdk.di.module.BaseActivityModule.provideBaseActivity(activity)
      com.ezetap.consumerpaymentsdk.base.BaseSDKActivity is injected at
          com.ezetap.consumerpaymentsdk.di.module.AppModule.provideServicePresenterPresenter(baseSDKActivity, …)
      com.ezetap.consumerpaymentsdk.presenters.ServicePresenter is injected at
          com.ezetap.consumerpaymentsdk.activity.ServiceSDKActivity.presenter
      com.ezetap.consumerpaymentsdk.activity.ServiceSDKActivity is injected at
          com.ezetap.consumerpaymentsdk.activity.ServiceSDKActivity_GeneratedInjector.injectServiceSDKActivity(com.ezetap.consumerpaymentsdk.activity.ServiceSDKActivity) [com.ezetap.consumerpaymentsdk.base.EzeApp_HiltComponents.SingletonC → com.ezetap.consumerpaymentsdk.base.EzeApp_HiltComponents.ActivityRetainedC → com.ezetap.consumerpaymentsdk.base.EzeApp_HiltComponents.ActivityC]
  The following other entry points also depend on it:
      com.ezetap.consumerpaymentsdk.activity.SimActivity_GeneratedInjector.injectSimActivity(com.ezetap.consumerpaymentsdk.activity.SimActivity) [com.ezetap.consumerpaymentsdk.base.EzeApp_HiltComponents.SingletonC → com.ezetap.consumerpaymentsdk.base.EzeApp_HiltComponents.ActivityRetainedC → com.ezetap.consumerpaymentsdk.base.EzeApp_HiltComponents.ActivityC]

AppModule

@Module(includes = [NetworkModule::class , BaseActivityModule::class ])
@InstallIn(SingletonComponent::class)
object AppModule {

    @Provides
    @Singleton
    fun provideBasePresenter(baseSDKActivity: BaseSDKActivity ,ezeApi: EzeApi, @ApplicationContext context: Context ) : BaseSDKPresenter  {
        return BaseSDKPresenter(baseSDKActivity, ezeApi , context )
    }


    @Provides
    @Singleton
    fun provideSimPresenter(baseSDKActivity: BaseSDKActivity, ezeApi: EzeApi , @ApplicationContext context: Context ) : SimPresenter  {
        return SimPresenter(baseSDKActivity , ezeApi , context)
    }

    @Provides
    @Singleton
    fun provideServicePresenterPresenter(baseSDKActivity: BaseSDKActivity,ezeApi: EzeApi , @ApplicationContext context: Context ) : ServicePresenter {
        return ServicePresenter(baseSDKActivity , ezeApi , context)
    }

}

BaseActivityModule

@Module
@InstallIn(ActivityComponent::class)
object BaseActivityModule {

     @Provides
     fun provideBaseActivity(activity :Activity): BaseSDKActivity {
        check(activity is BaseSDKActivity) { "Every Activity is expected to extend BaseActivity" }
        return activity as BaseSDKActivity
    }

}

BaseSDKActivity

open class BaseSDKActivity @Inject constructor() : AppCompatActivity() {
}

BaseSDKPresenter

@ActivityScoped
open class BaseSDKPresenter @Inject constructor(private val baseSDKActivity: BaseSDKActivity , private val ezeApi: EzeApi ,
                                                private val context: Context )
Related