Skip to content

Instantly share code, notes, and snippets.

@bleeding182
Created March 30, 2018 17:52
Show Gist options
  • Save bleeding182/3de7151949090f5f0e33b6cd7ca37cf4 to your computer and use it in GitHub Desktop.
Save bleeding182/3de7151949090f5f0e33b6cd7ca37cf4 to your computer and use it in GitHub Desktop.
Dagger 2 `@PerScreen` scope surviving configuration change
class App : Application() {
@Inject
lateinit var applicationInjector: ApplicationInjector
override fun onCreate() {
super.onCreate()
DaggerAppComponent.create().inject(this)
registerActivityLifecycleCallbacks(applicationInjector)
}
}
@Singleton
@Component(modules = [ScreenModule::class])
interface AppComponent {
fun inject(app: App)
fun getBinding(): Map<Class<out Activity>, Provider<ProvidesActivityInjectorFactories.Builder>>
}
@Singleton
class ApplicationInjector @Inject constructor(
private val injectorFactories: Map<Class<out Activity>, @JvmSuppressWildcards Provider<ProvidesActivityInjectorFactories.Builder>>
) : Application.ActivityLifecycleCallbacks {
//region < Unused callback methods >
override fun onActivityPaused(activity: Activity) = Unit
override fun onActivityResumed(activity: Activity) = Unit
override fun onActivityStarted(activity: Activity) = Unit
override fun onActivityDestroyed(activity: Activity) = Unit
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) = Unit
//endregion
/**
* Map of components that survive a configuration change.
*/
private val screenComponents: MutableMap<Class<out Activity>, ProvidesActivityInjectorFactories> = HashMap()
override fun onActivityStopped(activity: Activity) {
if (activity.isFinishing) {
// remove finished activity component
screenComponents.remove(activity.javaClass)
}
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
val screenComponent: ProvidesActivityInjectorFactories = screenComponents[activity.javaClass]
?: injectorFactories[activity.javaClass]!!.get().build().also {
screenComponents[activity.javaClass] = it
}
@Suppress("UNCHECKED_CAST")
val activityInjectorFactory: AndroidInjector.Factory<Activity> = (screenComponent.provideInjectors()[activity.javaClass] as AndroidInjector.Factory<Activity>)
activityInjectorFactory.create(activity).inject(activity)
}
}
@PerScreen
class ScreenRandom @Inject constructor() {
val random = Random().nextInt()
}
@PerActivity
class ActivityRandom @Inject constructor() {
val random = Random().nextInt()
}
class MainActivity : AppCompatActivity() {
@Inject
lateinit var screenRandom: ScreenRandom
@Inject
lateinit var activityRandom: ActivityRandom
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
label.text = "This screens random number is:\n${screenRandom.random}\n\nThis activities random number is:\n${activityRandom.random}"
}
}
@Module(subcomponents = [MainScreenSubComponent::class])
interface ScreenModule {
@Multibinds
fun screenInjectorFactories(): Map<Class<out Activity>, ProvidesActivityInjectorFactories.Builder>
@Binds
@IntoMap
@ActivityKey(MainActivity::class)
fun bindMainScreenSubComponent(builder: MainScreenSubComponent.Builder): ProvidesActivityInjectorFactories.Builder
}
interface ProvidesActivityInjectorFactories {
fun provideInjectors(): Map<Class<out Activity>, AndroidInjector.Factory<out Activity>>
interface Builder {
fun build(): ProvidesActivityInjectorFactories
}
}
@PerScreen
@Subcomponent(modules = [MainScreenModule::class])
interface MainScreenSubComponent : ProvidesActivityInjectorFactories {
@Subcomponent.Builder
abstract class Builder : ProvidesActivityInjectorFactories.Builder
}
@Module(subcomponents = [MainActivitySubComponent::class])
interface MainScreenModule {
@Binds
@IntoMap
@ActivityKey(MainActivity::class)
fun bindMainActivityInjectorFactory(builder: MainActivitySubComponent.Builder): AndroidInjector.Factory<out Activity>
}
@PerActivity
@Subcomponent
interface MainActivitySubComponent : AndroidInjector<MainActivity> {
@Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<MainActivity>()
}
@Scope
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class PerActivity
/**
* Survives orientation change.
*/
@Scope
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class PerScreen
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment