Skip to content

Instantly share code, notes, and snippets.

@marcellogalhardo
Last active April 29, 2020 19:10
Show Gist options
  • Save marcellogalhardo/0b96bd1dc0755aa60ecd1d7eae8ec079 to your computer and use it in GitHub Desktop.
Save marcellogalhardo/0b96bd1dc0755aa60ecd1d7eae8ec079 to your computer and use it in GitHub Desktop.
Coordinator base class.
package com.marcellogalhardo.coordinator
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.AbstractSavedStateViewModelFactory
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.HasDefaultViewModelProviderFactory
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.observe
import kotlin.reflect.KClass
/**
* Base class used to control the instantiation of [Fragment] and [ViewModel]
* instances as well as coordinate the communication of both objects.
*
* Implementations can be registered with a [FragmentManager] via
* [FragmentManager.setFragmentFactory] using [Coordinator.fragmentFactory].
*/
abstract class Coordinator {
/**
* Create a new instance of a [Fragment] with the given [KClass]. This uses
* [FragmentFactory.loadFragmentClass] and the empty constructor of the
* result [Class] by default.
*
* @param fragmentClass The [KClass] of the [Fragment] to instantiate.
* @return Returns a new [Fragment] instance or null to use the default behaviour.
*/
protected abstract fun <T : Fragment> createFragment(
fragmentClass: KClass<out T>
): Fragment?
/**
* Create a new instance of a [ViewModel] with the given [KClass]. This uses
* the [HasDefaultViewModelProviderFactory.getDefaultViewModelProviderFactory]
* of the current [Fragment] and the empty constructor of the [ViewModel]
* by default.
*
* @param modelClass The [KClass] of the [ViewModel] to instantiate.
* @return Returns a new fragment instance or null to use the default behaviour.
*/
protected abstract fun <T : ViewModel> createViewModel(
key: String,
modelClass: KClass<out T>,
handle: SavedStateHandle
): ViewModel?
/**
* Called immediately after [Fragment.onCreateView] has returned, but before
* any saved state has been restored in to the view.
*
* This gives a chance to bind a [Fragment] with a [ViewModel] once their
* view hierarchy has been completely created.
*
* @param fragment The [Fragment] returned by [createFragment].
* @param viewModelProvider A [ViewModelProvider] that delegates all calls
* to [createViewModel].
*/
protected abstract fun onViewCreated(
fragment: Fragment,
viewModelProvider: ViewModelProvider
)
/**
* Object used to integrate with [FragmentManager].
*
* @see FragmentManager.setFragmentFactory
*/
val fragmentFactory: FragmentFactory = CoordinatorFragmentFactory()
private val fragmentLifecycleObserver = FragmentLifecycleObserver()
private inner class CoordinatorFragmentFactory : FragmentFactory() {
@Suppress("UNCHECKED_CAST")
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
val fragmentClass = classLoader.loadClass(className) as Class<out Fragment>
val fragment = createFragment(fragmentClass.kotlin)
if (fragment != null) {
fragment.lifecycle.addObserver(fragmentLifecycleObserver)
return fragment
}
return super.instantiate(classLoader, className)
}
}
private inner class FragmentLifecycleObserver : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
val fragment = owner as Fragment
fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner ->
val viewLifecycleObserver = ViewLifecycleObserver(fragment)
viewLifecycleOwner.lifecycle.addObserver(viewLifecycleObserver)
}
}
}
private inner class ViewLifecycleObserver(
private val fragment: Fragment
) : DefaultLifecycleObserver {
private val viewModelProviderFactory = ViewModelProviderFactory(fragment)
private val viewModelProvider = ViewModelProvider(fragment, viewModelProviderFactory)
override fun onCreate(owner: LifecycleOwner) {
onViewCreated(fragment, viewModelProvider)
}
}
private inner class ViewModelProviderFactory(
val fragment: Fragment
) : AbstractSavedStateViewModelFactory(fragment, null) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
return createViewModel(key, modelClass.kotlin, handle) as? T
?: fragment.defaultViewModelProviderFactory.create(modelClass)
}
}
}
package com.marcellogalhardo.coordinator
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.observe
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.get
import com.marcellogalhardo.coordinator.Coordinator
import com.marcellogalhardo.coordinator.DaggerExampleComponent
import com.marcellogalhardo.coordinator.ExampleComponent
import com.marcellogalhardo.coordinator.ExampleFragment
import com.marcellogalhardo.coordinator.ExampleViewModel
import com.marcellogalhardo.coordinator.ExampleViewModelFactory
import kotlin.reflect.KClass
class ExampleCoordinator : Coordinator() {
// Create your component.
private val component = ExampleComponent.factory().create()
override fun <T : Fragment> createFragment(
fragmentClass: KClass<out T>
): Fragment? = when (fragmentClass) {
ExampleFragment::class -> component.exampleFragment
else -> null
}
override fun <T : ViewModel> createViewModel(
key: String,
modelClass: KClass<out T>,
handle: SavedStateHandle
): ViewModel? = when (modelClass) {
// SavedStateHandle is been "Assisted Injected"
ExampleViewModel::class -> component.exampleViewModelFactory.create(handle)
else -> null
}
override fun onViewCreated(
fragment: Fragment,
viewModelProvider: ViewModelProvider
) {
// Setup your Fragment and ViewModel(s).
if (fragment is ExampleFragment) {
val viewModel = viewModelProvider.get<ExampleViewModel>()
viewModel.state.observe(fragment.viewLifecycleOwner, fragment::onStateChanged)
viewModel.event.observe(fragment.viewLifecycleOwner, fragment::onEventChanged)
fragment.action.observe(fragment.viewLifecycleOwner, viewModel::onActionChanged)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment