[Koin/코인] Koin 시작하기

2020. 4. 24. 14:50DI/Koin

<Koin 이란 ?> 

- Kotlin 개발자를위한 실용적인 경량 DI(dependency injection: 의존성 주입) 프레임 워크
- 프록시, 코드 생성, 리플렉션이없는 기능적 해상도 만 사용하여 순수한 Kotlin으로 작성된 DI 라이브러리 
"Koin is a DSL, a light container and a pragmatic API"
(여기서 DSL이란 ? Domain-Specific Languages : 도메인 특화 언어 즉, 특정 분야에 최적화된 프로그래밍 언어)

* Dagger2 보다 진입장벽이 낮고 kotlin에 특화되어있어 Kotlin 사용자가 사용하기 더욱더 편리하다

 

<Koin 사용 설정 하기>

 

1. build.gradle(:project)

buildscript {
    ext{
        koin_version = '1.0.2'
    }
    //Add Jcenter to your repositories if needed
    repositories {
        jcenter()
    }
}

 

2. build.gradle(:app)

dependencies {
	// Koin AndroidX Scope feature
	implementation "org.koin:koin-androidx-scope:$koin_version"

	// Koin AndroidX ViewModel feature
	implementation "org.koin:koin-androidx-viewmodel:$koin_version"

	// Koin AndroidX Fragment Factory (unstable version)
	implementation "org.koin:koin-androidx-fragment:$koin_version"
}

 

<Koin 시작하기>

Koin은 DSL, 컨테이너 및 pragamtic API로 종속성을 활용합니다.

Koin DSL 은 다음과 같이 구성됨

  • KoinApplication DSL: 제공된 모듈 명세를 이용해 객체 인스턴스를 관리
  • Module DSL: 제공할 객체의 명세

 

1. KoinApplicaion 생성

 

Koin DSL 키워드에 대한 간단한 요약 :

  • startKoin { } -KoinApplication 인스턴스에 따라 생성 및 등록
  • koinApplication { } -KoinApplication 인스턴스 생성
  • modules(...) -사용한 모듈 선언
  • logger() -PrintLogger 선언
  • properties(...) -지도 속성 선언
  • fileProperties() -외부 파일의 속성 사용
  • environmentProperties() -환경 속성 사용
  • androidLogger() -Android Koin 로거 선언
  • androidContext(...) -주어진 안드로이드 컨텍스트를 사용
  • androidFileProperties() -안드로이드 자산의 속성 파일 사용
  • slf4jLogger(...) -SLF4J 로거 사용

 

a. startKoin 사용 

 

- AndroidManifest.xml

 <application
        android:name=".MyApplication"
        ...
 </application>

 

- MyApplicaion.kt

class MyApplication: Application() {
    override fun onCreate() {
        super.onCreate()
        // startKoin 호출 
        startKoin(
        	androidContext = applicationContext, 
        	modules = myDiModule
        )
    }
}

 

b. Custom Koin instance 생성 

// Create and register following KoinApplication instance
startKoin {
    logger()
    modules(coffeeAppModule)
}

// create KoinApplication instance
koinApplication {
    logger()
    modules(coffeeAppModule)
}

 

 

2. module 생성 (myModule.kt)

Koin DSL 키워드에 대한 간단한 요약 :

  • module { } -Koin 모듈 또는 하위 모듈 만들기 (모듈 내부)
  • factory { }- 팩토리 빈 정의를 제공
  • single { } -빈 정의를 제공
  • get() -컴포넌트 의존성 해결
  • named() -타입, 열거 형 또는 문자열로 한정자를 정의
  • bind -지정된 bean 정의에 대한 추가 Kotlin 유형 바인딩
  • binds -주어진 빈 정의에 대한 추가 Kotlin 유형 목록
  • getProperty() -코인 부동산을 해결

 

Koin 모듈은 모든 구성 요소를 선언 할 수 있는 공간 입니다.  module함수를 사용하여 Koin 모듈을 선언.

 

val myModule = module { 
	// your dependencies here 
}

 

