lateinit property dispatchingAndroidInjector has not been initialized

I receive the above error "lateinit property dispatchingAndroidInjector has not been initialized " when I run my fragment in dagger2 .
The above error is triggered in my application class which is below

KotlinTemplateApplication.kt

     class KotlinTemplateApplication:Application(), HasActivityInjector  {
        lateinit var retroComponent:RetroComponent

@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

companion object {
    @get:Synchronized
    lateinit var instance: KotlinTemplateApplication
     private set
}

override fun onCreate() {
    super.onCreate()
    instance = this
    retroComponent = DaggerRetroComponent.builder().retroModule(RetroModule(APIURL.BASE_URL)).build()
    //retroComponent.inject()

}



   fun fetchRetroComponent():RetroComponent{
   return retroComponent
   }

override fun activityInjector(): AndroidInjector<Activity> {
    return dispatchingAndroidInjector
}
   }


My Fragment class is as below :
I called dagger related code in onAttach() method of fragment :

RetroDIFragment:

      class RetroDIFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
lateinit var retroDIListViewModel: RetroDIListViewModel
lateinit var retroFitDIView: View
@Inject
lateinit var apiService: APIService

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    arguments?.let {
        param1 = it.getString(ARG_PARAM1)
        param2 = it.getString(ARG_PARAM2)
    }
    retroDIListViewModel = ViewModelProviders.of(activity!!).get(RetroDIListViewModel::class.java)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    // Inflate the layout for this fragment
    retroFitDIView =  inflater.inflate(R.layout.fragment_retro_di, container, false)

    return retroFitDIView
}

override fun onAttach(context: Context?) {
    super.onAttach(context)
    AndroidInjection.inject(activity)
    KotlinTemplateApplication.instance.fetchRetroComponent().inject(this@RetroDIFragment)

    retroDIListViewModel.fetchPostsFromWebSevice(apiService).observe(this,object : Observer<List<RetroModel>>{
        override fun onChanged(t: List<RetroModel>?) {
            for (i in t!!.indices)
                println(t[i].id)
        }

    })
}
companion object {
    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment RetroDIFragment.
     */
    // TODO: Rename and change types and number of parameters
    @JvmStatic
    fun newInstance(param1: String, param2: String) =
            RetroDIFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
}
    }


My component is as below :

RetroComponent :

 @Singleton
@Component(modules = arrayOf(RetroModule::class))
  interface RetroComponent {
    fun inject(retroDIFragment: RetroDIFragment)
   }


My Module is as below:

 @Module
   public class RetroModule(var urlPath:String) {


init{
    this.urlPath  = urlPath
}

@Singleton
@Provides
fun provideServiceAPI(retrofit: Retrofit):APIService{
    return retrofit.create(APIService::class.java)
}

@Singleton
@Provides
fun provideRetrofit():Retrofit{
    val retrofit = Retrofit.Builder()
            .baseUrl(urlPath)
            .addConverterFactory(ScalarsConverterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .client(providesOkHttpClientBuilder())
            .build()
    return  retrofit

}


private fun providesOkHttpClientBuilder(): OkHttpClient {

    val httpClient = OkHttpClient.Builder()
    return httpClient.readTimeout(1200, TimeUnit.SECONDS)
            .connectTimeout(1200, TimeUnit.SECONDS).build()

}
}


My Activity is as below

class RetroFitActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_retro_fit)
    supportFragmentManager.beginTransaction().replace(R.id.container_retro_di, RetroDIFragment()).commit()
}
   }


I included below code in my Gradle:

 implementation 'com.google.dagger:dagger:2.19'
implementation 'com.google.dagger:dagger-android:2.19'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.19'
annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
kapt 'com.google.dagger:dagger-android-processor:2.19'
kapt 'com.google.dagger:dagger-compiler:2.19'
//moxy
compile 'com.arello-mobile:moxy-app-compat:1.1.1'
kapt 'com.arello-mobile:moxy-compiler:1.1.1'




Can anyone help me in fixing this issue.

Upvotes: 6

Views: 8124

Answers (5)

Sam
Sam

Reputation: 6395

I had the same issues with Dagger2.26v, to fix , make sure ApplicationComponent extends AndroidInjector<SampleApplication> like this:

@Singleton
@Component(modules = AndroidInjectionModule::class)
interface ApplicationComponent : AndroidInjector<SampleApplication> { …}

Upvotes: 1

Abhishek Kumar
Abhishek Kumar

Reputation: 4808

this might be too late but this worked for me...

@set:Inject
internal var activityDispatchingAndroidInjector:DispatchingAndroidInjector<Activity>? = null

remove lateinit and use internal with @set:Inject instead of @Inject

this worked like charm for me.

Upvotes: 1

nayan dhabarde
nayan dhabarde

Reputation: 2346

You need to change the AppComponent class inject method parameter:

@Singleton
@Component(modules = [
        AndroidInjectionModule::class,
        ActivityModule::class,
        AppModule::class
])
interface AppComponent {

        @Component.Builder
        interface Builder {
                @BindsInstance
                fun application(application: Application): Builder

                fun build(): AppComponent
        }

        fun inject(app: Application) // This is the piece of code you need to change
}
@Singleton
@Component(modules = [
        AndroidInjectionModule::class,
        ActivityModule::class,
        AppModule::class
])
interface AppComponent {

        @Component.Builder
        interface Builder {
                @BindsInstance
                fun application(application: Application): Builder

                fun build(): AppComponent
        }

        fun inject(app: YourCustomAppClass) // Change to your custom app class
}

Also, where you are doing this

  DaggerAppComponent
            .builder()
            .application(yourAppInstance)
            .build()
            .inject(yourAppInstance)

yourAppInstance needs to be of type YourCustomApp class instead of Application.

Upvotes: 6

GRimmjow
GRimmjow

Reputation: 98

To use Dagger in a Fragment, you must add a DispatchingAndroidInjector <Fragment> in KotlinTemplateApplication

Edit KotlinTemplateApplication

class KotlinTemplateApplication : Application() , HasActivityInjector, HasSupportFragmentInjector {


    @Inject lateinit var activityInjector: DispatchingAndroidInjector<Activity>


    @Inject lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>



    override fun onCreate() {
        super.onCreate()

                ........

        DaggerAppComponent.builder().application(this).build().inject(this)

                .........
    }




    override fun activityInjector(): AndroidInjector<Activity> = activityInjector

    override fun supportFragmentInjector(): AndroidInjector<Fragment> = fragmentInjector

}

You can also create a special module for Fragments and then add it to interface RetroComponent

@Component(modules = arrayOf(RetroModule::class,FragmentModule::class)

@Module
abstract class FragmentModule {
    @ContributesAndroidInjector
    internal abstract fun contributeRetroDIFragment(): RetroDIFragment
}

Then in your Fragment RetroDIFragment

class RetroDIFragment : Fragment() {
    ......

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        retroFitDIView =  inflater.inflate(R.layout.fragment_retro_di, container, false)

        return retroFitDIView
    }

   .........

    /*---------------- Dagger Injection for Fragment -------------*/
    @Override
    override fun onAttach(context: Context?) {
        AndroidSupportInjection.inject(this)
        super.onAttach(context);
    }

}

Upvotes: 1

tynn
tynn

Reputation: 39853

The dispatchingAndroidInjector property has to be set eventually.

@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

As it's annotated with @Inject, it seems like you wanted to inject it. But since KotlinTemplateApplication is an Application, you need to do this manually on your component:

retroComponent.inject(this@KotlinTemplateApplication)

Upvotes: 1

Related Questions