Koin — ano ito, mga prinsipyo ng Dependency Injection at kung paano ito gumagana

May-akda: IT Sectr Nai-publish: 2026-05-04 Oras ng pagbabasa: 8 min

Ang Koin ay isang DI framework para sa Kotlin na gumagana nang walang code generation, reflection, at annotations. Ginagamit ng library ang DSL para ilarawan ang mga module at ini-inject ang dependencies sa pamamagitan ng magaan na container na may suporta para sa Android, Ktor at Multiplatform. Ayon sa opisyal na dokumentasyon ng Koin, ang framework ay nagbibigay ng mga module, scope, at built-in na suporta para sa Jetpack Compose na may minimal na boilerplate.

Mga pangunahing punto

  • Koin — DI framework para sa Kotlin na gumagamit ng DSL nang walang reflection at code generation.
  • Module — lohikal na pagpapangkat ng mga registration ng dependencies sa pamamagitan ng single at factory functions.
  • single — registration ng singleton na ang instance ay ginagawa nang isang beses.
  • factory — registration kung saan ang bawat request ay gumagawa ng bagong instance.
  • Scope — mekanismo ng pagbubuklod ng lifecycle ng dependency sa isang component, halimbawa sa Activity.

Ano ang Koin at paano ito naiiba

Koin — DI framework para sa Kotlin, na isinulat sa purong wika nang hindi gumagamit ng reflection, annotations o code generation. Hindi tulad ng Dagger Hilt na nangangailangan ng annotation processor at code generation sa compilation stage, ang Koin ay gumagana lamang sa runtime gamit ang magaan na DSL para ilarawan ang mga module.

Ang pangunahing ideya ng Koin ay magbigay ng simpleng API para sa pagrerehistro at paglutas ng mga dependencies nang hindi kailangang matuto ng mga kumplikadong konsepto ng dependency graphs at component trees. Inilalarawan ng developer kung aling mga klase ang available sa container, at awtomatikong ini-inject ng Koin ang mga ito sa pamamagitan ng constructor o lazy by inject delegates. Ang framework ay ganap na compatible sa Kotlin Multiplatform, na nagpapahintulot ng pinag-isang DI approach sa Android, iOS at server side.

Ayon sa survey ng Kotlin Developers Community (2025), ang Koin ay ginagamit sa 31% ng komersyal na Android projects, na pumapangalawa lamang sa kasikatan sa Hilt (47%). Ang pangunahing dahilan ng pagpili — pagiging simple ng configuration at kawalan ng pangangailangan para sa code generation, na nagpapabilis sa pagbuo ng proyekto.

Piliin ang Koin para sa katamtaman at malalaking proyekto kung saan mahalaga ang mabilis na pagsisimula ng development, o para sa Kotlin Multiplatform solutions kung saan hindi available ang Hilt para sa mga arkitektural na kadahilanan.

Hindi gumagamit ng reflection at code generation ang Koin — lahat ng registration ay batay sa inline functions na may reified types na sa compilation stage ay naglalagay ng konkretong uri sa katawan ng function. Ginagawa nitong isa ang Koin sa pinakamagaan na DI frameworks sa laki ng final APK: ang pagdagdag ng Koin ay nagpapataas ng laki ng app ng 100–150 KB lamang, samantalang ang Dagger Hilt ay nagdadagdag ng humigit-kumulang 500 KB dahil sa generated code.

Paano gumagana ang container at DSL ng Koin

Ang container ng Koin ay initialize sa pamamagitan ng startKoin function na tumatanggap ng lambda na may configuration. Sa loob ng lambda na ito, inilalarawan ang mga module na may mga registration — ang pangunahing building block ng DI logic.

startKoin at mga module

Ang function na startKoin ay gumagawa ng global container na accessible mula sa anumang punto ng application sa pamamagitan ng GlobalContext, ngunit sa multi-module projects ay inirerekomenda ang paggamit ng KoinApplication para sa paggawa ng isolated containers. Sa Android, para sa initialization ay ginagamit ang AndroidContext na awtomatikong nagbubuklod sa lifecycle ng Application. Ang mga module ay nirerehistro sa pamamagitan ng parameter na modules na tumatanggap ng listahan ng Module instances.

kotlin
val networkModule = module {
    single {
        OkHttpClient()
    }
    single {
        Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .build()
    }
}

startKoin {
    modules(networkModule)
}

Ang bawat module ay naglalaman ng mga definition sa pamamagitan ng single (singleton) o factory (bagong instance). Ang mga definition ay maaaring sumangguni sa ibang narehistrong dependencies sa pamamagitan ng get(), na bumubuo ng injection graph nang walang explicit na pagtukoy ng types at walang boilerplate code.

DSL at inline functions

Ang Koin ay aktibong gumagamit ng inline functions na may reified parameters para sa pagdeduce ng type mula sa context. Nagbibigay-daan ito sa pagsulat ng registration nang hindi itinatakda ang klase: awtomatikong tinutukoy ng single { MyService() } ang type batay sa return value ng lambda.

Hindi tulad ng Dagger, hindi sinusuri ng Koin ang dependency graph sa compilation stage — lahat ng error ay natutukoy sa runtime sa unang pag-access sa hindi nalutas na dependency. Ito ay isang kompromiso na malaki ang pinapasimple sa code at pinapabilis ang build, ngunit nangangailangan ng test coverage ng DI configuration. Maraming team ang pumipili ng Koin dahil sa bilis ng development at pagiging simple, sa kabila ng kawalan ng compile-time checks.

Sa Koin bersyon 3.5, lumitaw ang experimental graph checking sa compilation stage sa pamamagitan ng Koin Annotations plugin. Nagdagdag ang developer ng @Module at @KoinComponent annotations, at ang plugin ay gumagawa ng validation code na tumatakbo sa build. Gayunpaman, ang pangunahing bentahe ng Koin — kawalan ng code generation — ay nawawala sa mode na ito, kaya naman karamihan ng mga team ay patuloy na gumagamit ng klasikong DSL approach na may runtime checks sa pamamagitan ng tests.

Mga paraan ng pag-inject ng dependencies sa Koin

Koin ay nagbibigay ng ilang paraan ng pag-inject ng dependencies: by inject(), get() at direktang pagpasa sa pamamagitan ng constructor. Ang pagpili ng paraan ay depende sa konteksto ng paggamit.

by inject() — lazy injection

Ang delegato na by inject — pinakakaraniwang paraan ng injection sa ViewModel at Android fragments. Ang dependency ay initialize nang lazy — only sa unang pag-access sa property. Ito ay epektibo para sa resource-intensive services na maaaring hindi kailanganin agad.

kotlin
class MainViewModel : ViewModel() {
    private val repository: UserRepository by inject()

    fun loadUsers() {
        repository.fetchAll()
    }
}

get() — explicit na pagkuha

Ang function na get ay nagbabalik ng instance ng dependency kaagad. Ginagamit sa loob ng factory lambdas sa registration o kapag kailangan ang dependency sa synchronous context na walang lazy initialization. Hindi tulad ng by inject(), hindi sinusuportahan ng get() ang lazy loading at nangangailangan na ang container ay nainitialize na sa oras ng pagtawag.

Mga module at scope sa Koin

Scope sa Koin — mekanismo ng pagbubuklod ng lifecycle ng dependencies sa isang partikular na component, tulad ng Activity, Fragment o user session. Ito ay isang mahalagang functionality para sa pamamahala ng memory sa mga Android application.

scope — pagbubuklod sa component

Ang function na scope sa loob ng module ay gumagawa ng scope na nabubuhay hangga't nabubuhay ang nakataling component. Lahat ng dependencies na narehistro sa scope ay nawawasak kapag ito ay isinara, na pumipigil sa memory leaks.

kotlin
val userScope = module {
    scope<UserSession> {
        scoped {
            UserRepository(get())
        }
        scoped {
            SessionManager(get())
        }
    }
}

Ang function na scoped ay nagrerehistro ng dependency na iiral lamang sa loob ng scope. Kapag isinara ang scope, lahat ng scoped objects ay nagiging inaccessible sa garbage collector.

single vs factory

single — registration ng iisang instance para sa buong application na may lazy initialization. Ginagamit para sa stateless services: network clients, caches, logger.

factory — bawat tawag sa get() ay gumagawa ng bagong instance. Inilalapat para sa ViewModel, repositories at mga object na may state kung saan mahalaga ang sariwang instance sa bawat pag-access.

Koin sa mga proyektong Android

Ang integrasyon ng Koin sa isang Android project ay minimal: magdagdag ng dependency sa build.gradle at tawagan ang startKoin sa Application.onCreate. Nagbibigay ang Koin ng mga module para sa integrasyon sa Jetpack Compose, Navigation at WorkManager, na ginagawa itong ganap na alternatibo sa Hilt.

Ang espesyal na library na koin-android-compose ay nagpapahintulot ng pag-inject ng dependencies nang direkta sa Composable functions sa pamamagitan ng koinViewModel() at koinInject() functions. Ito ay nag-aalis ng pangangailangan na ipasa ang container sa pamamagitan ng parameters ng bawat screen at ginagawang mas malinis ang ViewModel code dahil sa awtomatikong pagbubuklod sa lifecycle.

Ayon sa Google I/O 2024, ang Jetpack Compose ay naging pangunahing framework para sa mga bagong Android projects. Ang Koin ay nagbibigay ng native na suporta para sa Compose nang walang karagdagang configuration, awtomatikong nagkokonekta ng mga scope sa lifecycle ng ViewModel sa pamamagitan ng koinViewModel() na isinasaalang-alang ang coroutine context.

Para sa pag-test, ang Koin ay nagbibigay ng koinTest at koinTestRule functions na gumagawa ng isolated test container na may test modules at awtomatikong isinasara ito pagkatapos ng test. Tinitiyak nito ang isolation ng tests at pinipigilan ang state leaks sa pagitan ng test cases.

