Dagger2 + Fragment + ViewModel に対応

[Dagger2 + Retrofit2 + Moshi + Kotlin を使って通信するまで](https://qiita.com/shimizu-you/items/216d875b49742f73eb86)
こちらの続きで、これに Fragment + ViewModel を追加していきたいと思います。

## フラグメント（with ViewModel）

### Fragment を実装

Fragment の Module を作成する。

```kotlin
@Module
abstract class FragmentModule {
    @ContributesAndroidInjector
    abstract fun contributeMainFragment(): MainFragment
}
```

Component にモジュールを追加する。

```kotlin
@Singleton
@Component(modules = [AndroidSupportInjectionModule::class, ActivityModule::class, FragmentModule::class, RetrofitModule::class])
```

Fragment を DaggerFragment に変更する。

```kotlin
class MainFragment : DaggerFragment() {
```

レイアウトを修正する。

```xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </FrameLayout>

</android.support.constraint.ConstraintLayout>
```

Activity から Fragment を呼び出す。

```kotlin
class MainActivity : DaggerAppCompatActivity() {
    @Inject
    lateinit var github: GitHubService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.container, MainFragment.newInstance())
                .commitNow()
        }
    }
...
```

### ViewModel を実装

annotation を作成する。

```kotlin
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
```

ViewModelProvider.Factory を作成する。

```kotlin
class ViewModelFactory @Inject constructor(
    private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        var creator: Provider<ViewModel>? = creators[modelClass]
        if (creator == null) {
            for ((key, value) in creators) {
                if (modelClass.isAssignableFrom(key)) {
                    creator = value
                    break
                }
            }
        }
        if (creator == null) throw IllegalArgumentException("unknown model class $modelClass")
        try {
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}
```

ViewModel の Module を作成する。

```kotlin
@Module
abstract class ViewModelModule {
    @Binds
    internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class)
    abstract fun bindMainViewModel(mainViewModel: MainViewModel): ViewModel
}
```

Component にモジュールを追加する。

```kotlin
@Singleton
@Component(modules = [AndroidSupportInjectionModule::class, ActivityModule::class, FragmentModule::class, ViewModelModule::class, RetrofitModule::class])
```

依存性の注入をする。
Fragment を下記の内容で修正する。

```kotlin
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
```

　↓

```kotlin
class MainFragment : DaggerFragment() {
    @Inject
    lateinit var viewModelFactory: ViewModelFactory
...
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)
```

ViewModel を修正する。
[Dagger2 + Retrofit2 + Moshi + Kotlin を使って通信するまで](https://qiita.com/shimizu-you/items/216d875b49742f73eb86)
で、Activity に実装した処理を ViewModel に実装することで、処理を分離することが可能です。

```kotlin
class MainViewModel @Inject constructor(private val service: GitHubService) : ViewModel() {
    fun getUser() {
        val userCall: Call<User> = service.getUser("xxxx")
        userCall.enqueue(object : Callback<User> {
            override fun onFailure(call: Call<User>, t: Throwable) {

            }

            override fun onResponse(call: Call<User>, response: Response<User>) {
                if (response.isSuccessful) {
                    val user: User? = response.body()
                }
            }
        })
    }
}
```

getUser() を呼び出す。

```kotlin
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)
        viewModel.getUser()
    }
```

Activity が、すっきりしました。

```kotlin
class MainActivity : DaggerAppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.container, MainFragment.newInstance())
                .commitNow()
        }
    }
}
```

---
本来は、Fragment + ViewModel 含めて記事にしたかったのですが、どうしても膨大になるので、分けて記載しました。
まだ、Dagger2 関連で記事に出来ることがありますので、時間がありましたら、記事にしていきたいと思います。