I have a injectable class like below
class MyClass(private val injectClass: InjectClass) {
fun somFunction() {
// do something
injectClass.load()
}
}
Inside Koin module
factory { MyClass (get()) }
I need to call somFunction() method inside a ViewHolder.
class MyViewHolder(private val viewBind: MyItemBinding) : RecyclerView.ViewHolder(viewBind.root) {
val myClass: MyClass by inject() // compile error
fun bindData() {
// do something
myClass.somFunction()
}
}
I tried to get MyClass instance using inject() but it's not working. How can I get MyClass instance inside the ViewHolder?
Related
I have an android application and I would like to perform dependency injection on a class which is not activity or fragment therefore the applicationContext is not present.
#HiltAndroidApp
class App: Application {
#Inject
lateinit var analytics: Analytics
override fun onCreate() {
super.onCreate()
// other details
}
}
My AppModule
#Module
#InstallIn(ApplicationComponent::class)
abstract class AppModule() {
companion object {
#Provide
#Singleton
fun provideSomeClass(): SomeClass = SomeClass()
}
}
If I try to inject SomeClass in a activity it works fine but not on a non activity class it fails with an error Object is not initialized.
class Consumer {
#lateinit var SomeClass someClass;
}
Can someone point what I am doing wrong?
Inject a field of a non-Activity class
To do this you have to create an Interface that will be an #EntryPoint,
and pass to that interface the ApplicationContext.
Code sample:
// No annotations here
class Consumer(ctx: Context) { // pass here the Android context
// Create an Interface (required by #InstallIn)
#EntryPoint
#InstallIn(SingletonComponent::class) // this could be implementation specific
interface Injector {
fun getSomeClass(): SomeClass // getter that will be injected
// you can also define a proper Kotlin Getter here
}
// create the injector object
val injector = EntryPoints.get(ctx, Injector::class.java)
// retrieve the injected object
val someObject = injector.getSomeClass()
suspend fun andFinallyUseIt() {
someObject.someMethod()
}
}
More:
Make sure you don't winde your scope
Read more: Dagger #EntryPoint
Use inject in constructor
class Consumer #Inject constructor(private val someclass:SomeClass){
//some code
}
I would like to define 2 injected classes, but one needs to use the second class method for the constructor. I am using Koin framework
class MainActivity : AppCompatActivity() {
private val connectionService : ConnectionService by inject()
private val resourcesHelper : ResourcesHelper by inject()
private val addressPropertyName = "connection.address"
private val portPropertyName = "connection.port"
private val appModule = module {
single { ResourcesHelperImpl(androidContext(), R.raw.config) }
single {
ConnectionServiceTcp(
resourcesHelper.getConfigValueAsString(addressPropertyName),
resourcesHelper.getConfigValueAsInt(portPropertyName)
)
}
}
And then I get an error because I cannot instantiate ConnectionServiceTcp using resourcesHelper. Is there a way to use injected field to inject another field?
Edit
Changing to get() helped, but now I struggle with module configuration.
I moved start koin to MainApplication class:
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this#MainApplication)
androidLogger()
modules(appModule)
}
}
}
And module to AppModule.kt
val appModule = module {
single { ResourcesHelperImpl(androidContext(), R.raw.drone) }
single {
ConnectionServiceTcp(
get<ResourcesHelper>().getConfigValueAsString(ResourcesHelper.droneAddressPropertyName),
get<ResourcesHelper>().getConfigValueAsInt(ResourcesHelper.dronePortPropertyName)
)
}
scope(named<MainActivity>()) {
scoped {
ConnectionServiceTcp(get(), get())
}
}
}
And then I try to inject some object to activities and I am getting
Caused by: org.koin.core.error.NoBeanDefFoundException: No definition found for has been found. Check your module definitions.
Okay, I encountered two problems, firstly I could not instantiate bean using other bean in constructor, it was resolved by changing my invoke to
ConnectionServiceTcp(
get<ResourcesHelper>().getConfigValueAsString(ResourcesHelper.droneAddressPropertyName),
get<ResourcesHelper>().getConfigValueAsInt(ResourcesHelper.dronePortPropertyName)
)
Secondly, there was a problem with NoBeanDefFoundException, it was due androidContext() in ResourcesHelperImpl, I needed there Context from the activity, not the koin context.
Is there any alternative to javax.inject.Provider in koin?
To react to actions, I am injecting Commands to my activity.
Command is a single-run object, for example WriteToFile.
In dagger I could make it like this:
class MainPresenter : Presenter() {
#Inject
lateinit var writeFile: Provider<WriteFileCommand>
fun onSaveClicked() {
writeFile.get().run()
}
}
in koin, when I try to use:
class MainPresenter : Presenter() {
lateinit var writeFile: Provider<WriteFileCommand> by inject()
fun onSaveClicked() {
writeFile.get().run()
}
}
My koin module:
val appModule = module {
factory { WriteFileCommand(get(), get()) }
factory { FileProvider() }
single { DataStore() }
}
Than I got error saying:
Can't create definition for 'Factory [name='WriteFileCommand',class='com.test.WriteFileCommand']' due to error :
No compatible definition found. Check your module definition
I understand that I can call:
var command: WriteFileCommand = StandAloneContext.getKoin().koinContext.get()
command.run()
But It looks so cumbersome
There's nothing like a provider directly. If you use inject, you'll use a lazy delegate. If you use get, you'll create a new instance you declared the dependency with a factory. So get is what you need in your case. Just let your MainPresenter implement KoinComponent and you'll be able to use get directly:
class MainPresenter : Presenter(), KoinCompontent {
fun onSaveClicked() = get<WriteFileCommand>().run()
}
WHY NOT HELP ME ANY PEOPLE???
In my application i used Kotin and Kodein for Dependency injection.
I write below codes, but when run application show me Force close error!
In my application i used Kotin and Kodein for Dependency injection.
I write below codes, but when run application show me Force close error!
Application class codes:
class QuoteDiApp : Application(), KodeinAware {
override val kodein = Kodein.lazy {
bind<Database>() with singleton { DatabaseFakeImpl() }
bind<QuoteDao>() with singleton { instance<Database>().quoteDao }
bind<QuoteRepository>() with singleton { QuoteRepositoryImpl(instance()) }
bind() from provider { QuotesViewModelFactory(instance()) }
}
}
Activity class codes:
class QuoteDiActivity : AppCompatActivity(), KodeinAware {
override val kodein by closestKodein()
private val viewModelFactory: QuoteViewModelFactory by instance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_quote_di)
initializeUi()
}
private fun initializeUi() {
// Use ViewModelProviders class to create / get already created QuotesViewModel
// for this view (activity)
val viewModel = ViewModelProviders.of(this, viewModelFactory).get(QuoteViewModel::class.java)
// Observing LiveData from the QuotesViewModel which in turn observes
// LiveData from the repository, which observes LiveData from the DAO ☺
viewModel.getQuotes().observe(this, Observer { quotes ->
val stringBuilder = StringBuilder()
quotes.forEach { quote ->
stringBuilder.append("\n$quote")
}
textView_quotes.text = stringBuilder.toString()
})
// When button is clicked, instantiate a Quote and add it to DB through the ViewModel
button_add_quote.setOnClickListener {
val quote = Quote(editText_quote.text.toString(), editText_author.text.toString())
viewModel.addQuote(quote)
editText_quote.setText("")
editText_author.setText("")
}
}
}
I initialize application class into Manifest
Error message :
Caused by: org.kodein.di.Kodein$NotFoundException: No binding found for bind<QuoteViewModelFactory>() with ? { ? }
Registered in this Kodein container:
bind<QuoteDao>() with singleton { QuoteDao }
bind<QuotesViewModelFactory>() with provider { QuotesViewModelFactory }
bind<Database>() with singleton { DatabaseFakeImpl }
bind<QuoteRepository>() with singleton { QuoteRepositoryImpl }
at org.kodein.di.internal.KodeinContainerImpl.factory(KodeinContainerImpl.kt:174)
at org.kodein.di.KodeinContainer$DefaultImpls.factory$default(KodeinContainer.kt:33)
at org.kodein.di.KodeinContainer$DefaultImpls.provider(KodeinContainer.kt:80)
at org.kodein.di.internal.KodeinContainerImpl.provider(KodeinContainerImpl.kt:7)
at org.kodein.di.KodeinContainer$DefaultImpls.provider$default(KodeinContainer.kt:79)
at org.kodein.di.KodeinAwareKt$Instance$1.invoke(KodeinAware.kt:152)
at org.kodein.di.KodeinProperty$provideDelegate$1.invoke(properties.kt:39)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at nouri.mohammad.mvvm_kodeindi.with_di.ui.quotes.QuoteDiActivity.getViewModelFactory(QuoteDiActivity.kt)
at nouri.mohammad.mvvm_kodeindi.with_di.ui.quotes.QuoteDiActivity.initializeUi(QuoteDiActivity.kt:30)
at nouri.mohammad.mvvm_kodeindi.with_di.ui.quotes.QuoteDiActivity.onCreate(QuoteDiActivity.kt:24)
at android.app.Activity.performCreate(Activity.java:5231)
In your application class you use QuotesViewModelFactory class, but into you activity class you use QuoteViewModelFactory class.
This two classes is not match!
Please check your classes and match this two classes !
I'm new at Koin. I have set all the stuff and is working. But I'm getting some problems when I'm trying to inject interactor and presenter at the same time. That not sure it is possible.
This is my Module
val applicationModule = module(override = true) {
factory{VoucherImpl(get())}
factory<VoucherContract.Presenter> { (view: VoucherContract.View) -> VoucherPresenter(view, get()) }
}
This is my Activity where inject the presenter
private val presenter: VoucherContract.Presenter by inject { parametersOf(this)}
This is my Presenter
class VoucherPresenter (private var view: VoucherContract.View?, private var mCodeRechargeInteract : VoucherImpl) : VoucherContract.Presenter, VoucherContract.Callback, KoinComponent {
override fun create() {
view?.initView()
view?.showProgress()
mCodeRechargeInteract.run()
}
.
.
.
Interactor class
class VoucherImpl(private var mCallback: VoucherContract.Callback?) : AbstractInteractor() {
.
.
.
contract
interface VoucherContract {
interface Presenter {
fun create()
fun destroy()
fun checkIfShoppingCartHaveItems()
fun addVoucherToShoppingCart(voucherProduct: Product)
fun onItemClick(product: Product)
}
interface Callback {
fun onResponseVouchers(vouchers: List<Product>?)
fun onError()
}
}
With this code I get
No definition found for 'xxx.voucher.VoucherContract$Callback' has been found. Check your module definitions.
Then, I try to put it in the module and I can't do it because I get: a Type mismatch. Required VoucherContract.Callback Found VoucherImpl
factory<VoucherContract.Callback> { (callBack: VoucherContract.Callback) -> VoucherImpl(callBack) }
You have a circular dependency that's why this doesn't work.
VoucherImpl(VoucherContract.Callback) and VoucherPresenter(View, VoucherImpl):VoucherContract.Callback
There are multiple ways out of this predicament.
I would recommend the following changes:
The VoucherImpl should not have the constructor parameter VoucherContract.Callback. This callback should be the parameter of a method something like this:
class VoucherImpl : AbstractInteractor(){
fun listen(VoucherContract.Callback){...}
}
This way the dependency becomes one way and you can inject them.