如何在 Koin 中正确创建 class 的实例?

How to create an instances of the class in Koin correctly?

我现在正在学习 Koin,这是我的麻烦。 当我尝试 运行 应用程序时,我收到此错误:

kt:23)
    at com.example.radioapp.ModulesKt$appModule.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.invoke(Modules.kt:23)
    at com.example.radioapp.ModulesKt$appModule.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.invoke(Modules.kt:13)
    at com.example.radioapp.ModulesKt$appModule.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.invoke(SingleInstanceFactory.kt:53)
    at org.koin.core.instance.SingleInstanceFactory$get.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.invoke(Modules.kt:23)
    at com.example.radioapp.ModulesKt$appModule.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.invoke(Modules.kt:23)
    at com.example.radioapp.ModulesKt$appModule.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.invoke(Modules.kt:13)
    at com.example.radioapp.ModulesKt$appModule.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.invoke(SingleInstanceFactory.kt:53)

(是的,这是我的日志,消息的开头是 t运行,因为它有 13k 行)

此应用程序是用于收听白俄罗斯广播的流媒体应用程序。

我不知道如何创建实现接口的 ViewModel 的实例,我只需要这样做来将 MainFragment 上下文获取到我的 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()
    }
}

所以这是我的 Modules.kt Koin

val appModule = module {
    single { RadioStations() }
    viewModel { MainViewModel(get()) }
    factory<MainViewModelInterface> { MainViewModel(get()) }
    fragment { MainFragment(get()) }
}

我的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)
    }

}

还有我的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()
            }
    }


}

如何在 Modules.kt 中编写我的 classes 的实例?我已经阅读了 Koin 文档,但仍然不明白如何正确执行此操作。

您需要打破依赖关系图中的循环。

RadioStations 取决于 MainFragmentMainFragment 取决于 MainViewModelInterface。它的实现 MainViewModel 取决于 RadioStations。尝试在此图中创建对象会导致无限递归,直到 VM 耗尽堆栈 space。

不会为您调试所有代码,但我会先删除

val context = get<MainFragment>()

因为片段并不是真正的上下文,并且像这样将片段作为依赖项无论如何都是可疑的。考虑将 Context 作为参数传递给那些需要它的函数。