LoginSignup
6
4

More than 5 years have passed since last update.

Dagger2 + Fragment + ViewModel に対応

Last updated at Posted at 2018-12-18

Dagger2 + Retrofit2 + Moshi + Kotlin を使って通信するまで
こちらの続きで、これに Fragment + ViewModel を追加していきたいと思います。

フラグメント(with ViewModel)

Fragment を実装

Fragment の Module を作成する。

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

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

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

Fragment を DaggerFragment に変更する。

class MainFragment : DaggerFragment() {

レイアウトを修正する。

<?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 を呼び出す。

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 を作成する。

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

ViewModelProvider.Factory を作成する。

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 を作成する。

@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 にモジュールを追加する。

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

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

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

 ↓

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 を使って通信するまで
で、Activity に実装した処理を ViewModel に実装することで、処理を分離することが可能です。

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() を呼び出す。

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

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

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 関連で記事に出来ることがありますので、時間がありましたら、記事にしていきたいと思います。

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4