Dependency Injection 의존성 주입 라이브러리
Activity나 Fragment에서 객체를 생성하는지에 따라 context가 계속 바뀌기 때문에 같은 클래스 타입 객체임에도 다르게 동작할 수 있습니다. 하지만 범용된 환경에서 객체를 생성하고 이렇게 생성된 객체를 Activity나 Fragment에서 주입 받아 상용하는 식으로 구현하면 context의 영향을 받지 않고도 공통으로 재사용할 수 있는 객체를 구현하게 됩니다.
참고로 안드로이드에서는 다음 두 가지 방식의 의존성 주입이 가능합니다.
Constructor Injection (생성자 주입): 생성자를 통해 의존하는 객체 전달
Field Injection (필드 주입) : 객체가 초기화된 후에 의존하는 객체 전달
buildscript { ext.koin_version = '버전 코드' repositories { jcenter() } }
dependencies { implementation "org.koin:koin-androidx-viewmodel:$koin_version" testImplementation "org.koin:koin-test:$koin_version" }
class SampleRepository() { val sampleData = "Sample Data!" }
class SampleController(val repository: SampleRepository) { fun printSampleData() { Log.d("Print sample data", repository.sampleData) } }
class SampleViewModel : ViewModel() { private var _isLoading = MutableLiveData<Boolean>() val isLoading: LiveData<Boolean> = _isLoading }
- 변수에 저장
val appModule = module { single { SampleRepository() }
factory { SampleController(get()) } }
§ val viewModelModule = module { viewModel { SampleViewModel() } }
- single : 앱이 실행되는 동안 계속 유지되는 싱글톤 객체를 생성합니다.
- factory : 요청할 때마다 매번 새로운 객체를 생성합니다.
- get() : 컴포넌트 내에서 알맞은 의존성을 주입 받습니다.
- ViewModel의 경우 viewModel 키워드로 선언해야 합니다.
class SampleApplication : Application() { override fun onCreate() { super.onCreate() startKoin {
androidLogger()
androidContext(this@SampleApplication)
modules(appModule)
modules(viewModelModule) }
}
}
- androidLogger() : AndroidLogger를 Koin logger로 사용합니다.
- androidContext(...) : 해당 안드로이드 context를 사용합니다.
- modules(....) : 사용할 모듈을 등록합니다.
의존성 주입 받기
- by inject() 로 Koin에 등록된 객체를 lazy 하게 주입 받을 수 있습니다.
○ class SampleActivity : AppCompatActivity() {
val controller: SampleController by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate()
} }
- ViewModel의 경우 by viewModel() 사용
○ val viewModel: SampleViewModel by viewModel()
class SampleTest : KoinTest {
@get:Rule val koinTestRule = KoinTestRule.create {
modules(appModule)
}
val controller : SampleController by inject()
@Test fun sampleTest() { ... } }
장점
- 러닝커브가 낮아 쉽고 빠르게 DI를 적용할 수 있습니다.
- Kotlin 개발 환경에 도입하기 쉽습니다.
- 별도의 어노테이션을 사용하지 않기 떄문에 컴파일 시간이 단축됩니다.
- ViewModel 주입을 쉽게 할 수 있는 별도의 라이브러리를 제공합니다.
단점
- Dagger2와 달리 런타임에서 에러가 날 수도 있습니다.(단위 테스트를 통해 방지할 수는 있습니다.)
Activity나 Fragment, Service등이 아닌 곳에서 사용하기 위해선 생성자로 넘기거나 별도의 구현을 해야 합니다.