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
}
}
Related
Trying to create a view model in a fragment by providing the factory with a dagger but app crashes with error. Move injection to another lifecycle doesn't work
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.bigproject.planyourlife, PID: 13934
kotlin.UninitializedPropertyAccessException: lateinit property factory has not been initialized
at com.bigproject.planyourlife.view.SignupFragment.getFactory(SignupFragment.kt:27)
ViewModel
class SignupViewModel(private val service: AuthService) : ViewModel() {
private var email: String = ""
private var password: String = ""
private val _uiState = MutableStateFlow<SignupState>(SignupState.Success(null))
val uiState: StateFlow<SignupState> = _uiState
#Suppress("UNCHECKED_CAST")
class Factory #Inject constructor(
private val service: AuthService
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
require(modelClass == SignupViewModel::class)
return SignupViewModel(service) as T
}
}
Fragment
class SignupFragment : Fragment(R.layout.signup_page) {
private val binding by viewBinding(SignupPageBinding::bind)
private val signupViewModel: SignupViewModel by viewModels {
factory
}
#Inject
lateinit var factory: SignupViewModel.Factory
override fun onAttach(context: Context) {
context.appComponent.inject(this)
super.onAttach(context)
}
}
First, create a module for your ViewModelFactory. This is important for you to provide ViewModelFactory for dagger.
ViewModelModule.kt
#Module
abstract class ViewModelModule {
#Binds
abstract fun provideViewModelFactory(factory: Factory): ViewModelProvider.Factory
// Binds your viewmodel in here.
}
Next, you need to include that module in your component. Specifically here is AppComponent.
AppComponent.kt
#Singleton
#Component(modules = [ViewModelModule::class, YourFragmentModule::class, YourActivityModule::class, ...])
interface AppComponent {
#Component.Factory
abstract class Factory : AndroidInjector.Factory<YourApplication>
}
And now, viewModelFactory is ready for you to inject into your Fragment, Activity, ....
YourFragment.kt
class YourFragment: Fragment() {
#Inject
lateinit var viewModelFactory: Factory
}
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 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
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()
}
}