Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dagger 2 - Under the hood

Dagger 2 - Under the hood

A look at Dagger's generated code and how it enables scoping to work.

Brandon Gogetap

November 14, 2017
Tweet

More Decks by Brandon Gogetap

Other Decks in Programming

Transcript

  1. QUICK RECAP WHAT IS DEPENDENCY INJECTION? ▸ The idea that

    objects should have their dependencies provided to them, not create them on their own. Good: class RepoRepository(private val service: RepoService) Bad: class RepoRepository() { private val service = RepoService() }
  2. QUICK RECAP WHAT IS A DEPENDENCY INJECTION FRAMEWORK? ▸ A

    library that assists the developer with providing dependencies to core application objects class RepoRepository @Inject constructor(private val service: RepoService)
  3. Dagger is an example of over-engineering— it’s too complicated for

    what you get out of it -Developer who hasn’t seen the light
  4. DAGGER 2 Components Hold all dependencies for a given scope

    Module Module Module Define how to create dependencies
  5. DAGGER 2 Components Hold all dependencies for a given scope

    Module Module Module Define how to create dependencies
  6. DAGGER 2 - SCOPES @Singleton @Component interface ApplicationComponent Application Module

    Network Module Database Module @ActivityScope @Subcomponent interface MyActivityComponent Navigation Module Permission Module
  7. DAGGER 2 - SCOPES @Singleton @Component interface ApplicationComponent Application Module

    Network Module Database Module @ActivityScope @Subcomponent interface MyActivityComponent Navigation Module Permission Module @ScreenScope @Subcomponent interface ScreenOneComponent List Item Renderer Module Lifecycle Task Module
  8. DAGGER 2 - SCOPES @ScreenScope @Subcomponent interface ScreenOneComponent List Item

    Renderer Module Lifecycle Task Module @ScreenScope @Subcomponent interface ScreenTwoComponent @Singleton @Component interface ApplicationComponent Application Module Network Module Database Module @ActivityScope @Subcomponent interface MyActivityComponent Navigation Module Permission Module Screen2 Endpoint Module Lifecycle Task Module
  9. DAGGER 2 - BUILD TYPE SPECIFIC DEPENDENCIES DEBUG VS. RELEASE

    BUILDS @Singleton @Component(modules = { AndroidSupportInjectionModule.class, ApplicationModule.class, ActivityBindingModule.class, NetworkModule.class, ServiceModule.class, }) public interface ApplicationComponent extends AndroidInjector<MyApplication> { }
  10. DAGGER 2 - BUILD TYPE SPECIFIC DEPENDENCIES DEBUG VS. RELEASE

    BUILDS @Module public abstract class NetworkModule { @Provides @Named("base_url") static String provideBaseUrl() { return “https://api.debug.mydomain.com/“; } @Provides @Singleton static Call.Factory provideCallFactory() { return new OkHttpClient.Builder() .addNetworkInterceptor(new StethoInterceptor()) .addInterceptor(new LoggingInterceptor()) .build(); } } @Module public abstract class NetworkModule { @Provides @Named("base_url") static String provideBaseUrl() { return “https://api.mydomain.com/"; } @Provides @Singleton static Call.Factory provideCallFactory() { return new OkHttpClient.Builder() .build(); } } Debug Release
  11. DAGGER 2 - TEST SPECIFIC DEPENDENCIES TEST VS. STANDARD BUILDS

    @Module public abstract class TestServiceModule { @Binds abstract RepoService provideRepoService(TestRepoService service); } @Module public abstract class ServiceModule { @Provides @Singleton static RepoService provideRepoService(Retrofit retrofit) { return retrofit.create(RepoService.class); } } androidTest main @Singleton @Component(modules = { AndroidSupportInjectionModule.class, ActivityBindingModule.class, ApplicationModule.class, TestServiceModule.class, TestNetworkModule.class, }) public interface TestApplicationComponent extends ApplicationComponent { void inject(RepoListFragmentTest repoListFragmentTest); } @Singleton @Component(modules = { AndroidSupportInjectionModule.class, ApplicationModule.class, ActivityBindingModule.class, NetworkModule.class, ServiceModule.class, }) public interface ApplicationComponent extends AndroidInjector<MyApplication> { }
  12. DAGGER 2 - BUILDING SCOPES Application @Singleton @Component ApplicationComponent Map

    of Activity Component Builders Activity @ActivityScope @Subcomponent MainActivityComponent Map of Screen Component Builders FragmentOne @ScreenScope @Subcomponent ScreenOneComponent FragmentTwo @ScreenScope @Subcomponent ScreenTwoComponent
  13. DAGGER 2 - BUILDING SCOPES @Singleton @Component(modules = { AndroidSupportInjectionModule.class,

    ApplicationModule.class, ActivityBindingModule.class, NetworkModule.class, ServiceModule.class, }) public interface ApplicationComponent extends AndroidInjector<MyApplication> { }
  14. DAGGER 2 - BUILDING SCOPES public class MyApplication extends DaggerApplication

    { protected ApplicationComponent component; @Override public void onCreate() { super.onCreate(); component = initComponent(); if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); } } protected ApplicationComponent initComponent() { return DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule(this)) .build(); } @Override protected AndroidInjector<? extends DaggerApplication> applicationInjector() { return component; } }
  15. DAGGER 2 - BUILDING SCOPES @ActivityScope @Subcomponent(modules = MainActivityModule.class) public

    interface MainActivityComponent extends AndroidInjector<MainActivity> { @Subcomponent.Builder abstract class Builder extends AndroidInjector.Builder<MainActivity> { } }
  16. DAGGER 2 - BUILDING SCOPES @Module(subcomponents = MainActivityComponent.class) public abstract

    class ActivityBindingModule_MainActivity { @Binds @IntoMap @ActivityKey(MainActivity.class) abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(MainActivitySubcomponent.Builder builder); }
  17. DAGGER 2 - BUILDING SCOPES @ScreenScope @Subcomponent(modules = ScreenModule.class) public

    interface RepoDetailComponent extends AndroidInjector<RepoDetailFragment> { @Subcomponent.Builder abstract class Builder extends AndroidInjector.Builder<RepoDetailFragment> { @BindsInstance public abstract void bindOwner(@Named("owner") String owner); @BindsInstance public abstract void bindRepoName(@Named("repo") String repo); @Override public void seedInstance(RepoDetailFragment repoDetailFragment) { bindOwner(repoDetailFragment.getArguments().getString(RepoDetailFragment.OWNER_KEY)); bindRepoName(repoDetailFragment.getArguments().getString(RepoDetailFragment.REPO_KEY)); } } }
  18. DAGGER 2 - BUILDING SCOPES @ScreenScope @Subcomponent(modules = ScreenModule.class) public

    interface RepoDetailComponent extends AndroidInjector<RepoDetailFragment> { @Subcomponent.Builder abstract class Builder extends AndroidInjector.Builder<RepoDetailFragment> { @BindsInstance public abstract void bindOwner(@Named("owner") String owner); @BindsInstance public abstract void bindRepoName(@Named("repo") String repo); @Override public void seedInstance(RepoDetailFragment repoDetailFragment) { bindOwner(repoDetailFragment.getArguments().getString(RepoDetailFragment.OWNER_KEY)); bindRepoName(repoDetailFragment.getArguments().getString(RepoDetailFragment.REPO_KEY)); } } }
  19. DAGGER 2 - BUILDING SCOPES @ScreenScope @Subcomponent(modules = ScreenModule.class) public

    interface RepoDetailComponent extends AndroidInjector<RepoDetailFragment> { @Subcomponent.Builder abstract class Builder extends AndroidInjector.Builder<RepoDetailFragment> { @BindsInstance public abstract void bindOwner(@Named("owner") String owner); @BindsInstance public abstract void bindRepoName(@Named("repo") String repo); @Override public void seedInstance(RepoDetailFragment repoDetailFragment) { bindOwner(repoDetailFragment.getArguments().getString(RepoDetailFragment.OWNER_KEY)); bindRepoName(repoDetailFragment.getArguments().getString(RepoDetailFragment.REPO_KEY)); } } }
  20. DAGGER 2 - BUILDING SCOPES @ScreenScope @Subcomponent(modules = ScreenModule.class) public

    interface RepoDetailComponent extends AndroidInjector<RepoDetailFragment> { @Subcomponent.Builder abstract class Builder extends AndroidInjector.Builder<RepoDetailFragment> { @BindsInstance public abstract void bindOwner(@Named("owner") String owner); @BindsInstance public abstract void bindRepoName(@Named("repo") String repo); @Override public void seedInstance(RepoDetailFragment repoDetailFragment) { bindOwner(repoDetailFragment.getArguments().getString(RepoDetailFragment.OWNER_KEY)); bindRepoName(repoDetailFragment.getArguments().getString(RepoDetailFragment.REPO_KEY)); } } }
  21. DAGGER 2 - BUILDING SCOPES @ScreenScope @Subcomponent(modules = ScreenModule.class) public

    interface RepoDetailComponent extends AndroidInjector<RepoDetailFragment> { @Subcomponent.Builder abstract class Builder extends AndroidInjector.Builder<RepoDetailFragment> { @BindsInstance public abstract void bindOwner(@Named("owner") String owner); @BindsInstance public abstract void bindRepoName(@Named("repo") String repo); @Override public void seedInstance(RepoDetailFragment repoDetailFragment) { bindOwner(repoDetailFragment.getArguments().getString(RepoDetailFragment.OWNER_KEY)); bindRepoName(repoDetailFragment.getArguments().getString(RepoDetailFragment.REPO_KEY)); } } }
  22. DAGGER 2 - BUILDING SCOPES @Module(subcomponents = RepoDetailComponent.class) public abstract

    class MainScreenBindingModule { @ScreenScope @ContributesAndroidInjector(modules = ScreenModule.class) abstract RepoListFragment repoListFragment(); @Binds @IntoMap @FragmentKey(RepoDetailFragment.class) abstract AndroidInjector.Factory<? extends Fragment> bindDetailInjector(RepoDetailComponent.Builder injectorBuilder); }
  23. DAGGER 2 - BUILDING SCOPES @Module(subcomponents = RepoDetailComponent.class) public abstract

    class MainScreenBindingModule { @ScreenScope @ContributesAndroidInjector(modules = ScreenModule.class) abstract RepoListFragment repoListFragment(); @Binds @IntoMap @FragmentKey(RepoDetailFragment.class) abstract AndroidInjector.Factory<? extends Fragment> bindDetailInjector(RepoDetailComponent.Builder injectorBuilder); }
  24. DAGGER 2 - BUILDING SCOPES @Module(subcomponents = RepoDetailComponent.class) public abstract

    class MainScreenBindingModule { @ScreenScope @ContributesAndroidInjector(modules = ScreenModule.class) abstract RepoListFragment repoListFragment(); @Binds @IntoMap @FragmentKey(RepoDetailFragment.class) abstract AndroidInjector.Factory<? extends Fragment> bindDetailInjector(RepoDetailComponent.Builder injectorBuilder); }
  25. DAGGER 2 - SCOPES @ScreenScope final class RepoDetailPresenter { @Inject

    RepoDetailPresenter( @Named("owner") String owner, @Named("repo") String repoName, RepoRepository repoRepository, @ForScreen DisposableManager disposableManager, RepoDetailViewModel viewModel ) { disposableManager.add(repoRepository.getRepo(owner, repoName) .observeOn(AndroidSchedulers.mainThread()) .doOnSuccess(viewModel::processRepo) .observeOn(Schedulers.io()) .flatMap(repo -> repoRepository.getContributors(repo.getContributorsUrl())) .observeOn(AndroidSchedulers.mainThread()) .subscribe(viewModel::processContributors)); } }
  26. DAGGER 2 - SCOPES @ScreenScope final class RepoDetailPresenter { @Inject

    RepoDetailPresenter( @Named("owner") String owner, @Named("repo") String repoName, RepoRepository repoRepository, @ForScreen DisposableManager disposableManager, RepoDetailViewModel viewModel ) { disposableManager.add(repoRepository.getRepo(owner, repoName) .observeOn(AndroidSchedulers.mainThread()) .doOnSuccess(viewModel::processRepo) .observeOn(Schedulers.io()) .flatMap(repo -> repoRepository.getContributors(repo.getContributorsUrl())) .observeOn(AndroidSchedulers.mainThread()) .subscribe(viewModel::processContributors)); } }
  27. DAGGER 2 - SCOPES @ScreenScope final class RepoDetailPresenter { @Inject

    RepoDetailPresenter( @Named("owner") String owner, @Named("repo") String repoName, RepoRepository repoRepository, @ForScreen DisposableManager disposableManager, RepoDetailViewModel viewModel ) { disposableManager.add(repoRepository.getRepo(owner, repoName) .observeOn(AndroidSchedulers.mainThread()) .doOnSuccess(viewModel::processRepo) .observeOn(Schedulers.io()) .flatMap(repo -> repoRepository.getContributors(repo.getContributorsUrl())) .observeOn(AndroidSchedulers.mainThread()) .subscribe(viewModel::processContributors)); } }
  28. DAGGER 2 - SCOPES @ScreenScope final class RepoDetailPresenter { @Inject

    RepoDetailPresenter( @Named("owner") String owner, @Named("repo") String repoName, RepoRepository repoRepository, @ForScreen DisposableManager disposableManager, RepoDetailViewModel viewModel ) { disposableManager.add(repoRepository.getRepo(owner, repoName) .observeOn(AndroidSchedulers.mainThread()) .doOnSuccess(viewModel::processRepo) .observeOn(Schedulers.io()) .flatMap(repo -> repoRepository.getContributors(repo.getContributorsUrl())) .observeOn(AndroidSchedulers.mainThread()) .subscribe(viewModel::processContributors)); } }
  29. DAGGER 2 - SCOPES private final class RepoDetailComponentImpl implements RepoDetailComponent

    { /** Fields **/ private RepoDetailComponentImpl(RepoDetailComponentBuilder builder) { assert builder != null; initialize(builder); } private void initialize(final RepoDetailComponentBuilder builder) { this.provideScopeSubjectProvider = DoubleCheck.provider(ScreenModule_ProvideScopeSubjectFactory.create()); this.provideDisposableManagerProvider = DoubleCheck.provider( ScreenModule_ProvideDisposableManagerFactory.create(provideScopeSubjectProvider)); this.repoDetailViewModelProvider = DoubleCheck.provider(RepoDetailViewModel_Factory.create()); this.bindOwnerProvider = InstanceFactory.create(builder.bindOwner); this.bindRepoNameProvider = InstanceFactory.create(builder.bindRepoName); this.repoDetailPresenterProvider = DoubleCheck.provider( RepoDetailPresenter_Factory.create( bindOwnerProvider, bindRepoNameProvider, DaggerApplicationComponent.this.repoRepositoryProvider, provideDisposableManagerProvider, repoDetailViewModelProvider)); this.repoDetailFragmentMembersInjector = RepoDetailFragment_MembersInjector.create( MainActivitySubcomponentImpl.this.dispatchingAndroidInjectorProvider, provideScopeSubjectProvider, provideDisposableManagerProvider, repoDetailViewModelProvider, repoDetailPresenterProvider); } @Override public void inject(RepoDetailFragment arg0) { repoDetailFragmentMembersInjector.injectMembers(arg0); } }
  30. DAGGER 2 - SCOPES private void initialize(final RepoDetailComponentBuilder builder) {

    this.provideScopeSubjectProvider = DoubleCheck.provider(ScreenModule_ProvideScopeSubjectFactory.create()); this.provideDisposableManagerProvider = DoubleCheck.provider( ScreenModule_ProvideDisposableManagerFactory.create(provideScopeSubjectProvider)); this.repoDetailViewModelProvider = DoubleCheck.provider(RepoDetailViewModel_Factory.create()); this.bindOwnerProvider = InstanceFactory.create(builder.bindOwner); this.bindRepoNameProvider = InstanceFactory.create(builder.bindRepoName); this.repoDetailPresenterProvider = DoubleCheck.provider( RepoDetailPresenter_Factory.create( bindOwnerProvider, bindRepoNameProvider, DaggerApplicationComponent.this.repoRepositoryProvider, provideDisposableManagerProvider, repoDetailViewModelProvider)); this.repoDetailFragmentMembersInjector = RepoDetailFragment_MembersInjector.create( MainActivitySubcomponentImpl.this.dispatchingAndroidInjectorProvider, provideScopeSubjectProvider, provideDisposableManagerProvider, repoDetailViewModelProvider, repoDetailPresenterProvider); }
  31. DAGGER 2 - SCOPES private void initialize(final RepoDetailComponentBuilder builder) {

    this.provideScopeSubjectProvider = DoubleCheck.provider(ScreenModule_ProvideScopeSubjectFactory.create()); this.provideDisposableManagerProvider = DoubleCheck.provider( ScreenModule_ProvideDisposableManagerFactory.create(provideScopeSubjectProvider)); this.repoDetailViewModelProvider = DoubleCheck.provider(RepoDetailViewModel_Factory.create()); this.bindOwnerProvider = InstanceFactory.create(builder.bindOwner); this.bindRepoNameProvider = InstanceFactory.create(builder.bindRepoName); this.repoDetailPresenterProvider = DoubleCheck.provider( RepoDetailPresenter_Factory.create( bindOwnerProvider, bindRepoNameProvider, DaggerApplicationComponent.this.repoRepositoryProvider, provideDisposableManagerProvider, repoDetailViewModelProvider)); this.repoDetailFragmentMembersInjector = RepoDetailFragment_MembersInjector.create( MainActivitySubcomponentImpl.this.dispatchingAndroidInjectorProvider, provideScopeSubjectProvider, provideDisposableManagerProvider, repoDetailViewModelProvider, repoDetailPresenterProvider); }
  32. DAGGER 2 - SCOPES public final class InstanceFactory<T> implements Factory<T>,

    Lazy<T> { public static <T> Factory<T> create(T instance) { return new InstanceFactory<T>(checkNotNull(instance, "instance cannot be null")); } private final T instance; private InstanceFactory(T instance) { this.instance = instance; } @Override public T get() { return instance; } }
  33. DAGGER 2 - SCOPES private void initialize(final RepoDetailComponentBuilder builder) {

    this.provideScopeSubjectProvider = DoubleCheck.provider(ScreenModule_ProvideScopeSubjectFactory.create()); this.provideDisposableManagerProvider = DoubleCheck.provider( ScreenModule_ProvideDisposableManagerFactory.create(provideScopeSubjectProvider)); this.repoDetailViewModelProvider = DoubleCheck.provider(RepoDetailViewModel_Factory.create()); this.bindOwnerProvider = InstanceFactory.create(builder.bindOwner); this.bindRepoNameProvider = InstanceFactory.create(builder.bindRepoName); this.repoDetailPresenterProvider = DoubleCheck.provider( RepoDetailPresenter_Factory.create( bindOwnerProvider, bindRepoNameProvider, DaggerApplicationComponent.this.repoRepositoryProvider, provideDisposableManagerProvider, repoDetailViewModelProvider)); this.repoDetailFragmentMembersInjector = RepoDetailFragment_MembersInjector.create( MainActivitySubcomponentImpl.this.dispatchingAndroidInjectorProvider, provideScopeSubjectProvider, provideDisposableManagerProvider, repoDetailViewModelProvider, repoDetailPresenterProvider); }
  34. DAGGER 2 - SCOPES public final class RepoDetailPresenter_Factory implements Factory<RepoDetailPresenter>

    { /* Fields */ public RepoDetailPresenter_Factory(…) { /…/ } @Override public RepoDetailPresenter get() { return new RepoDetailPresenter( ownerProvider.get(), repoNameProvider.get(), repoRepositoryProvider.get(), disposableManagerProvider.get(), viewModelProvider.get()); } public static Factory<RepoDetailPresenter> create(…) { return new RepoDetailPresenter_Factory(…); } }
  35. DAGGER 2 - SCOPES public final class RepoDetailPresenter_Factory implements Factory<RepoDetailPresenter>

    { /* Fields */ public RepoDetailPresenter_Factory(…) { /…/ } @Override public RepoDetailPresenter get() { return new RepoDetailPresenter( ownerProvider.get(), repoNameProvider.get(), repoRepositoryProvider.get(), disposableManagerProvider.get(), viewModelProvider.get()); } public static Factory<RepoDetailPresenter> create(…) { return new RepoDetailPresenter_Factory(…); } }
  36. DAGGER 2 - SCOPES public T get() { Object result

    = instance; if (result == UNINITIALIZED) { synchronized (this) { result = instance; if (result == UNINITIALIZED) { result = provider.get(); /* */ instance = result; /* Null out the reference to the provider. We are never going to need it again, so we * can make it eligible for GC. */ provider = null; } } } return (T) result; }