Ang integrasyon ng Koin sa Jetpack Navigation ay ipinapatupad sa pamamagitan ng module na koin-androidx-navigation. Ang ViewModel ng bawat screen ay awtomatikong tumatanggap ng dependencies sa pamamagitan ng by viewModel() na may pagpasa ng SavedStateHandle para sa pagpapanatili ng state sa screen rotation at pag-restore pagkatapos ng minimization ng app.

Para sa unit testing ng ViewModel gamit ang Koin, ginagamit ang koinTestRule mula sa library na koin-test-junit5 o koin-test-junit4. Ang rule ay gumagawa ng isolated container na may test modules bago ang bawat test at awtomatikong isinasara ito pagkatapos, na pumipigil sa state leaks sa pagitan ng test cases. Ang mga tunay na dependencies ay pinapalitan ng mga mock sa pamamagitan ng MockK: ang module na may registrations na single { mockk() } ay nag-o-override sa pangunahing module, at ang ViewModel sa test ay tumatanggap ng predictable na behavior ng dependencies.

Isa sa mga pangunahing kakayahan ng Koin 3.x ay ang suporta para sa Ktor para sa paggawa ng server applications sa Kotlin at Compose Multiplatform para sa desktop applications. Ginagawa nitong Koin ang tanging DI framework na sumasaklaw sa lahat ng tatlong Kotlin platform nang hindi binabago ang injection paradigm. Ang module na koin-ktor ay nagpapahintulot ng registration ng dependencies sa pamamagitan ng install(Koin) sa Application block at pag-inject ng services sa routes sa pamamagitan ng by inject() tulad ng sa Android. Ginagawa nitong Koin isang unibersal na DI solution para sa Kotlin projects ng anumang architecture — mula mobile client hanggang server backend.

Ang integrasyon ng Koin sa Jetpack Navigation sa pamamagitan ng module na koin-androidx-navigation ay nag-aalis ng pangangailangan para sa manual na paggawa ng ViewModelProvider.Factory para sa bawat screen. Para sa multi-module projects, sinusuportahan ng Koin ang lazy loading ng modules sa pamamagitan ng loadKoinModules, na nagpapahintulot ng pagkonekta ng DI configuration ng bawat feature module nang independyente.

Mga madalas itanong

Paano naiiba ang Koin sa Dagger Hilt?

Koin ay gumagana sa runtime nang walang code generation at annotations, na nagpapabilis ng build, ngunit hindi sinusuri ang dependency graph sa compilation stage. Ang Hilt ay gumagawa ng code sa compilation at nakakakita ng DI errors nang mas maaga, ngunit nangangailangan ng kumplikadong configuration at pinapabagal ang build.

Sinusuportahan ba ng Koin ang Kotlin Multiplatform?

Oo, ganap na sinusuportahan ng Koin ang Kotlin Multiplatform. Ang library na koin-core ay gumagana sa lahat ng Kotlin platforms, at ang koin-android at koin-compose ay nagdaragdag ng platform-specific capabilities para sa Android at iOS ayon sa pagkakabanggit.

Paano haharapin ang cyclic dependencies sa Koin?

Ang mga cyclic na dependencies ay nagdudulot ng StackOverflowError sa runtime. Hindi awtomatikong natutukoy ng Koin ang mga ito. Solusyon — refactoring ng architecture: pag-extract ng common interface, paggamit ng Listener/Observer pattern, o pag-break ng cycle sa pamamagitan ng factory na may delayed initialization.

Kailangan bang manual na isara ang mga scope?

Sa Android, ang mga scope ay maaaring i-binding sa lifecycle ng Activity o Fragment sa pamamagitan ng AndroidScope. Kapag ang component ay nawasak, awtomatikong isinasara ng Koin ang kaukulang scope. Sa custom scopes (user session), ang pagsasara ay ginagawa nang manual sa pamamagitan ng pagtawag sa scope.close.

Paano i-test ang code gamit ang Koin?

Gamitin ang function na koinTest mula sa module na koin-test. Gumagawa ito ng isolated container na may test modules na awtomatikong nagsasara pagkatapos ng test. Ang mga tunay na dependencies ay pinapalitan ng mga mock sa pamamagitan ng module na may Mockito o MockK.

Buod

  • Koin — DI framework para sa Kotlin na may DSL nang walang reflection, annotations at code generation.
  • Ang mga module ay nagpapangkat ng mga registration sa pamamagitan ng single (singleton) at factory (bagong instance).
  • Ang startKoin ay nag-i-initialize ng global container na accessible sa pamamagitan ng by inject() at get().
  • Scope ay nagbubuklod ng lifecycle ng dependencies sa lifecycle ng Android component.
  • Ang injection sa Compose ay ginagawa sa pamamagitan ng koinViewModel() at koinInject().
  • Sinusuportahan ng Koin ang Kotlin Multiplatform, nagpapahintulot ng pinag-isang DI approach sa lahat ng platforms.
  • Ang DI errors ay natutukoy sa runtime, kaya naman ang test coverage ng configuration ay sapilitan.

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din