Am getting UninitializedPropertyAccessException when i inject a methord in my presenter
My provider class
#Module
class ActivityModule(private var activity: BaseActivity) {
#Provides
fun provideActivity(): BaseActivity {
return activity
}
#Provides
#Inject
fun providePresenter(): MainContract.Presenter {
return MainPresenter()
}
#Provides
#Singleton
fun provideGson(): Gson {
return GsonBuilder().setLenient().create()
}
#Provides
#Inject
#Singleton
fun provideServiceGenerator(): ServiceGenerator {
return ServiceGenerator()
}
}
My component class
#Component(modules = [ActivityModule::class])
interface ActivityComponent {
fun inject(mainActivity: MainActivity)
}
And in my Activity class i am injecting the Components as shown below
val activityComponent = DaggerActivityComponent.builder()
.activityModule(ActivityModule(this))
.build()
activityComponent.inject(this)
All works fine in a button click i am calling one api As you can see in my provide i have MainContract.Presenter, am injecting the presenter in my activity and its injected successfully.
#Inject
lateinit var presenter: MainContract.Presenter
Now in My Presenter there is a ServiceGenerator class which i also provided in my provider class and i am injecting the service generatort in my presenter,The proble happens when i call the presenter the injected ServiceGenerator inside the presenter is giving UninitializedPropertyAccessException What is the cause of it and how can i solve this?
The snippet of presenter class as shown below
class MainPresenter : MainContract.Presenter {
#Inject
lateinit var serviceGenerator: ServiceGenerator
When i going for val newsService = serviceGenerator.createService(ApiService::class.java,Constants.BASE_URL)
call am getting the error,Please guide me if am doing anything wrong
You can do one of two things:
Stick with property/field injection for your presenter, in this case you need to add an inject method for it in your Component, and call it in your presenter, just like you've done with your Activity.
Use constructor injection, this way the presenter's dependencies will be injected "automatically" when it's injected in the Activity:
class MainPresenter #Inject constructor(
private val serviceGenerator: ServiceGenerator
) : MainContract.Presenter {
}
Related
I am having a problem that I need inject an instance of repository class into Application class which is provided by Module (Installed in ViewModelComponent, and provide function marked with #ViewModelScope annotation)
Repository
interface IARepository
class ARepository #Inject constructor() : IARepository
Module
#Module
#InstallIn(ViewModelComponent::class)
interface RepositoryModule {
#Binds
#ViewModelScoped
fun provideARepos(impl: ARepository): IARepository
}
ViewModel
#HiltViewModel
class TestViewModel #Inject constructor(
private val useCase1: UseCase1,
private val useCase2: UseCase2,
) {
...
}
Two UseCase1 and UseCase2 are using IARepository, since if I provides IARepository with ViewModelScope, two instance useCase1 and useCase2 will be using the same instance of repository.
It worked until I inject repository into Application (singleton things)
Application
#HiltAndroidApp
class TestApplication : Application() {
#Inject
lateinit var a: IARepository
}
After that I got error
[Dagger/MissingBinding] IARepository cannot be provided without an #Provides-annotated method.
public abstract static class SingletonC implements FragmentGetContextFix.FragmentGetContextFixEntryPoint
Application_HiltComponents.java:129: error: [Dagger/MissingBinding] ...core.domain.IARepository cannot be provided without an #Provides-annotated method.
public abstract static class SingletonC implements FragmentGetContextFix.FragmentGetContextFixEntryPoint,
^
A binding for ....core.domain.IARepository exists in ...Application_HiltComponents.ViewModelC:
....core.domain.IARepository is injected at
[...Application_HiltComponents.SingletonC] ...Application.a
...Application is injected at
...Application_HiltComponents.SingletonC] ...Application_GeneratedInjector.injectMoonRoverApplication
In application, I tried switch to inject directly implementation class is ARepository, it worked fine.
#HiltAndroidApp
class TestApplication : Application() {
#Inject
lateinit var a: ARepository
}
But I still want to use interface. Are there any solution for it?
I think you have to use #Provides in module as below
#Module
#InstallIn(ViewModelComponent::class)
interface RepositoryModule {
#Binds
#ViewModelScoped
#Provides
fun provideARepos(impl: ARepository): IARepository
}
Also add #HiltViewModel in your view model as below
#HiltViewModel
class TestViewModel #Inject constructor(
private val useCase1: UseCase1, private val useCase2: UseCase2
) {
...
}
I hope it will help you.
in viewmodel please specify the #HiltViewModel
#HiltViewModel
class TestViewModel #Inject constructor(
private val useCase1: UseCase1, private val useCase2: UseCase2
) {
...
}
edited:-
#Module
#InstallIn(SingletonComponent::class)
object Module {
#Provides
fun ProvideImplRepo() = ImplRepo()
}
#Module
#InstallIn(ViewModelComponent::class)
abstract class RepositoryModule {
#Binds
abstract fun bindLoginRepository(impl: ImplRepo): Repo
}
I am new to Dagger 2. I am trying to make a simple app that has 2 activities, a login activity and a main activity, which is opened when user logs in from the login activity. I have 3 scopes #Singleton, #UserScope, and #ActivityScope. I use Subcomponents.
AppComponent.kt is the root component, and it provides UserComponent and LoginActivityComponent
#Singleton
#Component(modules=[ApplicationModule::class])
interface ApplicationComponent {
// subcomponents
fun plus(userModule: UserModule): UserComponent
fun plus(loginActivityModule: LoginActivityModule): LoginActivityComponent
}
AppModule
#Module
class ApplicationModule (private val baseApp: BaseApp, private val applicationContext: Context) {
#Provides
#Singleton
fun provideApplication(): Application = baseApp
#Provides
#Singleton
fun provideApplicationContext(): Context = applicationContext
}
UserComponent provides MainActivityComponent
#UserScope
#Subcomponent(modules=[UserModule::class])
interface UserComponent {
// subcomponent
fun plus (mainActivityModule: MainActivityModule):MainActivityComponent
}
MainActivityComponent.kt
#ActivityScope
#Subcomponent(modules=[MainActivityModule::class])
interface MainActivityComponent {
fun inject(mainActivity: MainActivity)
}
Now MainActivityModule has #Provide MainPresenter method, cause I need to inject it into MainActivity
#Module
class MainActivityModule(private val mainActivity: MainActivity) {
#Provides
#ActivityScope
fun provideMainActivity(): MainActivity = mainActivity
#Provides
#ActivityScope
fun provideMainPresenter(): MainPresenter = MainPresenterImpl()
}
MainActivity
class MainActivity: BaseActivity(), MainView {
#Inject
lateinit var presenter: MainPresenter
#Inject
lateinit var user: User
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupActivityComponent()
presenter.attachView(this)
logoutButton.setOnClickListener {
presenter.onLogoutButtonClicked()
}
}
override fun setupActivityComponent() {
BaseApp.instance.getUserComponent()?.plus(MainActivityModule(this))?.inject(this)
}
}
Yet the compiler complains:
[app.di.component.MainActivityComponent.inject(app.ui.main.MainActivity)] app.ui.main.MainPresenter cannot be provided without an #Provides- or #Produces-annotated method.
public abstract interface ApplicationComponent {
^
app.ui.main.MainPresenter is injected at
app.ui.main.MainActivity.presenter
app.ui.main.MainActivity is injected at
app.di.component.MainActivityComponent.inject(mainActivity)
I'm creating an application in Kotlin using the MVP pattern.
I would need to inject a Repository into my Presenter for this purpose. Except that for this, my Repository requires a Retrofit interface as a parameter of its constructuor.
I'm a beginner in the use of Dagger2, and the answers found on the internet are far too complicated for such a basic case like mine.
Here's the repository i want to be injected :
class RepositoryInventory(private val api: Service): IRepositoryInventory {
override fun getInventoryItemByNum(itemnum: String): Observable<Response<Item>> {
return api.getInventoryItemByNum(itemnum)
.toObservable()
}
override fun getAllInventoryItems(): Single<Response<Item>> {
return api.getAllInventoryItems()
}
}
My Component
#Singleton
#Component(modules = arrayOf(ActivityModule::class))
interface ActivityComponent {
fun inject(loginActivity: LoginActivity)
fun inject(itemDetailActivity: ItemDetailActivity)
}
My module :
#Module
class ActivityModule(private var activity: Activity) {
#Provides
fun provideActivity(): Activity {
return activity
}
#Provides
fun provideLoginPresenter(): LoginPresenter {
return LoginPresenter()
}
#Provides
fun provideItemDetailPresenter(): ItemDetailPresenter {
return ItemDetailPresenter()
}
}
In my activity, my module is injected with this method :
private fun injectDependency() {
val activityComponent = DaggerActivityComponent.builder()
.activityModule(ActivityModule(this))
.build()
activityComponent.inject(this)
}
I have 2 components and 2 modules: one designed to inject into a fragment and the other into an activity.
Except in my case, I want to inject into a Presenter that is not a Fragment or an Activity but a class
Ok, my guess is you want to inject RepositoryInventory into LoginPresenter. If so, you can make use of #ContributesAndroidInjector and Binds
First, create a LoginActivityModule
#Module
abstract class LoginActivityModule {
#Binds
abstract fun loginPresenter(loginPresenter: LoginPresenter): LoginPresenter
}
Then, create a module called ActivityBindingModule
#Module
abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = [LoginActivityModule::class])
abstract fun loginActivity(): LoginActivity
#ContributesAndroidInjector()
abstract fun itemDetailActivity(): ItemDetailActivity
}
And change your ActivityComponent like this
#Singleton
#Component(modules = arrayOf(ActivityModule::class, ActivityBindingModule::class))
interface ActivityComponent {
}
And in your LoginPresenter:
class LoginPresenter #Inject constructor(private val repositoryInventory: IRepositoryInventory) {
...
}
Remember to remove this in ActivityModule:
#Provides
fun provideLoginPresenter(): LoginPresenter {
return LoginPresenter()
}
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)
}
I have problem using dagger2
I create Component, Module, Provide
class testModule {
#Provides #Singleton
fun provideTestServer(): TestService {
}
}
and i called onCreate() in MainActivity
DaggerImageComponent.builder().build().inject(this)
here's my problem
DI works fine in MainActivity
class MainActivity: AppCompatActivity {
#Inject
lateinit var testService: TestService
}
but other file is not working.
object TestObject {
#Inject
#JvmSynthetic // error: static field cannot inject
lateinit var testService: TestService
fun test() = testService.testfun()
}
or
#Singleton
class TestClass {
#Inject
lateinit var testService: TestService
fun test() = testService.testfun()
}
TestClass and TestObject get error
- lateinit property testInterface has not been initialized
i don't understand why error occured in TestClass, TestObject.
You should call "inject" inside of class where you want to get injected variable.
You did it for MainActivity, but also you should inject your component inside other classes. By the way, you have TestClass, it seems like you use it in client code also from injection, because it has "Singleton" annotation. If it's true - you can simply add provider for it in your module and pass service as a contructor parameter:
class testModule {
#Provides #Singleton
fun provideTestServer(): TestService {
}
#Provides #Singleton
fun provideTestServer(testService: TestService): TestClass {
}
}
then, your TestClass should have constructor:
class TestClass(var testService: TestService) {
fun test() = testService.testfun()
}
I suggest you to read once again about dagger, check this tutorial:
http://www.vogella.com/tutorials/Dagger/article.html