Reputation: 3748
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
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
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
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
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
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