I am trying to inject Context using Dagger 2. I have seen many other questions on this website related to this but still problem is not solved.
AppComponent.kt:
#Singleton
#Component(
modules = [
AppModule::class
]
)
interface AppComponent {
fun context(): Context
fun inject(context: Context)
}
AppModule.kt:
#Module
class AppModule(private val context: Context) {
#Provides
#Singleton
fun providesApplicationContext(): Context = context
}
MainApp.kt:
class MainApp : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = initDagger()
appComponent.inject(this)
}
private fun initDagger() = DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
}
Manager.kt: (Class where I want to inject Context)
class Manager {
#Inject
lateinit var context: Context
fun foo() {
context.resources
}
}
However, I am getting following error at context.resources when Manager().foo() is called from anywhere, say in onCreate() function of MainActivity:
kotlin.UninitializedPropertyAccessException: lateinit property context has not been initialized
How to fix this? Why is Dagger not injecting Context?
Try to use constructor injection
class Manager #Inject constructor(val context: Context) {
fun foo() {
context.resources
}
}
And then in your Activity/Fragment use manager like below:
#Inject lateinit var manager: Manager
Related
I have android gradle multi-module dagger application.
Inside each module I'm doing inject via it's own component like this:
MainApp.appComponent.inject(this) for module :app and
BaseApp.appComponent.inject(this) for module :app:base.
Now trying to make SharedPreferences wrapper available within app:base module. Until this point everything worked fine, but after this I faced this error
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.alazar.tracker/com.alazar.tracker.MainActivity}: kotlin.UninitializedPropertyAccessException: lateinit property appComponent has not been initialized
Application.name property specified at Manifest.xml of the :app:base module.
What am I doing wrong and how to fix that? Thanks for any suggestion.
MainActivity:
class MainActivity : BaseActivity(), View.OnClickListener {
#Inject
lateinit var userManager: UserManagerInterface
#Inject
lateinit var preferences: PreferenceProvider
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MainApp.appComponent.inject(this)
binding = ActivityMainBinding.inflate(layoutInflater)
binding.btnSignOut.setOnClickListener(this)
binding.btnStart.setOnClickListener(this)
binding.btnStop.setOnClickListener(this)
if (!userManager.isAuthenticated()) {
openPostActivity.launch(Intent(this, AuthActivity::class.java))
} else {
setContentView(binding.root)
changeStatus(preferences.getServiceStatus())
}
}
}
MainApp:
#MainScope
#Component(
dependencies = [
],
modules = [
MainModule::class,
]
)
interface MainAppComponent {
fun inject(activity: MainActivity)
fun inject(activity: MapActivity)
}
#Module(
includes = [
AuthUserModule::class,
BaseModule::class,
]
)
class MainModule
class MainApp : Application() {
override fun onCreate() {
super.onCreate()
appComponent = DaggerMainAppComponent
.builder()
.build()
}
companion object {
lateinit var appComponent: MainAppComponent
}
}
BaseApp:
#Singleton
#Component(
modules = [
BaseModule::class,
]
)
interface BaseComponent {
fun inject(baseApp: BaseApp)
fun inject(wrapper: SharedPrefWrapper)
}
#Module
class BaseModule constructor(private val application: Application) {
#Provides
fun provideContext() : Context = application.applicationContext
#Provides
fun provideSharedPreferences() : PreferenceProvider = SharedPrefWrapper(application.applicationContext)
}
class BaseApp : Application() {
override fun onCreate() {
super.onCreate()
appComponent = DaggerBaseComponent
.builder()
.baseModule(BaseModule(this))
.build()
}
companion object {
lateinit var appComponent: BaseComponent
}
}
SharedPreferences wrapper
class SharedPrefWrapper #Inject constructor(private val context: Context) : PreferenceProvider {
init {
BaseApp.appComponent.inject(this)
Log.d("************ CONTEXT", context.toString())
}
private var preferences: SharedPreferences = context.getSharedPreferences(
context.getString(R.string.shared_preference_name),
AppCompatActivity.MODE_PRIVATE
)
override fun saveServiceStatus(status: Boolean) {
val editor = preferences.edit()
editor.putBoolean(context.getString(R.string.preference_service_param), status)
editor.apply()
}
override fun getServiceStatus(): Boolean {
return preferences.getBoolean(context.getString(R.string.preference_service_param), false)
}
}
#Module
#InstallIn(ApplicationComponent::class)
object AppDataModule {
#Provides
#Singleton
fun provideAU(loginPreferences: LoginPreferences, #ApplicationContext context: Context): AccountUtil = AccountUtil (loginPreferences, context)
}
class SomeClass {
#Inject
lateinit var accountUtil: AccountUtil
constructor(context:Context){}
constructor(context:Context, obj:SomeClass1){}
init {
accountUtil.isLoggedIn()
}
}
I got
kotlin.UninitializedPropertyAccessException: lateinit property accountUtil has not been initialized
How can i resolve this one? is i need to use custom component in Hilt?
Make sure you have your application as a #HiltApplication
#HiltAndroidApp class Application: Application()
Then once you've created a module like so
#Module
#InstallIn(ApplicationComponent::class)
object AppDataModule {
#Provides
#Singleton
fun provideAU(loginPreferences: LoginPreferences, #ApplicationContext context: Context): AccountUtil = AccountUtil (loginPreferences, context)
}
Make sure the class you're wanting to inject is injected in the constructor
class SomeClass #Inject constructor(private var accountUtil: AccountUtil) {
init { accountUtil.isLoggedIn() }
}
class SomeClass #Inject constructor(){
#Inject constructor(private var accountUtil: AccountUtil)
#Inject constructor(private var util1: AccountUtil, private var util2: AccountUtil)
init { accountUtil.isLoggedIn() }
}
I try to use Dagger2 to my project. I have a Firebase service and a class called SyncFactory that makes a specific request. When i get a call from Firebase i make my request.
I have created a Mangers Module
#Module(includes = [RepositoryModule::class, NetworkModule::class, AppModule::class])
class ManagersModule {
...
#Singleton
#Provides
fun provideSyncFactory(context: Context, accountsRepository: AccountsRepository, messagesRepository: MessagesRepository) : SyncFactory {
return SyncFactory(context, accountsRepository, messagesRepository)
}
...
}
The SyncFactory class is like below
class SyncFactory #Inject constructor(
private val context: Context,
private val accountsRepository: AccountsRepository,
private val messagesRepository: MessagesRepository
) {
fun getAccounts(){....}
and i also have an interface
#Singleton
#Component(modules = [ViewModelsModule::class, DatabaseModule::class, RepositoryModule::class, AppModule::class, NetworkModule::class, ManagersModule::class])
interface ViewModelComponent {
fun inject(viewModels: ViewModels)
fun inject(firebaseService: AppFirebase)
}
And finally inside my firebase service i Inject the SyncFactory
class AppFirebase : FirebaseMessagingService(), SyncFactoryCallback {
#Inject
lateinit var syncFactory: SyncFactory
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
// lateinit property syncFactory has not been initialized
syncFactory.getAccounts()
}
And when my service gets called i get a lateinit property syncFactory has not been initialized exception.
What do i do wrong..?
The solution is to implement a HasServiceInjector in your Application class
class MyApplication : Application(), HasServiceInjector {
#Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Service>
companion object {
private lateinit var instance: MyApplication
}
override fun serviceInjector(): AndroidInjector<Service> {
return dispatchingAndroidInjector
}
}
I'm using Dagger2 to inject class dependent like bellow.
This is a component for Dagger2, AppComponent.kt:
#Component(modules = [ContextModule::class, SuggestModule::class, RetrofitModule::class,
TranslateModule::class, DatabaseModule::class, ViewModelModule::class, FragmentModule::class])
interface AppComponent {
#Singleton
fun inject(fragment: TranslateFragment)
#Singleton
fun inject(fragment: FavouriteFragment)
#Singleton fun inject(fragment: TensesFragment)
#Singleton
fun inject(activity: TensesActivity)
#Singleton
fun inject(activity: MainActivity)
#Singleton
fun inject(translateViewModel: TranslateViewModel)
#Singleton
fun inject(favouriteViewModel: FavouriteViewModel)
#Singleton
fun inject(translateProvider: TranslateProvider)
}
This is App class extended Application class, where i built my component , App.kt
class App : Application() {
companion object{
lateinit var appComponent: AppComponent
}
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.builder()
.contextModule(ContextModule(this))
.suggestModule(SuggestModule(this))
.retrofitModule(RetrofitModule())
.translateModule(TranslateModule(TranslateProvider()))
.databaseModule(DatabaseModule(DatabaseManager(this)))
.viewModelModule(ViewModelModule())
.fragmentModule(FragmentModule())
.build()
}
}
First, i injected TranslateFragment into MainActivity, MainActivity.kt
class MainActivity : AppCompatActivity {
constructor(){
App.appComponent.inject(this)
}
#Inject
lateinit var translateFragment: TranslateFragment
}
Second, i injected TranslateViewModel into TranslateFragment, TranslateFragment.kt
class TranslateFragment : Fragment {
#Inject
constructor() {
App.appComponent.inject(this)
}
#Inject
lateinit var translateViewModel: TranslateViewModel
}
Third, i injected TranslateProvider into TranslateViewModel, TranslateViewModel.kt
class TranslateViewModel : BaseObservable {
#Inject
constructor() {
App.appComponent.inject(this)
}
#Inject
lateinit var translateProvider: TranslateProvider
}
End, i injected RetrofitProvider into TranslateProvider, TranslateProvider.kt
class TranslateProvider {
#Inject
constructor() {
App.appComponent.inject(this)
}
#Inject
lateinit var retrofitProvider: RetrofitProvider
}
But i received a error at TranslateProvider.kt:
kotlin.UninitializedPropertyAccessException: lateinit property
appComponent has not been initialized
I'm not understand, please help me.
Thanks!
I created a instance of TranslateProvider in: .translateModule(TranslateModule(TranslateProvider()))
When constructor of TranslateProvider called appComponent, but appComponent was not initialized that time.
Just move it go to out of TranslateModule constructor look like:
Before:
#Module
class TranslateModule(private val translateProvider: TranslateProvider) {
#Provides
fun getTranslateProvider(): TranslateProvider {
return translateProvider
}
}
After:
#Module
class TranslateModule {
#Provides
fun getTranslateProvider(): TranslateProvider {
return TranslateProvider()
}
}
I'm a Dagger newb and have a trouble with using it.
What I want to develop is that using RxAndroidBle and to initialize it by Dagger for providing Context.
So I researched how it can be implemented, and I wrote some codes and It seems to be working for me but not working at all.
The followings are my codes.
AppComponent.kt
#Singleton
#Component(modules = [
AppModule::class,
BluetoothModule::class,
AndroidInjectionModule::class])
interface AppComponent : AndroidInjector<BluetoothController> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
}
AppModule.kt
#Module
class AppModule {
#Provides
#Named("appContext")
#Singleton
fun provideContext(application: Application): Context =
application.applicationContext
}
BluetoothModule.kt
#Module
class BluetoothModule {
#Provides
#Named("rxBleClient")
#Singleton
fun provideRxBleClient(#Named("appContext") context: Context):RxBleClient =
RxBleClient.create(context)
}
BluetoothController.kt for injecting by DaggerApplication.
class BluetoothController : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().application(this).build()
}
}
I've inserted
android:name".BluetoothController"
to AndroidManifest.xml
And this is how I would use it.
#field:[Inject Named("rxBleClient")]
lateinit var rxBleClient: RxBleClient
But it always occurs an error says: lateinit property context has not been initialized
What things I've missed? Can anyone help me?
Thanks in advance.
Add the below code to make this happen.
Create ActivityBuilderModule for injecting within the activity. Consider our activity as MainActivity
#Module
abstract class ActivityBuilderModule {
#ContributesAndroidInjector(modules=[MainActivityModule::class])
abstract fun contributeSplashActivity(): MainActivity
}
Create your MainActivityModule
#Module
class MainActivityModule{
#Provides()
fun contributeSplashActivity(mainActivity: MainActivity):
MainActivity=mainActivity
}
Modify your component.
#Singleton
#Component(modules = [
AppModule::class,
BluetoothModule::class,
ActivityBuilderModule::class,
AndroidInjectionModule::class])
interface AppComponent : AndroidInjector<BluetoothController> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
}
Within MainActivity just inject.
class MainActivity{
...
#Inject
lateinit var rxBleClient: RxBleClient
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
}
Let us know in case of any issue.
the context object is not initialized before its called
Though i dont know, how chained your initialization are.
Use #Inject to add deppendencies, do something like this
#Module
class BluetoothModule(val context : Context) {
//#Inject private lateinit var context : Context
#Provides
#Named("rxBleClient")
#Singleton
fun provideRxBleClient():RxBleClient =
RxBleClient.create(context)
}
let your call be like this
val component = AppComponent()
component.bluetoothModule(appContext)
.//other calls here
.build()