Android DevConference - Dagger 2: uso avançado em projetos Android

81 views

Published on

Rafael Toledo, Concrete Solutions, fala sobre Dagger 2: uso avançado em projetos Android no Android DevConference 2016. Saiba mais em: http://androidconference.com.br/

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
81
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Android DevConference - Dagger 2: uso avançado em projetos Android

  1. 1. Dagger 2 uso avançado em projetos Android
  2. 2. DI - Dependency Injection Não crie, peça Dependa de abstrações, não dependa de classes concretas
  3. 3. Abstração x Concreto
  4. 4. Abstração x Concreto
  5. 5. AVISO! Cuidado com a quantidade de interfaces!
  6. 6. Dagger 2 Qualifiers
  7. 7. Qualifiers - por quê? Diferenciação de dependências de mesmo tipo / classe
  8. 8. Qualifiers - como? @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface ApiUrl { }
  9. 9. @Module public class QualifiedModule { @Provides @ApiUrl public String provideApiUrl() { return "https://api.stackexchange.com/2.2/"; } @Provides public String provideUnqualified() { return "Android!"; } }
  10. 10. public class InjectedClass { @Inject String value; @Inject @ApiUrl String url; ...
  11. 11. @Module public class AnotherModule { @Provides public MyClass provideClass(String value, @ApiUrl String url) { return new MyClass(value, url); } ...
  12. 12. Qualifiers - extras @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Named { String value() default ""; }
  13. 13. @Module public class QualifiedModule { @Provides @Named("One") public String provideV1() { return "one!"; } @Provides @Named("Two") public String provideV2() { return "two!"; } }
  14. 14. public class InjectedClass { @Inject @Named("One") String value1; // one! @Inject @Named("Two") String value2; // two! ...
  15. 15. Qualifiers - extras @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface ComplexQualifier { String value() default ""; int ordinal() default 1; }
  16. 16. Dagger 2 Custom Scopes
  17. 17. Custom Scopes - por quê? Ciclo de vida de um objeto dentro de um escopo É uma espécie de “Singleton local”
  18. 18. Custom Scopes - como? @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { }
  19. 19. @Module public class ScopedModule { @Provides @ActivityScope public MyObject provideDependency() { // Singleton on this scope } @Provides public MyOtherObject provideOtherDependency() { // Each request, a new object } }
  20. 20. Custom Scopes - como? @ActivityScope @SubComponent(modules = ScopedModule.class) public interface ActivityComponent { ... }
  21. 21. Custom Scopes - como? @Singleton @Component(...) public interface MainComponent { ActivityComponent plus(); }
  22. 22. Custom Scopes - como? @Singleton @Component(...) public interface MainComponent { ActivityComponent plus(); // Shared dependency Application getApplication(); }
  23. 23. Custom Scopes - como? @Singleton @Component(...) public interface MainComponent { // If module has no default constructor ActivityComponent plus(CustomModule m); }
  24. 24. Custom Scopes - como? ActivityComponent activityComponent = mainComponent.plus(); // ou ActivityComponent activityComponent = mainComponent.plus(new MyModule(ctx));
  25. 25. Custom Scopes - importante É importante sempre gerenciar o ciclo de vida do componente de acordo com o escopo desejado para evitar leaks Explicitamente: activityComponent = null;
  26. 26. Pro Tip: Escopo @Reusable
  27. 27. Escopo @Reusable Otimização para dependências que podem ser reutilizadas mas não precisam necessariamente ser a mesma instância. Muito útil para Utils e Helpers Disponível a partir da versão 2.3
  28. 28. Dagger 2 Multibindings
  29. 29. Multibindings - por quê? Dependências que só fazem sentido juntas São entregues juntas dentro de uma collection - Map ou Set
  30. 30. @Module public class MultibindingsSetModule { @Provides @IntoSet public String provideFirstValue() { return "One!"; } @Provides @IntoSet public String provideSecondValue() { return "Two!"; } }
  31. 31. public class InjectedClass { @Inject Set<String> values; // One!, Two! ...
  32. 32. @Module public class MultibindingsMapModule { @Provides @IntoMap @StringKey("key1") public String provideFirstValue() { return "One!"; } @Provides @IntoMap @StringKey("key2") public String provideSecondValue() { return "Two!"; }
  33. 33. public class InjectedClass { @Inject Map<String, String> values; // key1 => One! // key2 =: Two! ...
  34. 34. Multibindings - Maps As chaves podem ser: IntKey LongKey StringKey ClassKey
  35. 35. Multibindings - Dica Caso a coleção possa, por algum motivo, ficar vazia, precisamos de um módulo abstrato declarando a coleção. @Module public abstract class MyModule { @Multibinds public abstract Set<String> possiblyEmpty(); }
  36. 36. Dagger 2 Binds
  37. 37. Binds - por quê? Servem para ligar interfaces a implementações dentro de um módulo abstrato É um excelente aliado na criação de módulos de teste e na substituição de objetos reais por mocks
  38. 38. @Module public abstract class BindModule { @Binds abstract Bluetooth provideBluetooth(BluetoothImpl impl); ... } @Module public class ConcreteModule { @Provides BluetoothImpl provideBluetoothImpl() { return new BluetoothImpl(); } }
  39. 39. @Module public abstract class BindTestModule { @Binds abstract Bluetooth provideBluetooth(BluetoothMock mock); ... } @Module public class ConcreteTestModule { @Provides BluetoothMock provideBluetoothMock() { return new BluetoothMock(); } }
  40. 40. Dagger 2 Producers
  41. 41. Producers - por quê? São praticamente uma biblioteca à parte - de fato, são uma dependência à parte :) É uma API para a injeção de dependência de forma assíncrona, ideal para dependências muito pesadas ou de inicialização lenta.
  42. 42. Producers - avisos Não segue o padrão da JSR-330 - não usa anotações @Inject, por exemplo Adiciona o Guava ao classpath do projeto, o que pode engordar o APK e contribuir para aumento do número de métodos… #MultidexDanger
  43. 43. Producers - passo 1 (Executor) @Module public class ExecutorModule { @Provides @Production public Executor provideExecutor() { return Executors.newCachedThreadPool(); } }
  44. 44. Producers - passo 2 (ProducerModules) @ProducerModule public class MyProducerModule { @Produces public HeavyDep produceHeavyDep() { // Sempre Singleton return new HeavyDep(); } }
  45. 45. Producers - passo 3 (ProductionComponent) @ProductionComponent( modules = { MyProducerModule.class, ExecutorModule.class } ) public interface ProducerComponent { ListenableFuture<HeavyDep> heavyDep(); }
  46. 46. Producers - passo 4 ProducerComponent component = ... Futures.addCallback(component.heavyDep(), new FutureCallback() { @Override public void onSuccess(HeavyDep dep) {} @Override public void onFailure(Throwable t) {} });
  47. 47. Producers - observações Os Production Components podem depender de Components convencionais, e podem conter módulos que fornecem dependências pela anotação @Provides
  48. 48. Dagger 2 Testing Tricks
  49. 49. Trick #1 - Só APT compile 'com.google.dagger:dagger:2.6.1' androidTestApt 'com.google.dagger:dagger-compiler:2.6.1' testApt 'com.google.dagger:dagger-compiler:2.6.1'
  50. 50. Trick #2 - Facilidade de Mock Crie o módulo recebendo as dependências mockadas no construtor. Assim, fica mais fácil controlar o objeto dentro de um teste!
  51. 51. @Module public class ConcreteTestModule { private BluetoothMock btMock; public ConcreteTestModule(BluetoothMock mock) { this.btMock = mock; } @Provides public Bluetooth provideBluetoothMock() { return btMock; } }
  52. 52. @Before public void setUp() { Bluetooth bt = mock(Bluetooth.class); MyComponent testComponent = DaggerTestComponent.builder() .concreteTestModule(new ConcreteTestModule(bt)) .build(); // RuntimeEnvironment.application // ou // InstrumentationRegistry.getTargetContext() // .getApplicationContext() app.setComponent(testComponent); }
  53. 53. Mais Dagger 2 google.github.io/dagger frogermcs.github.io medium.com/android-dev-br Slack #dagger
  54. 54. www.concretesolutions.com.br Rio de Janeiro – Rua São José, 90 – cj. 2121 Centro – (21) 2240-2030 São Paulo - Av. Nações Unidas, 11.541 3º andar - Brooklin - (11) 4119-0449 Ajudamos empresas a criar produtos digitais de sucesso @_rafaeltoledo www.rafaeltoledo.net blog.concretesolutions.com.br concretesolutions.com.br/carreira

×