How to inject a LifecycleOwner in Android using Dagger2? - android

I happen to have an Android lifecycle aware component with the following interface:
class MyLifecycleAwareComponent #Inject constructor(
private val: DependencyOne,
private val: DependencyTwo
) {
fun bindToLifecycleOwner(lifecycleOwner: LifecycleOwner) {
...
}
...
}
All Dagger specific components and modules are configured correctly and have been working great so far.
In each activity when I need to use the component I do the following:
class MyActivity: AppCompatActivity() {
#Inject
lateinit var component: MyLifecycleAwareComponent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
component.bindToLifecycleOwner(this)
...
}
}
Now I want to get rid of bindLifecycleOwner and denote my component like this:
class MyLifecycleAwareComponent #Inject constructor(
private val: DependencyOne,
private val: DependencyTwo,
private val: LifecycleOwner
) {
...
}
And provide the lifecycleOwner within the scope of individual activities (which implement the interface by extending AppCompatActivity).
Is there any way to do it with Dagger?

You may bind your Activity to LifecycleOwner from your ActivityModule:
#Module
abstract class ActivityModule {
...
#Binds
#ActivityScope
abstract fun bindLifecycleOwner(activity: AppCompatActivity): LifecycleOwner
...
}

Related

Dagger Hilt: Inject interface that inherits another interface

