I'm learning Koin right now and here is my trouble. When I'm trying to run the application, i get this error:
kt:23)
at com.example.radioapp.ModulesKt$appModule$1$3.invoke(Modules.kt:15)
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:53)
at org.koin.core.instance.FactoryInstanceFactory.get(FactoryInstanceFactory.kt:38)
at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:110)
at org.koin.core.scope.Scope.resolveValue(Scope.kt:254)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:241)
at org.koin.core.scope.Scope.get(Scope.kt:204)
at com.example.radioapp.ModulesKt$appModule$1$4.invoke(Modules.kt:23)
at com.example.radioapp.ModulesKt$appModule$1$4.invoke(Modules.kt:16)
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:53)
at org.koin.core.instance.FactoryInstanceFactory.get(FactoryInstanceFactory.kt:38)
at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:110)
at org.koin.core.scope.Scope.resolveValue(Scope.kt:254)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:241)
at org.koin.core.scope.Scope.get(Scope.kt:204)
at com.example.radioapp.coordinators.RadioStations.<init>(RadioStations.kt:130)
at com.example.radioapp.ModulesKt$appModule$1$1.invoke(Modules.kt:13)
at com.example.radioapp.ModulesKt$appModule$1$1.invoke(Modules.kt:13)
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:53)
at org.koin.core.instance.SingleInstanceFactory.create(SingleInstanceFactory.kt:46)
at org.koin.core.instance.SingleInstanceFactory$get$1.invoke(SingleInstanceFactory.kt:53)
at org.koin.core.instance.SingleInstanceFactory$get$1.invoke(SingleInstanceFactory.kt:51)
at org.koin.mp.KoinPlatformTools.synchronized(PlatformToolsJVM.kt:20)
at org.koin.core.instance.SingleInstanceFactory.get(SingleInstanceFactory.kt:51)
at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:110)
at org.koin.core.scope.Scope.resolveValue(Scope.kt:254)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:241)
at org.koin.core.scope.Scope.get(Scope.kt:204)
at com.example.radioapp.ModulesKt$appModule$1$3.invoke(Modules.kt:23)
at com.example.radioapp.ModulesKt$appModule$1$3.invoke(Modules.kt:15)
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:53)
at org.koin.core.instance.FactoryInstanceFactory.get(FactoryInstanceFactory.kt:38)
at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:110)
at org.koin.core.scope.Scope.resolveValue(Scope.kt:254)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:241)
at org.koin.core.scope.Scope.get(Scope.kt:204)
at com.example.radioapp.ModulesKt$appModule$1$4.invoke(Modules.kt:23)
at com.example.radioapp.ModulesKt$appModule$1$4.invoke(Modules.kt:16)
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:53)
at org.koin.core.instance.FactoryInstanceFactory.get(FactoryInstanceFactory.kt:38)
at org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:110)
at org.koin.core.scope.Scope.resolveValue(Scope.kt:254)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:241)
at org.koin.core.scope.Scope.get(Scope.kt:204)
at com.example.radioapp.coordinators.RadioStations.<init>(RadioStations.kt:130)
at com.example.radioapp.ModulesKt$appModule$1$1.invoke(Modules.kt:13)
at com.example.radioapp.ModulesKt$appModule$1$1.invoke(Modules.kt:13)
at org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:53)
at org.koin.core.instance.SingleInstanceFactory.create(SingleInstanceFactory.kt:46)
at org.koin.core.instance.SingleInstanceFactory$get$1.invoke(SingleInstanceFactory.kt:53)
(yes, this is my log, the beginning of the message was truncated because it has 13k lines)
This application is a streaming app for Belarussian radio listening.
I don't know how to create instance of a ViewModel which is implementing an interface and i'm just need to do this to get the MainFragment context to my ApiImpl class (RadioStations):
RadioStations.kt
interface RadioStationsURLAPI {
fun start(url: String)
fun stop()
fun getURL(id: Int): String
}
class RadioStations : KoinComponent, RadioStationsURLAPI {
private val mp = MediaPlayer()
val context = get<MainFragment>()
private fun getRadioCultureURL(): String {
return URLs.radioCulture
}
private fun getRadioWorldURL(): String {
return URLs.radioWorld
}
private fun getRadioEnergyURL(): String {
return URLs.radioEnergy
}
private fun getRadioCenterURL(): String {
return URLs.radioCenter
}
private fun getRadioOneURL(): String {
return URLs.radioOne
}
private fun getRadioRadiusFmURL(): String {
return URLs.radioRadius
}
private fun getRadioCapitalURL(): String {
return URLs.radioCapital
}
private fun getRadioBelarusURL(): String {
return URLs.radioBelarus
}
private fun getRadioBelarusFmURL(): String {
return URLs.radioBelarusFM
}
private fun getRadioCityFmURL(): String {
return URLs.radioCityFM
}
private fun getRadioMogilevURL(): String {
return URLs.radioMogilev
}
private fun getRadioBaURL(): String {
return URLs.radioBA
}
private fun getRadioRocksURL(): String {
return URLs.radioRocks
}
override fun getURL(id: Int): String {
when (id) {
R.id.radio_item_culture -> return getRadioCultureURL()
R.id.radio_item_world -> return getRadioWorldURL()
R.id.radio_item_energy -> return getRadioEnergyURL()
R.id.radio_item_center -> return getRadioCenterURL()
R.id.radio_item_one -> return getRadioOneURL()
R.id.radio_item_radius -> return getRadioRadiusFmURL()
R.id.radio_item_capital -> return getRadioCapitalURL()
R.id.radio_item_belarus -> return getRadioBelarusURL()
R.id.radio_item_belarus_fm -> return getRadioBelarusFmURL()
R.id.radio_item_city_fm -> return getRadioCityFmURL()
R.id.radio_item_mogilev -> return getRadioMogilevURL()
R.id.radio_item_BA -> return getRadioBaURL()
R.id.radio_item_rocks -> return getRadioRocksURL()
else -> return ""
}
}
override fun start(url: String) {
val uri = Uri.parse(url)
println(uri)
mp.apply {
context.context?.let { setDataSource(it, uri) }
prepareAsync()
}
mp.start()
}
override fun stop() {
mp.stop()
}
}
So here is my Modules.kt for Koin
val appModule = module {
single { RadioStations() }
viewModel { MainViewModel(get()) }
factory<MainViewModelInterface> { MainViewModel(get()) }
fragment { MainFragment(get()) }
}
my MainViewModule.kt
interface MainViewModelInterface {
val choosedRadio: BehaviorSubject<Int>
val recoverableError: PublishSubject<String?>
}
class MainViewModel(val radioStations: RadioStations): ViewModel(), MainViewModelInterface {
override val choosedRadio: BehaviorSubject<Int> = BehaviorSubject.create<Int>()
override val recoverableError: PublishSubject<String?> = PublishSubject.create<String?>()
init {
choosedRadio
.map {
radioStations.getURL(it)
}
.observeOn(Schedulers.newThread())
.doOnError {
recoverableError.onNext(it.localizedMessage)
}
.subscribe({
radioStations.start(it)
}, Timber::e)
}
}
And my MainFragment.kt
class MainFragment(val viewModel: MainViewModelInterface) : Fragment() {
private lateinit var toolbar: androidx.appcompat.widget.Toolbar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.toolbar_menu, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_main, container, false)
// Inflate the layout for this fragment
toolbar = view.findViewById(R.id.radio_toolbar)
(requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)
return view
}
override fun onResume() {
super.onResume()
toolbar.setOnMenuItemClickListener {
viewModel.choosedRadio.onNext(it.itemId)
true
}
viewModel.recoverableError
.subscribe {
Toast.makeText(this.context, it, Toast.LENGTH_LONG).show()
}
}
}
How to write instances of my classes in Modules.kt? I've read the Koin documentation but still don't get it how to do this correctly.
CodePudding user response:
You need to break the cycles in your dependency graph.
RadioStations depends on MainFragment. MainFragment depends on MainViewModelInterface. Its implementation MainViewModel depends on RadioStations. Trying to create an object in this graph results in infinite recursion until the VM runs out of stack space.
Not going to debug all of your code for you, but I'd start by removing
val context = get<MainFragment>()
since a fragment is not really a context and having a fragment as dependency like this is suspicious anyway. Consider passing a Context as a parameter to those functions that need it.
