카테고리 없음

Dagger2 설정하기

그란. 2021. 3. 3. 15:43
  1. Without ViewModel
  2. With ViewModel
  3. ViewModel With SavedStateHandle

 

(1)  Without ViewModel

 

Application 

class SSApplication : DaggerApplication() {
    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerAppComponent.builder()
            .application(this)
            .build()
    }
}

 

AppModule

@Module
class AppModule {
    @Binds
    @Singletone
    fun bindsApplication(app:SSApplication) : Application

}

 

ActivityModule

@Module
interface ActivityBindingModule {
    @ActivityScope
    @ContributesAndroidInjector(modules = [MainModule::class])
    fun bindsMainActivity(): MainActivity

}

 

Component

@Singleton
@Component(
    modules = [
        AndroidInjectionModule::class,
        AppModule::class,
        ActivityBindingModule::class
    ]
)
interface AppComponent : AndroidInjector<SSApplication> {

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(app: Application): Builder

        fun build(): AppComponent
    }
}

 

(2) With ViewModel (MultiBinding)

 

ViewModelFactory

@Singleton
class ViewModelFactory @Inject constructor(
    private val mViewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>
): ViewModelProvider.Factory{
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return mViewModels[modelClass]?.get() as T
    }
}

 

ViewModelKey

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

 

ViewModelModule

@Module
abstract class ViewModelModule {
    @Multibinds
    abstract fun bindsViewModels(): Map<Class<out ViewModel>, @JvmSuppressWildcards ViewModel>

    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class)
    abstract fun bindsMainViewModel(factory: MainViewModel.Factory)
    : AssistedSavedStateViewModelFactory<out ViewModel>

}

 

MainViewModel

class MainViewModel : ViewModel() {
    var count = MutableLiveData(0)
 }

 

 

Activity

class MainActivity : DaggerAppCompatActivity() {

    @Inject
    lateinit var viewModelProvider: ViewModelProvider

    private lateinit var tv: TextView
    private lateinit var btn: Button
    private lateinit var finish: Button
    private lateinit var viewModel: MainViewModel


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel = viewModelProvider.get(MainViewModel::class.java)

        tv = findViewById(R.id.tv)
        btn = findViewById(R.id.btn)
        finish = findViewById(R.id.finish)
        btn.setOnClickListener {
            viewModel.count.value = (viewModel.count.value ?: 0) + 1
            showCount()
        }
        finish.setOnClickListener {
            finish()
        }
        showCount()
    }

    private fun showCount() {
        tv.text = "Count is ${viewModel.count.value}"
    }

}

 

 

 

(3) ViewModel With SavedStateHandle

 

AssistedSavedStateViewModelFactory

interface AssistedSavedStateViewModelFactory<T : ViewModel> {
    fun create(savedStateHandle: SavedStateHandle): T
}

 

InjectingSavedStateViewModelFactory

@Singleton
class InjectingSavedStateViewModelFactory @Inject constructor(
    private val assistedFactories: Map<Class<out ViewModel>, @JvmSuppressWildcards AssistedSavedStateViewModelFactory<out ViewModel>>,
    private val viewModelProviders: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) {
    /**
     * @AssistedInject 또는 @Inject로 어노테이션이 달린 ViewModel 인스턴스를 작성하고 필요한 종속성을 전달한다.
     */
    fun create(owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null) =
        object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
            override fun <T : ViewModel?> create(
                key: String,
                modelClass: Class<T>,
                handle: SavedStateHandle
            ): T {
                val viewModel =
                    createAssistedInjectViewModel(modelClass, handle)
                        ?: createInjectViewModel(modelClass)
                        ?: throw IllegalArgumentException("Unknown model class $modelClass")

                try {
                    @Suppress("UNCHECKED_CAST")
                    return viewModel as T
                } catch (e: Exception) {
                    throw RuntimeException(e)
                }
            }
        }

    /**
     * @AssistedInject 생성자와 해당 Factory를 기반으로 ViewModel을 생성한다.
     */
    private fun <T : ViewModel?> createAssistedInjectViewModel(
        modelClass: Class<T>,
        handle: SavedStateHandle
    ): ViewModel? {
        val creator = assistedFactories[modelClass]
            ?: assistedFactories.asIterable()
                .firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
            ?: return null

        return creator.create(handle)
    }

    /**
     * 생성자에 @Inject가 있는 일반적인 Dagger 기반의 ViewModel을 생성한다.
     */
    private fun <T : ViewModel?> createInjectViewModel(
        modelClass: Class<T>
    ): ViewModel? {
        val creator = viewModelProviders[modelClass]
            ?: viewModelProviders.asIterable()
                .firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
            ?: return null

        return creator.get()
    }
}

 

VIewModelModuel 변경 

@AssistedModule
@Module(includes = [AssistedInject_ViewModelModule::class])
abstract class ViewModelModule {
    @Multibinds
    abstract fun bindsViewModels(): Map<Class<out ViewModel>, @JvmSuppressWildcards ViewModel>

    @Multibinds
    abstract fun bindAssistedViewModels(): Map<Class<out ViewModel>, @JvmSuppressWildcards AssistedSavedStateViewModelFactory<out ViewModel>>

    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class)
    abstract fun bindsMainViewModel(factory: MainViewModel.Factory): AssistedSavedStateViewModelFactory<out ViewModel>

}

 

MainModule

@Module
class MainModule {
    @Provides
    @ActivityScope
    fun provideViewModelProvider(
        activity: MainActivity,
        viewModelFactory: InjectingSavedStateViewModelFactory
    ): ViewModelProvider {
        return ViewModelProvider(activity, viewModelFactory.create(activity))
    }
}

 

MainViewModel

class MainViewModel @AssistedInject constructor(
    @Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    var count = savedStateHandle.getLiveData<Int>(0)

     @AssistedInject.Factory
    interface Factory : AssistedSavedStateViewModelFactory<MainViewModel>
}

 

 

참고 : www.charlezz.com/?p=44198