I have an app that implements MVP pattern and I want to simplify boilerplate code with Hilt. So far so good. The problem comes when I want to Inject a presenter that takes as a paramter a interface implementation of MyView, but I want to pass a implementation of an interface that inherits MyView because the presenter may be injected in diferent fragments with diferent MyView implementations
My presenter:
class BluetoothPresenter #Inject constructor(
#ApplicationContext private val context: Context,
private val view: MyView
) {
The presenter is injected in the Fragment:
class DevicesListFragment() : Fragment(), DevicesListMyView {
#Inject
lateinit var btPresenter: BluetoothPresenter
DevicesListView is a interface that inherits MyView interface
interface DevicesListView : MyView {
fun onSearchDevicesStarted();
fun searchDevicesFinished();
fun onDeviceFound(device: BluetoothDevice)
}
MyView:
interface MyView {
fun showError()
fun showMessage()
}
My module that tells Hilt how to provide the MyView:
#Module
#InstallIn(SingletonComponent::class)
interface MainModule {
#Binds
fun provideView(devicesListFragment: DevicesListView) : MyView
#Binds
fun provideDeviceView(devicesListFragment: DevicesListFragment) : DevicesListView
}
I get this error:
kotlin.UninitializedPropertyAccessException: lateinit property btPresenter has not been initialized
Thanks

Hilt field injection in the super Fragment or ViewModel

I'm using Dagger-Hilt for dependency injection in my Android project, now I have this situation where I have a base abstract Fragment
BaseViewModel.kt
abstract class BaseViewModel constructor(
val api: FakeApi,
) : ViewModel() {
//...
}
Here, I have a dependency which is FakeApi. What I'm trying to do is to inject the FakeApi into the BaseViewModel to be available in the BaseViewModel and all its children.
The first approach I tried is using the constructor injection and inject it to the child and pass it to the super using the constructor.
TaskViewModel.kt
#HiltViewModel
class TaskViewModel #Inject constructor(
api: FakeApi
) : BaseViewModel(api){
}
This approach works fine, but I don't need to pass the dependency from the child to the super class, I need the FakeApi to be automatically injected in the BaseViewModel without having to pass it as I have three levels of abstraction (There is another class inheriting from the TaskViewModel) So I have to pass it two times.
The second approach was to use the field injection as follows
BaseViewModel.kt
abstract class BaseViewModel: ViewModel() {
#Inject
lateinit var api: FakeApi
//...
}
TaskViewModel.kt
#HiltViewModel
class TaskViewModel #Inject constructor(): BaseViewModel() {
}
This approach didn't work for me and the FakeApi wasn't injected and I've got an Exception
kotlin.UninitializedPropertyAccessException: lateinit property api has not been initialized
My questions are
Why field injection doesn't work for me?
Is there any way to use constructor injection for the super class instead of passing the dependency from the child?
Thanks to this Github Issue I figured out that the problem is that you can't use the field injected properties during the ViewModel constructor initialization, but you still use it after the constructor -including all the properties direct initialization- has been initialized.
Dagger firstly completes the constructor injection process then the field injection process takes place. that's why you can't use the field injection before the constructor injection is completed.
❌ Wrong use
abstract class BaseViewModel : ViewModel() {
#Inject
protected lateinit var fakeApi: FakeApi
val temp = fakeApi.doSomething() // Don't use it in direct property declaration
init {
fakeApi.doSomething() // Don't use it in the init block
}
}
✔️ Right use
abstract class BaseViewModel : ViewModel() {
#Inject
protected lateinit var fakeApi: FakeApi
val temp: Any
get() = fakeApi.doSomething() // Use property getter
fun doSomething(){
fakeApi.doSomething() // Use it after constructor initialization
}
}
Or you can use the by lazy to declare your properties.
I tested and I see that field injection in base class still work with Hilt 2.35. I can not get the error like you so maybe you can try to change the Hilt version or check how you provide FakeApi
abstract class BaseViewModel : ViewModel() {
#Inject
protected lateinit var fakeApi: FakeApi
}
FakeApi
// Inject constructor also working
class FakeApi {
fun doSomeThing() {
Log.i("TAG", "do something")
}
}
MainViewModel
#HiltViewModel
class MainViewModel #Inject constructor() : BaseViewModel() {
// from activity, when I call this function, the logcat print normally
fun doSomeThing() {
fakeApi.doSomeThing()
}
}
AppModule
#Module
#InstallIn(SingletonComponent::class)
class AppModule {
#Provides
fun provideAPI(
): FakeApi {
return FakeApi()
}
}
https://github.com/PhanVanLinh/AndroidHiltInjectInBaseClass
After many searches on the Internet, I think the best solution is to not use initializer blocks init { ... } on the ViewModel, and instead create a function fun initialize() { ... } that will be called on the Fragment.
BaseViewModel.kt
#HiltViewModel
open class BaseViewModel #Inject constructor() : ViewModel() {
#Inject
protected lateinit var localUserRepository: LocalUserRepository
}
OnboardingViewModel.kt
#HiltViewModel
class OnboardingViewModel #Inject constructor() : BaseViewModel() {
// Warning: don't use "init {}", the app will crash because of BaseViewModel
// injected properties not initialized
fun initialize() {
if (localUserRepository.isLoggedIn()) {
navigateToHomeScreen()
}
}
}
OnBoardingFragment.kt
#AndroidEntryPoint
class OnBoardingFragment() {
override val viewModel: OnboardingViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.initialize()
}
}
Sources:
https://github.com/google/dagger/issues/2507
the answers on this question

Use the same instance of view model in multiple fragments using dagger2