mvvm 패턴 기준으로 먼저, 아래와 같이 클래스를 생성한다 .

   1. "retrofit".class -> retrofitPart = module 에 추가 

   2. "model".class -> modelPart = module 에 추가 

   3. "viewModel".class -> viewModelPart = module 에 추가 

 

 

- myModule.kt

val retrofitPart = module {
    single<RetrofitService> {
        RetrofitFactory.build().create(RetrofitService::class.java)
    }
}

var adapterPart = module {
    factory {
        CustomRecyclerViewAdapter()
    }
}

var modelPart = module {
    factory<DataModel> {
        DataModelImpl(get())
    }
}

var viewModelPart = module {
    viewModel {
        MainViewModel(get())
    }
}

//startKoin에서 나열할 module  
var myDiModule = listOf(retrofitPart, adapterPart, modelPart, viewModelPart)

 

정의한 모듈을 startKoin에서 나열해야하는데 미리 myDiModule로 묶어두면 myDiModule만 호출하면 됩니다.

 

 

모듈에 쓰인 클래스들 

 

1. RetrofitService.kt

interface RetrofitService {

    @GET("/v2/search/image")
    fun searchImage(@Header("Authorization") auth:String,
                    @Query("query") query:String,
                    @Query("sort") sort:String,
                    @Query("page") page:Int,
                    @Query("size") size:Int): Single<ImageSearchResponse>

}

 

2. CustomRecyclerViewAdapter.kt

 

class MainSearchRecyclerViewAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>(){

...

}

 

3. DataModel.kt

 

interface DataModel {
    fun getData(query:String, sort: String, page:Int, size:Int): Single<ImageSearchResponse>
}

 

4. DataModelImpl.kt

 

class DataModelImpl(private val service: RetrofitService): DataModel{
    private val API_KEY = "a4a7dc0b83f626751bdba54aa76936d5"

    override fun getData(query:String, sort: String, page:Int, size:Int): Single<ImageSearchResponse> {
        return service.searchImage(auth = API_KEY, query = query, sort = sort.sort, page = page, size = size)
    }
}

 

5. MainViewModel.kt

 

class MainViewModel(private val model: DataModel): BaseKtViewModel(){

    companion object{
        private val TAG = MainViewModel::class.java.simpleName
    }

    private val _imageSearchResponseLiveData = MutableLiveData<ImageSearchResponse>()
    val imageSearchResponseLiveData: LiveData<ImageSearchResponse>
        get() = _imageSearchResponseLiveData

    fun getImageSearch(query: String, page: Int, size: Int){
        addDisposable(model.getData(query, "recency", page, size)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                it.run {
                    if(documents.size > 0) {
                        Log.d(TAG, "document: $documents")
                        _imageSearchResponseLiveData.postValue(this)
                    }
                    Log.d(TAG, "meta : $meta")
                }
            },{
                Log.d(TAG, "response error, message : ${it.message}")
            }))
    }

}

 

3. 실제 사용 방법 (주입받는 방법) 

- get() 또는 by inject() 를 통해 바로 의존성 주입 
- getKoin()  현재 Koin인스턴스 에 액세스


Koin은 ViewModel을 관리하기위한 특수 기능을 제공합니다.
- viewModel ViewModel을 선언하는 특수 DSL 키워드
- by viewModel() & getViewModel()  에서 ViewModel 인스턴스를 주입합니다 ( Activity&에서 Fragment)
- by sharedViewModel() & getSharedViewModel()  ( Fragment) 에서 호스팅 활동에서 ViewModel 인스턴스를 재사용

- MainActivity.class

class MainActivity :
    BaseKtActivity<ActivityMainBinding, MainViewModel>() {
	
    ...
    
    override val viewModel: MainViewModel by viewModel()
    private val mainSearchRecyclerViewAdapter: MainSearchRecyclerViewAdapter by inject()

    ...
    
    companion object {
        private val TAG = MainActivity::class.java.simpleName
    }
}

 

* 더 자세한 내용은 Koin 공식 사이트, Koin 문서, Koin github 를 참고하세요 !!