Koinつかってみた(Koin 1.0.1)

# Koinとは

> Koin is a DSL, a lightweight container and a pragmatic API.

See ： [insert-koin.io](https://insert-koin.io/)

※以下のコードは、Koin `v1.0.1`の場合です。今後変更されることがあります。
ViewModel内のNavigatorがActivityに依存しているので、画面回転後の遷移でNPE起こします。ご注意ください。

# 導入する

See ： [Getting started with Android & Java](https://insert-koin.io/docs/1.0/getting-started/android-java/)

```
repositories {
  jcenter()    
}
dependencies {
    // Koin for Android
    compile 'org.koin:koin-android:1.0.1'
    // or Koin for Lifecycle scoping
    compile 'org.koin:koin-androidx-scope:1.0.1'
    // or Koin for Android Architecture ViewModel
    compile 'org.koin:koin-androidx-viewmodel:1.0.1'
}
```
# DIする

今回は、ActivityとFragmentにAACのViewModelをinjectする。
ViewModelには、Repositoryと画面遷移用のNavigatorをコンストラクタで受け取る。

Koinを用いて、ActivityにはViewModelを生成して注入し、
Fragmentには親Activityと同じViewModelを注入する。

## 登場人物

|クラス名|説明|
|:----|:-----|
| `MyApplication`|-|
|`KoinModule`|クラス間の依存解決を行う。|
|`MainViewModel`|AACのViewModelを継承するViewModel。RepositoryとNavigatorに依存する。|
|`Navigator`|画面遷移を担当する。Activityに依存する。|
|`Repository`|リポジトリインターフェース。特に依存関係はなし。|
|`MainActivity`|親Activity。MainFragmentを持つ。レイアウトファイルは省略。|
|`MainFragment`|Fragment。ボタンを一つ持つ。レイアウトファイルは省略。|

## 使用するKoinメソッド

依存解決(Module)する：`single{}`、`factory{}`、`viewModel{}`
注入する：`by viewModel()`、`by sharedViewModel()`

## 登場人物定義
### 依存解決クラス

`KoinModule.kt` では、依存関係の解決を行う。
使用できるBeanDefinitionは以下ページを参照。

[Defining a singleton](https://insert-koin.io/docs/1.0/documentation/reference/index.html#_defining_a_singleton)

[Defining a factory](https://insert-koin.io/docs/1.0/documentation/reference/index.html#_defining_a_factory)

[Scope definition](https://insert-koin.io/docs/1.0/documentation/reference/index.html#_scope_definition)

[ViewModel DSL](https://insert-koin.io/docs/1.0/documentation/reference/index.html#_viewmodel_dsl)

``` KoinModule.kt
// Module用のobjectを作成していると管理が楽かも
object KoinModule {
    fun module() = module {
        // Singletonとなり、同じインスタンスが返される
        // Interfaceを返す場合は、型に指定する
        single<Repository> {RepositoryImpl()}

        // factoryは呼び出される毎に生成される
        // 生成に引数が必要な場合は、ラムダ式に引数を追加する。
        // この引数にはgetやinjectでparametersOfを使用して指定する。
        factory<Navigator> {(activity: Activity) -> Navigator(activity)}

        // AACのViewModelの生成を行う
        // 内部で ViewModelProviders.of(~) が呼ばれている。
        // 1つ目のgetは、Navigatorを解決しているので、引数が必要。
        // 2つ目のgetは、Repositoryを解決しているので、引数が不要。
        viewModel {(app: AppCompatActivity) -> MainViewModel(get{ parametersOf(app) }, get())}
    }
}
```

### Application

Koinの初期化を行う。
listOf()には、複数のmoduleを指定できるので、層によって分けるとかしたほうがいいと思う。

``` MyApplication.kt
class MyApplication: Application() {
    override fun onCreate() {
        super.create()
        startKoin(this, listOf(KoinModule.module()))
    }
}
```

### 注入するクラス

``` MainViewModel.kt
class MainViewModel(private val repository: Repository,
                    private val navigator: Navigator): ViewModel() {

    // Applicationは暗黙的に解決されるため、Moduleには定義不要
    private val application: Application by inject()
    
    val item = MutableLiveData<String>()

    init {
        item.observeForever {
            repository.getItem().subscribe {
                // use item
            }
        }
    }

    fun init(itemResId: Int) {
        item.postValue(application.getString(itemResId))
    }

    fun next() {
        navigator.navigateNext()
    }
}
```

``` Repository.kt
interface Repository {
    fun getItem(): Single<Item>
}
class RepositoryImpl(): Repository {
    override fun getItem(): Single<Item> = Single.just(Item())
}
```

``` Navigator.kt
class Navigator(private val activity: Activity) {
    fun navigateToNext() {
        val intent = Intent(activity, NextActivity::class.java)
        activity.startActivity(intent)
    }
}
```

### 注入されるクラス

``` MainActivity.kt
class MainActivity: AppCompatActivity() {

    // ViewModelを生成する際に、自身（AppCompatActivity）を引数として渡す
    private val vm: MainViewModel by viewModel{ parametersOf(this) }

    override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        vm.init(R.string.text)
    }
}

```

``` MainFragment.kt
class MainFragment: Fragment() {

    // sharedViewModel()で親Activityと同一インスタンスのViewModelが取得できる
    private val vm: MainViewModel by sharedViewModel()

    private lateinit var binding: MainFragmentBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        binding = DataBindingUtil.inflate(inflater,R.layout.fragment_main, container, false)
        binding.button.setOnClickListener {v ->
            vm.next()
        }
        return binding.root
    }
}
```
# v1.0より前と比べて
DSLの書き方が変わっているので、参考にする際はバージョンを確認するようにしたほうがいい。



# 所感
すごくシンプルにDIできて楽だった。
AACのViewModelに対応しているので、AcitvityとFragmentでのViewModel共有が簡単にできる。
コード生成がないので消耗が少ない。なんでビルドできないんや・・・的なのがない。

Daggerとかと比べると、機能的には物足りない。
依存関係を動的に解決するので、解決に失敗しているとコンパイル時にわからず、実行時エラーになる。

コードは雑に書いてるのでコンパイルできないかも・・・
間違ってたりおかしいところあれば連絡ください。