I am using only dagger2 (not dagger-android) in my project. It's working fine to inject the ViewModel using multibinding. But there's one problem with that previously without dagger2 I was using the same instance of viewmodel used in activity in multiple fragments (using fragment-ktx method activityViewModels()), but now since dagger2 is injecting the view model it's always gives the new instance (checked with hashCode in each fragment) of the viewmodel for each fragment, that's just breaks the communication between fragment using viewmodel.
The fragment & viewmodel code is as below:
class MyFragment: Fragment() {
#Inject lateinit var chartViewModel: ChartViewModel
override fun onAttach(context: Context) {
super.onAttach(context)
(activity?.application as MyApp).appComponent.inject(this)
}
}
//-----ChartViewModel class-----
class ChartViewModel #Inject constructor(private val repository: ChartRepository) : BaseViewModel() {
//live data code...
}
Here's the code for viewmodel dependency injection:
//-----ViewModelKey class-----
#MapKey
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
//-----ViewModelFactory class------
#Singleton
#Suppress("UNCHECKED_CAST")
class ViewModelFactory
#Inject constructor(
private val viewModelMap: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = viewModelMap[modelClass] ?: viewModelMap.asIterable()
.firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
?: throw IllegalArgumentException("Unknown ViewModel class $modelClass")
return try {
creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
//-----ViewModelModule class-----
#Module
abstract class ViewModelModule {
#Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(ChartViewModel::class)
abstract fun bindChartViewModel(chartViewModel: ChartViewModel): ViewModel
}
Is there any way to achieve the same instance of viewmodel for multiple fragment and also at the same time inject the view model in fragments.
Also is there any need for the bindViewModelFactory method as it seems to have no effect on app even without this method.
One workaround could be to make a BaseFragment for fragments which shares the common viewmodel, but that will again include the boilerplate code and also I am not a great fan of BaseFragment/BaseActivity.
This is generated code for ChartViewModel which always create the newInstance of viewModel:
#SuppressWarnings({
"unchecked",
"rawtypes"
})
public final class ChartViewModel_Factory implements Factory<ChartViewModel> {
private final Provider<ChartRepository> repositoryProvider;
public ChartViewModel_Factory(Provider<ChartRepository> repositoryProvider) {
this.repositoryProvider = repositoryProvider;
}
#Override
public ChartViewModel get() {
return newInstance(repositoryProvider.get());
}
public static ChartViewModel_Factory create(Provider<ChartRepository> repositoryProvider) {
return new ChartViewModel_Factory(repositoryProvider);
}
public static ChartViewModel newInstance(ChartRepository repository) {
return new ChartViewModel(repository);
}
}
The problem is that when you inject the viewmodel like this
class MyFragment: Fragment() {
#Inject lateinit var chartViewModel: ChartViewModel
dagger simply creates a new viewmodel instance. There is no viewmodel-fragment-lifecycle magic going on because this viewmodel is not in the viewmodelstore of the activity/fragment and is not being provided by the viewmodelfactory you created. Here, you can think of the viewmodel as any normal class really. As an example:
class MyFragment: Fragment() {
#Inject lateinit var anything: AnyClass
}
class AnyClass #Inject constructor(private val repository: ChartRepository) {
//live data code...
}
Your viewmodel is equivalent to this AnyClass because the viewmodel is not in the viewmodelstore and not scoped to the lifecycle of the fragment/activity.
Is there any way to achieve the same instance of viewmodel for multiple fragment and also at the same time inject the view model in fragments
No. Because of the reasons listed above.
Also is there any need for the bindViewModelFactory method as it seems to have no effect on app even without this method.
It does not have any effect because (I'm assuming that) you are not using the ViewModelFactory anywhere. Since it's not referenced anywhere, this dagger code for the viewmodelfactory is useless.
#Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
Here's what #binds is doing: 1 2
That's why removing it has no effect on the app.
So what is the solution? You need to inject the factory into the fragment/activity and get the instance of the viewmodel using the factory
class MyFragment: Fragment() {
#Inject lateinit var viewModelFactory: ViewModelFactory
private val vm: ChartViewModel by lazy {
ViewModelProvider(X, YourViewModelFactory).get(ChartViewModel::class.java)
}
What is X here? X is ViewModelStoreOwner. A ViewModelStoreOwner is something that has viewmodels under them. ViewModelStoreOwner is implemented by activity and fragment. So you have a few ways of creating a viewmodel:
viewmodel in activity
ViewModelProvider(this, YourViewModelFactory)
viewmodel in fragment
ViewModelProvider(this, YourViewModelFactory)
viewmodel in fragment (B) scoped to a parent fragment (A) and shared across child fragments under A
ViewModelProvider(requireParentFragment(), YourViewModelFactory)
viewmodel in fragment scoped to parent activity and shared across fragments under the activity
ViewModelProvider(requireActivity(), YourViewModelFactory)
One workaround could be to make a BaseFragment for fragments which shares the common viewmodel, but that will again include the boilerplate code and also I am not a great fan of BaseFragment/BaseActivity
Yes, this is indeed a bad idea. The solution is to use requireParentFragment() and requireActivity() to get the viewmodel instance. But you'll be writing the same in every fragment/activity that has a viewmodel. To avoid that you can abstract away this ViewModelProvider(x, factory) part in a base fragment/activity class and also inject the factory in the base classes, which will simplify your child fragment/activity code like this:
class MyFragment: BaseFragment() {
private val vm: ChartViewModel by bindViewModel() // or bindParentFragmentViewModel() or bindActivityViewModel()
You can share ViewModel between fragments when instantiating if the fragments has the same parent activity
FragmentOne
class FragmentOne: Fragment() {
private lateinit var viewmodel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewmodel= activity?.run {
ViewModelProviders.of(this).get(SharedViewModel::class.java)
} : throw Exception("Invalid Activity")
}
}
FragmentTwo
class FragmentTwo: Fragment() {
private lateinit var viewmodel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewmodel= activity?.run {
ViewModelProviders.of(this).get(SharedViewModel::class.java)
} ?: throw Exception("Invalid Activity")
}
}
Add your ViewModel as PostListViewModel inside ViewModelModule:
#Singleton
class ViewModelFactory #Inject constructor(private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T = viewModels[modelClass]?.get() as T
}
#Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
#MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
#Module
abstract class ViewModelModule {
#Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(PostListViewModel::class)
internal abstract fun postListViewModel(viewModel: PostListViewModel): ViewModel
//Add more ViewModels here
}
To end with, our activity will have ViewModelProvider.Factory injected and it will be passed to theprivate val viewModel: PostListViewModel by viewModels { viewModelFactory }
class PostListActivity : AppCompatActivity() {
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val viewModel: PostListViewModel by viewModels { viewModelFactory }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_post_list)
getAppInjector().inject(this)
viewModel.posts.observe(this, Observer(::updatePosts))
}
//...
}
For more check this post:Inject ViewModel with Dagger2 And Check github

