Visualizando na prática as condições e componentes que nos auxiliam a ter uma estrutura de injeção de dependência sólida e robusta, fazendo uma avaliação de problemas comuns em desenvolvimento android.
val currentTheme: Int, ) : MyContract.View { lateinit var presenter: MyContract.Presenter // … } Dependemos de um tipo nativo E de uma Interface Que precisa ser injetada a posteriori
uma abstração? interface ChessPlayer class RookieChessPlayer : ChessPlayer class GMChessPlayer : ChessPlayer E se nossa dependência for em uma classe que não possuímos ou em um tipo primitivo? interface GithubApi // Gerado pelo Retrofit val targetCluster: String // Tipo primitivo
uma abstração? interface ChessPlayer class RookieChessPlayer : ChessPlayer class GMChessPlayer : ChessPlayer E se nossa dependência for em uma classe que não possuímos ou em um tipo primitivo? interface GithubApi // Gerado pelo Retrofit val targetCluster: String // Tipo primitivo E se o consumidor da nossa classe não for nosso? class MessageWorker : Worker // Usado por WorkManager class HomeViewModel : ViewModel // Mantido via provider
uma instância? val featuraAEnabled: Boolean val featureBEnabled: Boolean E se quisermos ter mais controle sobre o ciclo de vida das instâncias de nossas dependências? Referências reutilizáveis e fracas, ou fortes em escopo específico
uma instância? val featuraAEnabled: Boolean val featureBEnabled: Boolean E se quisermos ter mais controle sobre o ciclo de vida das instâncias de nossas dependências? Referências reutilizáveis e fracas, ou fortes em escopo específico Resolução de dependência após construção
uma instância? val featuraAEnabled: Boolean val featureBEnabled: Boolean E se quisermos ter mais controle sobre o ciclo de vida das instâncias de nossas dependências? Referências reutilizáveis e fracas, ou fortes em escopo específico Resolução de dependência após construção E como lidaríamos com tipos genéricos? val versions: List<Int> val children: List<Node>
pra gerenciar essas instâncias para nós? class HomeViewModel interface BackendApi class MessagingManager class ServiceProvider : Provider val featureAEnabled: Boolean class ConfigFactory<T>
pra gerenciar essas instâncias para nós? class HomeViewModel interface BackendApi class MessagingManager val featureAEnabled: Boolean class ConfigFactory<T> class ServiceProvider : Provider
pra gerenciar essas instâncias para nós? a. É capaz de prover instância para todo objeto que possa ser referenciado no código, ou para seus providers* *Mais sobre isto adiante
val retrofit = Retrofit.Builder() .baseUrl(“https:$//chess.com/”) .client(okHttpClient) .build() val boardPrinter: BoardPrinter = GLCompletePrinter() val debugPrinter: BoardPrinter = MinimalPrinter() val availablePrinters: Map<String, BoardPrinter> = mapOf( “DEFAULT” to boardPrinter, “DEBUG” to debugPrinter )
val retrofit = Retrofit.Builder() .baseUrl(“https:$//chess.com/”) .client(okHttpClient) .build() val boardPrinter: BoardPrinter = GLCompletePrinter() val debugPrinter: BoardPrinter = MinimalPrinter() val availablePrinters: Map<String, BoardPrinter> = mapOf( “DEFAULT” to boardPrinter, “DEBUG” to debugPrinter ) val versionName: String = “BLUE-Kasparov”
pra gerenciar essas instâncias para nós? a. É capaz de prover instância para todo objeto que possa ser referenciado no código, ou para seus providers* b. É capaz de controlar o ciclo de vida de suas referências* *Mais sobre isto adiante
pra gerenciar essas instâncias para nós? a. É capaz de prover instância para todo objeto que possa ser referenciado no código, ou para seus providers* b. É capaz de controlar o ciclo de vida de suas referências* *Mais sobre isto adiante
ViewModel AAC Activity ViewModelStore ViewModel Factory ViewModelProvider Criado por Cria instância Guarda instância Provê instância Esta estrutura permite que uma ViewModel sobreviva a mudanças de configuração
{ val myClass = Injector.myClass override fun onCreate(savedInstanceState: Bundle) { … } } val viewModel by lazy { ViewModelProvider(viewModelStore, viewModelFactory) .get(MainViewModel ::class.java) } viewModelFactory De onde vem??
homeUseCase: HomeUseCase ) : ViewModel() { fun foo(): LiveData<Bar> { … } } override fun <T : ViewModel?> create(modelClass: Class<T>): T { return MainViewModel(homeUseCase) as T } } : ViewModelProvider.Factory { ( private val homeUseCase: HomeUseCase ) class MainViewModelFactory Isso não escala bem
homeUseCase: HomeUseCase ) : ViewModel() { fun foo(): LiveData<Bar> { … } } override fun <T : ViewModel?> create(modelClass: Class<T>): T { } } ) : ViewModelProvider.Factory { class MainViewModelFactory( val viewModelMap: Map<Class<out ViewModel>, ViewModel> return viewModelMap[modelClass] as T Mas peraê… ainda há um problema aqui!
gerenciar essas instâncias para nós, mas que temos que separar o gerenciamento da declaração. class HomeViewModel interface BackendApi class MessagingManager class ServiceProvider : Provider val featureAEnabled: Boolean class ConfigFactory<T>
gerenciar essas instâncias para nós, mas que temos que separar o gerenciamento da declaração. class HomeViewModel interface BackendApi class MessagingManager class ServiceProvider : Provider val featureAEnabled: Boolean class ConfigFactory<T>
gerenciar essas instâncias para nós, mas que temos que separar o gerenciamento da declaração. class HomeViewModel interface BackendApi class MessagingManager class ServiceProvider : Provider val featureAEnabled: Boolean class ConfigFactory<T>
gerenciar essas instâncias para nós, mas que temos que separar o gerenciamento da declaração. class HomeViewModel interface BackendApi class MessagingManager class ServiceProvider : Provider val featureAEnabled: Boolean class ConfigFactory<T>
E como expomos as dependências? val calculator = calculatorModule.calculator() val homeUseCase get() = domainModule.homeUseCase() val vmFactoryMap = mapOf( MainViewModel ::class.java to { mainModule.mainViewModel(homeUseCase) } ) fun viewModelProvider( storeOwner: ViewModelStoreOwner ): ViewModelProvider { return baseViewModelModule.viewModelProvider( storeOwner, baseViewModelModule.viewModelFactory(vmFactoryMap)
E como expomos as dependências? val calculator = calculatorModule.calculator() val homeUseCase get() = domainModule.homeUseCase() val vmFactoryMap = mapOf( MainViewModel ::class.java to { mainModule.mainViewModel(homeUseCase) } ) fun viewModelProvider( storeOwner: ViewModelStoreOwner ): ViewModelProvider { return baseViewModelModule.viewModelProvider( storeOwner, baseViewModelModule.viewModelFactory(vmFactoryMap) { mainModule.mainViewModel(homeUseCase) }
E como expomos as dependências? val calculator = calculatorModule.calculator() val homeUseCase get() = domainModule.homeUseCase() val vmFactoryMap = mapOf( MainViewModel ::class.java to { mainModule.mainViewModel(homeUseCase) } ) fun viewModelProvider( storeOwner: ViewModelStoreOwner ): ViewModelProvider { return baseViewModelModule.viewModelProvider( storeOwner, baseViewModelModule.viewModelFactory(vmFactoryMap) Ainda dá pra melhorar { mainModule.mainViewModel(homeUseCase) }
não deve gerenciar nossas instâncias, apenas fazer seu provisionamento, injetá-las. class HomeViewModel interface BackendApi class MessagingManager class ServiceProvider : Provider val featureAEnabled: Boolean class ConfigFactory<T>
não deve gerenciar nossas instâncias, apenas fazer seu provisionamento, injetá-las. class HomeViewModel interface BackendApi class MessagingManager class ServiceProvider : Provider val featureAEnabled: Boolean class ConfigFactory<T>
não deve gerenciar nossas instâncias, apenas fazer seu provisionamento, injetá-las. class HomeViewModel interface BackendApi class MessagingManager class ServiceProvider : Provider val featureAEnabled: Boolean class ConfigFactory<T> Estamos falando de Providers
T } class MapProviderFactory<K, out V>( val contributingMap: Map<K, Provider<V >> ) : Provider<Map<K, Provider<V >>> { override fun get() = contributingMap }
T } class MainViewModel_Provider( val mainModule: MainModule, val homeUseCaseProvider: Provider<HomeUseCase> ) : Provider<MainViewModel> { override fun get() = mainModule.mainViewModel( homeUseCaseProvider.get()) }
T } class MainViewModel_Provider( val mainModule: MainModule, val homeUseCaseProvider: Provider<HomeUseCase> ) : Provider<MainViewModel> { override fun get() = mainModule.mainViewModel( homeUseCaseProvider.get()) } val vmMapProvider = MapProviderFactory(mapOf( MainViewModel ::class.java to mainViewModelProvider ))
T } class ViewModelFactory_Provider( val module: BaseViewModelModule, val vmMapProvider: Provider<Map<Class<out ViewModel>, ) : Provider<ViewModelProvider.Factory> { override fun get() = module.viewModelFactory( vmMapProvider.get()) } Provider<ViewModel >>>
T } class ViewModelProvider_Provider( val module: BaseViewModelModule, val vmFactoryProvider: Provider<ViewModelProvider.Factory>, val vmStoreOwnerProvider: Provider<ViewModelStoreOwner> ) : Provider<ViewModelProvider> { override fun get() = baseViewModelModule.viewModelProvider( vmStoreOwnerProvider.get() vmFactoryProvider.get() ) }
T } class ViewModelProvider_Provider( val module: BaseViewModelModule, val vmFactoryProvider: Provider<ViewModelProvider.Factory>, val vmStoreOwnerProvider: Provider<ViewModelStoreOwner> ) : Provider<ViewModelProvider> { override fun get() = baseViewModelModule.viewModelProvider( vmStoreOwnerProvider.get() vmFactoryProvider.get() ) } val vmStoreOwnerProvider: Provider<ViewModelStoreOwner>
T } class ViewModelProvider_Provider( val module: BaseViewModelModule, val vmFactoryProvider: Provider<ViewModelProvider.Factory>, ) : Provider<ViewModelProvider> { override fun get() = baseViewModelModule.viewModelProvider( vmStoreOwnerProvider.get() vmFactoryProvider.get() ) } val vmStoreOwnerProvider: Provider<ViewModelStoreOwner>
T } class ViewModelProvider_Provider( val module: BaseViewModelModule, val vmFactoryProvider: Provider<ViewModelProvider.Factory>, ) : Provider<ViewModelProvider> { override fun get() = baseViewModelModule.viewModelProvider( vmStoreOwnerProvider.get() vmFactoryProvider.get() ) } val vmStoreOwnerProvider: ViewModelStoreOwner
domainModule = DomainModule() val baseViewModelModule = BaseViewModelModule() val mainModule = MainModule() val providerHomeUseCase = HomeUseCase_Provider( domainModule) val providerMainViewModel = MainViewModel_Provider( mainModule, providerHomeUseCase val providerVmMap = MapProviderFactory(mapOf( MainViewModel$::class.java to providerMainViewModel)) val providerVmFactory = ViewModelFactory_Provider( baseViewModelModule, providerVmMap)
domainModule = DomainModule() val baseViewModelModule = BaseViewModelModule() val mainModule = MainModule() val providerHomeUseCase = HomeUseCase_Provider( domainModule) val providerMainViewModel = MainViewModel_Provider( mainModule, providerHomeUseCase val providerVmMap = MapProviderFactory(mapOf( MainViewModel$::class.java to providerMainViewModel)) val providerVmFactory = ViewModelFactory_Provider( baseViewModelModule, providerVmMap)
domainModule = DomainModule() val baseViewModelModule = BaseViewModelModule() val mainModule = MainModule() val providerHomeUseCase = HomeUseCase_Provider( domainModule) val providerMainViewModel = MainViewModel_Provider( mainModule, providerHomeUseCase val providerVmMap = MapProviderFactory(mapOf( MainViewModel$::class.java to providerMainViewModel)) val providerVmFactory = ViewModelFactory_Provider( baseViewModelModule, providerVmMap)
de injeção de dependências. Abordamos três papéis fundamentais nessa estrutura: - Raiz de Composição (incorporado por nosso Injector) - Catalogar como construir instâncias (através de Modules) - Criar e guardar instâncias (através de Providers) CompositionRoot Module Module Module Provider Provider Provider BaseViewModelModule Provider<ViewModelFactory> Provider<ViewModelProvider>
pra funcionar, logo é sensato delegar este trabalho a uma framework. i.e.: Koin, Dagger, Katana, Kodein… Existem diferenças entre a implementação de cada framework, mas a base estrutural é a mesma. i.e.: Os providers do Koin (chamados de Beans), ao invés de receberem modules e outros providers, recebem a raiz da composição diretamente, enquanto os Providers do Dagger funcionam como vimos.
pra funcionar, logo é sensato delegar este trabalho a uma framework. i.e.: Koin, Dagger, Katana, Kodein… Existem diferenças entre a implementação de cada framework, mas a base estrutural é a mesma. i.e.: Os providers do Koin (chamados de Beans), ao invés de receberem modules e outros providers, recebem a raiz da composição diretamente, enquanto os Providers do Dagger funcionam como vimos. Não apenas agora vimos como esses frameworks funcionam, agora podemos usar isto em nosso favor.
em galhos focados em partes específicas do sistema. Podemos tentar entender como injetar dependências em nossas activities sem ter de buscar explicitamente na raiz de composição
em galhos focados em partes específicas do sistema. Weak references - Dependências que podem ser reutilizadas porém são facilmente coletadas pelo GC quando for necessário. Podemos tentar entender como injetar dependências em nossas activities sem ter de buscar explicitamente na raiz de composição
em galhos focados em partes específicas do sistema. Weak references - Dependências que podem ser reutilizadas porém são facilmente coletadas pelo GC quando for necessário. Assisted injection - Quando uma classe tem dependências em mais de uma origem além de nossa raiz de composição. (WorkManager) Podemos tentar entender como injetar dependências em nossas activities sem ter de buscar explicitamente na raiz de composição
em galhos focados em partes específicas do sistema. Weak references - Dependências que podem ser reutilizadas porém são facilmente coletadas pelo GC quando for necessário. Assisted injection - Quando uma classe tem dependências em mais de uma origem além de nossa raiz de composição. (WorkManager) Entender bem as nuances e poderes do seu framework preferido e começar a destravar os seus poderes especiais a seu favor. Podemos tentar entender como injetar dependências em nossas activities sem ter de buscar explicitamente na raiz de composição
DoubleCheck … Scopes, por Garima Jain https://proandroiddev.com/dagger-2-check-singlecheck-doublecheck-scopes-4ee48fc31736 Projeto KOIN Core (e também o projeto ViewModel) https://github.com/InsertKoinIO/koin/tree/master/koin-projects/koin-core Dagger 2 on Android: Assisted Injection por Fred Porcíuncula, com talk de Jake Wharton na droidCon 2018 https://proandroiddev.com/dagger-assisted-injection-2002885b3cba That Missing Guide: How to use Dagger2 por Gabor Varadi https://medium.com/@Zhuinden/that-missing-guide-how-to-use-dagger2-ef116fbea97
O M U N D O . RIO SÃO PAULO BH RECIFE Centro Av. Presidente Wilson, 231 29º andar (21) 2240-2030 Cidade Monções Av. Nações Unidas, 11.541 3º andar (11) 4119-0449 Savassi Av. Getúlio Vargas, 671 Sala 800 - 8º andar (31) 3360-8900 Ilha do Leite Rua Sen. José Henrique, 199 2º andar (81) 3018-6299 W W W. C O N C R E T E . C O M . B R