Hello i am using dagger 2 in my project with this configuration.
This is my SellerHubApplication.kt
class SellerHubApplication : Application(), HasAndroidInjector {
#Inject
lateinit var activityDispatchingAndroidInjector: AndroidInjector<Any>
override fun androidInjector(): AndroidInjector<Any> = activityDispatchingAndroidInjector
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder()
.application(this)
.build()
.inject(this)
Fresco.initialize(applicationContext)
}
}
and this is my AppComponent.kt
#Singleton
#Component(modules = [AndroidInjectionModule::class, ActivityBuilder::class, AppModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
fun inject(app: SellerHubApplication)
}
After i run the application, i get this error "error: [Dagger/MissingBinding] dagger.android.AndroidInjector cannot be provided without an #Provides-annotated method."
if i change the "fun inject(app: SellerHubApplication)" to "fun inject(app: Application)", application runs but it says "activityDispatchingAndroidInjector has not been initialized". Please help me with this.
Try to do it as such:
AppComponent:
#Singleton
#Component(modules = [AndroidInjectionModule::class, ActivityBuilder::class, AppModule::class, FragmentBuilder::class,])
interface AppComponent : AndroidInjector< SellerHubApplication > {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: SellerHubApplication): Builder
fun applicationModule(applicationModule: AppModule): Builder
fun build(): AppComponent
}
}
Application:
class SellerHubApplication : Application(), HasActivityInjector, HasSupportFragmentInjector {
#Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
#Inject
lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>
companion object {
lateinit var applicationComponent: AppComponent
private set
}
override fun onCreate() {
super.onCreate()
init()
}
override fun activityInjector(): AndroidInjector<Activity> {
return dispatchingAndroidInjector
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentInjector
}
private fun init() {
initDagger()
}
private fun initDagger() {
applicationComponent = DaggerAppComponent
.builder()
.application(this)
.applicationModule(AppModule(this))
.build()
applicationComponent.inject(this)
}
}
AppModule:
#Module
class AppModule(private val application: SellerHubApplication) {
#Provides
#Singleton
fun provideApplication(): Application {
return application
}
}
Related
I am trying to implement the latest version of Dagger2 in a Single-Activity app, but it is not known why, when initializing my starting activity, Dagger2 does not inject dependencies, I has a
fatal error in my base activity : Unable to resume activity kotlin.UninitializedPropertyAccessException: lateinit property navigatorHolder has not been initialized
here is my code
AppComponent:
#Singleton
#Component(modules = [
AndroidSupportInjectionModule::class,
ActivityInjectionModule::class,
ActivityProviderModule::class,
AndroidInjectionModule::class,
NetworkModule::class,
RemoteModule::class,
NavigationModule::class,
ParserModule::class,
CacheModule::class])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
#BindsInstance
fun context(context: Context) : Builder
fun build(): AppComponent
}
override fun inject(app: App)
}
App:
class App : DaggerApplication(){
private val applicationInjector =
DaggerAppComponent.builder().application(this).context(this).build()
override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
applicationInjector
companion object {
lateinit var cicerone: Cicerone<Router>
private set
}
override fun onCreate() {
super.onCreate()
cicerone = Cicerone.create()
initAppComponent()
initStetho()
Timber.plant(Timber.DebugTree())
}
AppActivity:
class AppActivity : MvpAppCompatActivity() , HasAndroidInjector, RouterProvider {
#Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
#Inject
lateinit var mainActivityProvider: ActivityProvider
#Inject
lateinit var navigatorHolder: Lazy<NavigatorHolder>
#Inject
override lateinit var ciceroneRouter: Router
override fun androidInjector(): AndroidInjector<Any> = dispatchingAndroidInjector
private val navigator : Navigator by lazy {
CustomSupportAppNavigator(this, supportFragmentManager, R.layout.activity_main)
}
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
AndroidInjection.inject(this)
mainActivityProvider.acitvity = this
Timber.e("onCreate AppActivity")
super.onCreate(savedInstanceState, persistentState)
setContentView(R.layout.activity_main)
initBottomBar()
}
override fun onResumeFragments() {
super.onResumeFragments()
navigatorHolder.get().setNavigator(navigator)
}
ActivityInjectionModule
#Module(includes = [AndroidInjectionModule::class])
interface ActivityInjectionModule {
#ContributesAndroidInjector(
modules = [FragmentInjectionModule::class]
)
fun activityInjector() : AppActivity
}
NavigationModule
#Module
class NavigationModule {
#Provides
#Singleton
fun provideRouter() = App.cicerone.router
#Provides
#Singleton
fun provideNavigatorHolder() : NavigatorHolder {
return App.cicerone.navigatorHolder
}
#Provides
#Singleton
fun provideLocalNavigationHolder(): LocalCiceroneHolder {
return LocalCiceroneHolder()
}
}
Also i post android:name=".App" inandroid manifest.
I have tried many different options already, but I still cannot find the reason
Can you change your App to like this and try again:
class App : DaggerApplication(){
private val appComponent =
DaggerAppComponent.builder().application(this).context(this).build().inject(this)
companion object {
lateinit var cicerone: Cicerone<Router>
private set
}
override fun onCreate() {
super.onCreate()
cicerone = Cicerone.create()
initAppComponent()
initStetho()
Timber.plant(Timber.DebugTree())
}
Also remove AndroidInjector<App> from AppComponent
I'm learning dagger2 with a module architecture. And I think, something is not clear to me, for example
in module utilites i have di package
class UtilsComponent
#Component(modules = [UtilsModule::class])
interface UtilsComponent {
fun getResourceProvider() : IResourceProvider
fun getNetworkProvider(): INetworkProvider
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): UtilsComponent.Builder
fun build(): UtilsComponent
}
}
#Module
abstract class UtilsModule {
#Binds
abstract fun bindContext(application: Application): Context
#Module
companion object {
#Provides
#JvmStatic
fun bindResourceProvider(context: Context): IResourceProvider {
return ResourceProvider(context = context)
}
#Provides
#JvmStatic
fun bindNetworkProvider(context: Context): INetworkProvider {
return NetworkProvider(context = context)
}
}
}
then in app package in AppComponent i included all modules
#Component(
dependencies = [UtilsComponent::class],
modules = [
AndroidInjectionModule::class,
ActivityBindingModule::class,
MainModule::class // test module
]
)
#AppScope
interface AppComponent: AndroidInjector<App> {
// inject to ...
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): AppComponent.Builder
fun utilsComponent(utilsComponent: UtilsComponent): AppComponent.Builder
fun build(): AppComponent
}
}
In the app component, I have MainModule - this is my test module, so this module looks like this
#Module
class MainModule {
#Provides
fun getMainPresenter(networkProvider: NetworkProvider): MainPresenter {
return MainPresenter(networkProvider)
}
}
when I run the app, I have an error
[Dagger/MissingBinding] com.example.utilities.di.UtilsModule cannot be
provided without an #Provides-annotated method. public abstract
interface AppComponent extends
dagger.android.AndroidInjector<com.example.testmoduleapp.App> {
^
com.example.utilities.di.UtilsModule is injected at
com.example.testmoduleapp.di.modules.MainModule.getMainPresenter(utilsModule)
com.example.testmoduleapp.ui.activities.main.MainPresenter is injected at
com.example.testmoduleapp.ui.activities.main.MainActivity.mainPresenter
com.example.testmoduleapp.ui.activities.main.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.example.testmoduleapp.di.AppComponent →
com.example.testmoduleapp.di.modules.ActivityBindingModule_MainActivity.MainActivitySubcomponent]
I understand that the error is because I have not a module witch return NetworkProvider object, but I can't understand how I can get this object from UtilsModule
also in App
#Inject
lateinit var androidInjector : DispatchingAndroidInjector<Any>
override fun androidInjector(): AndroidInjector<Any> = androidInjector
companion object{
lateinit var appComponent: AppComponent
}
override fun onCreate() {
super.onCreate()
initializeDagger()
}
private fun initializeDagger() {
appComponent = DaggerAppComponent
.builder()
.application(this)
.utilsComponent(provideUtilsComponent())
.build()
}
private fun provideUtilsComponent(): UtilsComponent {
return DaggerUtilsComponent
.builder()
.application(this)
.build()
}
In Dependencies Graph You already provide INetworkProvider so when you need NetworkProvider it means Dagger can not be resolved. Change to this, don't forget to change constructor MainPresenter to INetworkProvider
#Provides
fun getMainPresenter(networkProvider: INetworkProvider): MainPresenter {
return MainPresenter(networkProvider)
}
but i cant understand how i can get this object from UtilsModule
For your question, in UtilComponent you already exposed getNetworkProvider() it means any Componenent dependencies to UtilsComponent can be get it.
In the following code, the field serviceUtil is not being injected by Dagger:
AppController.kt
class App : Application() {
#Inject
lateinit var serviceUtil: ServiceUtil
init {
DaggerAppComponent
.builder()
.build()
.inject(this)
}
override fun onCreate() {
super.onCreate()
context = this
}
fun startService() {
serviceUtil.startService()
}
companion object {
lateinit var context: App
}
}
AppComponent.kt
#Singleton
#Component(modules = [(ServiceUtilModule::class)])
interface AppComponent {
fun inject(app: Application)
}
ServiceUtilModule.kt
#Module
class ServiceUtilModule {
#Provides
fun provideServiceUtil() : ServiceUtil {
return ServiceUtil()
}
}
From my main activity I call:
App.context.startService()
You mistyped here
#Singleton
#Component(modules = [(ServiceUtilModule::class)])
interface AppComponent {
fun inject(app: Application)
}
You should pass you App class as argument, not the basic one.
#Singleton
#Component(modules = [(ServiceUtilModule::class)])
interface AppComponent {
fun inject(app: App)
}
Hi is it possible to add test modules in my AppComponent?
Below is my real representation of my appComponent
#Singleton
#Component(modules = arrayOf(MainModule::class,
AnalyticsModule::class,
MainAndroidBinding::class,
AccountAndroidBinding::class,
AndroidSupportInjectionModule::class,
HomeAndroidBinding::class,
NetworkModule::class))
interface ApplicationComponent : AndroidInjector<DaggerApplication> {
fun inject(myApplication: MyApplication)
override fun inject(instance: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(applicaton: Application): Builder
fun build(): ApplicationComponent
}
}
I could just add the test modules directly to the testAppComponent like this but it doesnt offer me much flexibility to dynamically add different testModules.
#Singleton
#Component(modules = [
(MainModuleTest::class),
(TestMainAndroidBindingTest::class),
(AccountAndroidBindingTest::class),
(AnalyticsModuleTest::class),
(AndroidSupportInjectionModule::class),
(NetworkModuleTest::class)])
interface TestAppComponent : ApplicationComponent {
fun inject(launchActivityTest: LaunchActivityTest)
#Component.Builder
interface Builder {
#BindsInstance
fun application(applicaton: Application): Builder
fun build(): TestAppComponent
}
}
Here is my MyApplication class
class MyApplication : DaggerApplication() {
companion object {
private lateinit var application: MyApplication
fun get(): MyApplication {
return application
}
}
#Inject
lateinit var dispatchingActivityInjector: DispatchingAndroidInjector<Activity>
lateinit var applicationComponent: ApplicationComponent
override fun onCreate() {
super.onCreate()
application = this
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
applicationComponent = DaggerApplicationComponent.builder()
.application(this)
.build()
applicationComponent.inject(this)
return applicationComponent
}
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(this)
}
}
On the LaunchActivityTest this is how i set it up to use this testApp component
#Before
fun setUp() {
val app = InstrumentationRegistry.getTargetContext().applicationContext as MyApplication
val testAppComponent = DaggerTestAppComponent.builder().application(app).build()
app.applicationComponent = testAppComponent
testAppComponent.inject(this)
}
I was following this guide until i stumbled o the point where my DaggerTestAppComponent doesnt expose the modules for me to dynamically add myself due to the fact that my AppComponent class extends AndroidInjector which automatically adds the modules for you
https://proandroiddev.com/writing-espresso-instrumentation-tests-with-dagger2-kotlin-d30f12c4769b
The above dynamically adds its modules like this:
testAppComponent = DaggerTestAppComponent.builder()
.appModule(AppModule(app))
.apiModule(TestApiModule())
.prefModule(TestPrefModule())
.build()
I cant do that in my case unless i redo my AppComponent so that it doesnt extend AndroidInjector. If i do that then in my real impl code i have to manually set the modules.
Is there any other way?
You should add a function to your component builder and use "BindsInstance".
Example component
#Singleton
#Component(modules = {
AndroidSupportInjectionModule.class,
ApplicationTestModule.class,
ActivityBuilder.class})
public interface TestExampleComponent extends AndroidInjector<DaggerApplication> {
void inject(TestApplication app);
#Override
void inject(DaggerApplication instance);
#Component.Builder
interface Builder {
#BindsInstance
TestExampleComponent.Builder application(DaggerApplication application);
Builder applicationModule(ApplicationTestModule appTestModule);
TestExampleComponent build();
}
}
In this component, I added applicationModule function with using "BindsInstance" and I can pass ApplicationTestModule.
Then you can add different test modules.
TestApplicationComponent appComponent = DaggerTestAppComponent.builder().application(this).
applicationModule(appTestModule).build();
appComponent.inject(this);
I'm trying to implement mvp pattern with dagger 2 support in my app
Here's the objects:
class BaseApplication : Application(), HasActivityInjector
{
override fun onCreate()
{
super.onCreate()
initDi()
}
private fun initDi(){
DaggerAppComponent.builder().application(this).build().inject(this)
}
#Inject lateinit var activityInjector: DispatchingAndroidInjector<Activity>
override fun activityInjector(): AndroidInjector<Activity>
{
return activityInjector
}
}
#Singleton
#Component(modules = arrayOf(AndroidInjectionModule::class, AppModule::class, ActivityBuilder::class))
interface AppComponent
{
#Component.Builder
interface Builder
{
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: BaseApplication)
}
#Module
class AppModule
{
#Provides
#Singleton
internal fun provideContext(application: Application): Context
{
return application
}
}
#Module
abstract class ActivityBuilder
{
#ContributesAndroidInjector(modules = arrayOf(LoginFragmentProvider::class))
internal abstract fun bindAuthenticationActivity(): AuthenticationActivity
}
#Module
public abstract class LoginFragmentProvider
{
#ContributesAndroidInjector
abstract LoginFragment provideLoginFragmentFactory();
}
class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector
{
#Inject lateinit var androidInjector: DispatchingAndroidInjector<Fragment>
override fun supportFragmentInjector(): AndroidInjector<Fragment>
{
return androidInjector
}
}
class LoginFragment : Fragment() {
override fun onAttach(context: Context?)
{
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
The problem is, when login fragment calls AndroidSupportInjection.inject(this), The AuthenticationActivity supportFragmentInjector get called, but the androidInjector is still null
As a result, I'm getting the exception:
java.lang.RuntimeException: Unable to start activity .....AuthenticationActivity}: kotlin.UninitializedPropertyAccessException: lateinit property androidInjector has not been initialized
I'm not sure how to fix this
Thanks in advance
I think you forget to inject your AuthenticationActivity. You should call AndroidInjection in onCreate.
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
Edit: You can check my example repo for more information. https://github.com/savepopulation/dc-tracker