Dagger 2: multi-module project, inject dependency but get "lateinit property repository has not been initialize" error at runtime

Dagger version is 2.25.2.
I have two Android project modules: core module & app module.
In core module, I defined for dagger CoreComponent ,
In app module I have AppComponent for dagger.
CoreComponet in core project module:
#Component(modules = [MyModule::class])
#CoreScope
interface CoreComponent {
fun getMyRepository(): MyRepository
}
In core project module, I have a repository class, it doesn't belong to any dagger module but I use #Inject annotation next to its constructor:
class MyRepository #Inject constructor() {
...
}
My app component:
#Component(modules = [AppModule::class], dependencies = [CoreComponent::class])
#featureScope
interface AppComponent {
fun inject(activity: MainActivity)
}
In MainActivity:
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val coreComponent = DaggerCoreComponent.builder().build()
DaggerAppComponent
.builder()
.coreComponent(coreComponent)
.build()
.inject(this)
}
}
My project is MVVM architecture, In general:
MainActivity hosts MyFragment
MyFragment has a reference to MyViewModel
MyViewModel has dependency MyRepository (as mentioned above MyRepository is in core module)
Here is MyViewModel :
class MyViewModel : ViewModel() {
// Runtime error: lateinit property repository has not been initialize
#Inject
lateinit var repository: MyRepository
val data = repository.getData()
}
MyViewModel is initialized in MyFragment:
class MyFragment : Fragment() {
lateinit var viewModel: MyViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
...
}
}
When I run my app, it crashes with runtime error:
kotlin.UninitializedPropertyAccessException: lateinit property repository has not been initialize
The error tells me dagger dependency injection does't work with my setup. So, what do I miss? How to get rid of this error?
==== update =====
I tried :
class MyViewModel #Inject constructor(private val repository: MyRepository): ViewModel() {
val data = repository.getData()
}
Now when I run the app, I get new error:
Caused by: java.lang.InstantiationException: class foo.bar.MyViewModel has no zero argument constructor
====== update 2 =====
Now, I created MyViewModelFactory:
class MyViewModelFactory #Inject constructor(private val creators: Map<Class<out ViewModel>,
#JvmSuppressWildcards Provider<ViewModel>>): ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = creators[modelClass] ?: creators.entries.firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
try {
#Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
I updated MyFragment to be :
class MyFragment : Fragment() {
lateinit var viewModel: MyViewModel
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onAttach(context: Context) {
// inject app component in MyFragment
super.onAttach(context)
(context.applicationContext as MyApplication).appComponent.inject(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// I pass `viewModelFactory` instance here, new error here at runtime, complaining viewModelFactory has not been initialized
viewModel = ViewModelProviders.of(this, viewModelFactory).get(MyViewModel::class.java)
...
}
}
Now I run my app, I get new error:
kotlin.UninitializedPropertyAccessException: lateinit property viewModelFactory has not been initialized
What's still missing?
In order to inject dependencies Dagger must be either:
responsible for creating the object, or
ask to perform an injection, just like in the activities or fragments, which are instantiated by the system:
DaggerAppComponent
.builder()
.coreComponent(coreComponent)
.build()
.inject(this)
In your first approach none of the above is true, a new MyViewModel instance is created outside Dagger's control:
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
therefore the dependency doesn't even get initialized. Additionally, even if you'd perform the injection more manually, like in the activity, the code still would fail, because you are trying to reference the repository property during the initialization process of the object val data = repository.getData(), before the lateinit var gets a chance to be set. In such cases the lazy delegate comes handy:
class MyViewModel : ViewModel() {
#Inject
lateinit var repository: MyRepository
val data by lazy { repository.getData() }
...
}
However, the field injection isn't the most desirable way to perform a DI, especially when the injectable objects needs to know about it. You can inject your dependencies into ViewModels using the construction injection, but it requires some additional setup.
The problem lies in the way view models are created and managed by the Android SDK. They are created using a ViewModelProvider.Factory and the default one requires the view model to have non-argument constructor. So what you need to do to perform the constructor injection is mainly to provide your custom ViewModelProvider.Factory:
// injects the view model's `Provider` which is provided by Dagger, so the dependencies in the view model can be set
class MyViewModelFactory<VM : ViewModel> #Inject constructor(
private val viewModelProvider: #JvmSuppressWildcards Provider<VM>
) : ViewModelProvider.Factory {
#Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
viewModelProvider.get() as T
}
(There are 2 approaches to implementing a custom ViewModelProvider.Factory, the first one uses a singleton factory which gets a map of all the view models' Providers, the latter (the one above) creates a single factory for each view model. I prefer the second one as it doesn't require additional boilerplate and binding every view model in Dagger's modules.)
Use the constructor injection in your view model:
class MyViewModel #Inject constructor(private val repository: MyRepository): ViewModel() {
val data = repository.getData()
}
And then inject the factory into your activities or fragments and use it to create the view model:
#Component(modules = [AppModule::class], dependencies = [CoreComponent::class])
#featureScope
interface AppComponent {
fun inject(activity: MainActivity)
fun inject(fragment: MyFragment)
}
class MyFragment : Fragment() {
#Inject
lateinit var viewModelFactory: MyViewModelFactory<MyViewModel>
lateinit var viewModel: MyViewModel
override fun onAttach(context: Context) {
// you should create a `DaggerAppComponent` instance once, e.g. in a custom `Application` class and use it throughout all activities and fragments
(context.applicationContext as MyApp).appComponent.inject(this)
super.onAttach(context)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this, viewModelFactory)[MyViewModel::class.java]
...
}
}
A few steps you'll need to use Dagger with the AAC ViewModel classes:
You need to use constructor injection in your ViewModel class (as you're doing in the updated question)
You will need a ViewModelFactory to tell the ViewModelProvider how to instantiate your ViewModel
Finally, you will need to tell Dagger how to create your ViewModelFactory
For the first step, pass the repository in the ViewModel constructor and annotate your view model class with #Inject:
class MyViewModel #Inject constructor(private val repository: MyRepository): ViewModel() {
val data = repository.getData()
}
For the second and third steps, one easy way to create a generic ViewModelFactory for any ViewModels that you will have in your project, and also tell Dagger how to use it you can:
Create a Singleton generic ViewModelFactory:
#Singleton
class ViewModelFactory #Inject constructor(private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>) :
ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
viewModels[modelClass]?.get() as T
}
Create a custom annotation to identify your ViewModels and let Dagger know that it needs to provide them:
#Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
#MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
Create a new module for your ViewModels:
#Module
abstract class ViewModelModule {
#Binds
internal abstract fun bindsViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
// Add any other ViewModel that you may have
#Binds
#IntoMap
#ViewModelKey(MyViewModel::class)
internal abstract fun bindsMyViewModel(viewModel: MyViewModel): ViewModel
}
Don't forget to declare the new module in your dagger component
And use the view model in your activity, instantiating it with the help of the ViewModelFactory:
class MyFragment : Fragment() {
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
lateinit var viewModel: MyViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this, viewModelFactory).get(MyViewModel::class.java)
...
}
}

How can i inject object into presenter in android kotlin MVP mosby app with dagger

I am trying to get dagger working in my application.
After creating Module Component and MyApp i can use dagger to inject database service into view but i am having trouble doing same thing with presenter.
Code:
class MyApp : Application() {
var daoComponent: DaoComponent? = null
private set
override fun onCreate() {
super.onCreate()
daoComponent = DaggerDaoComponent.builder()
.appModule(AppModule(this)) // This also corresponds to the name of your module: %component_name%Module
.daoModule(DaoModule())
.build()
}
}
Module
#Module
class DaoModule {
#Provides
fun providesEstateService(): EstateService = EstateServiceImpl()
}
Component
#Singleton
#Component(modules = arrayOf(AppModule::class, DaoModule::class))
interface DaoComponent {
fun inject(activity: MainActivity)
}
AppModule
#Module
class AppModule(internal var mApplication: Application) {
#Provides
#Singleton
internal fun providesApplication(): Application {
return mApplication
}
}
MainActivity
class MainActivity : MvpActivity<MainView, MainPresenter>(), MainView {
#Inject
lateinit var estateService : EstateService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
(application as MyApp).daoComponent!!.inject(this)estateService.numberOfInvoicedEstates.toString()
}
override fun createPresenter(): MainPresenter = MainPresenterImpl()
}
After injecting estateService this way I can use it, but I cant figure out how do I inject service directly into the presenter.
I tried doing it like this but it isn't working.
Should I just pass injected objects from the activity? or maybe I should pass MyApp as an argument or make static method allowing my to get it from any place in the application?
class MainPresenterImpl
#Inject
constructor(): MvpBasePresenter<MainView>(),MainPresenter {
#Inject
lateinit var estateService : EstateService
}
Your component should provide the presenter like that:
#Component(modules = arrayOf(AppModule::class, DaoModule::class))
#Singleton
interface MyComponent {
mainPresenter() : MainPresenter
}
And all dependencies the presenter needs are injected to the presenter via constructor parameters:
class MainPresenterImpl
#Inject constructor(private val estateService : EstateService ) :
MvpBasePresenter<MainView>(),MainPresenter {
...
}
Than in createPresenter() just grab the presenter from dagger component like this:
class MainActivity : MvpActivity<MainView, MainPresenter>(), MainView {
...
override fun createPresenter(): MainPresenter =
(application as MyApp).myComponent().mainPresenter()
}
Please note that the code shown above will not compile. This is just pseudocode to give you an idea how the dependency graph could look like in Dagger.
Please refer to this example on how to use Dagger 2 in combination with MVP.
You have to tell your component where you want to inject it.
So, try with this component:
#Singleton
#Component(modules = arrayOf(AppModule::class, DaoModule::class))
interface DaoComponent {
fun inject(presenter: MainPresenterImpl)
}

Categories

Resources