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

Intro To Dependency Injection

9ceae595ce38efe3f27af7819559ac92?s=47 Or Bar
September 26, 2016

Intro To Dependency Injection

Dependency Injection continues to grow in popularity due to it's code simplification effects. If you've haven't had a chance to learn and apply DI yet, this introduction is for you.

This talk will cover the theory of "Inversion of Control", introduce Dependency injection, how it can be used within Android to simplify your App's design and help you write more testable code

9ceae595ce38efe3f27af7819559ac92?s=128

Or Bar

September 26, 2016
Tweet

Transcript

  1. Intro To Dependency Injection Or Bar

  2. - What is a Dependency? - Dependency Injection - Dependency

    Inversion Principle - Dependency Injection Frameworks Agenda
  3. A depends on B

  4. public class Example {
 DatabaseHelper mDatabaseHelper;
 
 public Example() {


    mDatabaseHelper = new DatabaseHelper();
 }
 
 public void doStuff() {
 ...
 mDatabaseHelper.loadUsers();
 ...
 }
 }
  5. - Dependency injection is a software design pattern that implements

    inversion of control for resolving dependencies - An injection is the passing of a dependency to a dependent object to use
  6. public class Example {
 DatabaseHelper mDatabaseHelper;
 
 public Example() {


    mDatabaseHelper = new DatabaseHelper();
 }
 }
  7. public class Example {
 DatabaseHelper mDatabaseHelper;
 
 public Example() {


    mDatabaseHelper = new DatabaseHelper();
 }
 } public class Example { 
 DatabaseHelper mDatabaseHelper;
 
 public Example(DatabaseHelper databaseHelper) {
 mDatabaseHelper = databaseHelper;
 }
 }
  8. public class Example {
 DatabaseHelper mDatabaseHelper;
 
 public Example() {


    mDatabaseHelper = new DatabaseHelper();
 }
 } public class Example {
 DatabaseHelper mDatabaseHelper; public Example() {
 mDatabaseHelper = new DatabaseHelper();
 }
 }
 public class Example { 
 DatabaseHelper mDatabaseHelper;
 
 public Example(DatabaseHelper databaseHelper) {
 mDatabaseHelper = databaseHelper;
 }
 } public class Example {
 DatabaseHelper mDatabaseHelper; 
 public Example(DatabaseHelper databaseHelper) {
 mDatabaseHelper = databaseHelper;
 }
 }

  9. public class Example {
 DatabaseHelper mDatabaseHelper;
 
 public Example() {


    }
 
 public void setDatabaseHelper(DatabaseHelper databaseHelper) {
 mDatabaseHelper = databaseHelper;
 }
 }
  10. - Shared dependencies - Configure dependencies externally - Separation of

    modules - Inherent testability Why?
  11. public class ExampleTest {
 @Mock DatabaseHelper mockDatabaseHelper;
 
 @Test
 public

    void testExample_doStuff() {
 Example example = new Example(mockDatabaseHelper);
 example.doStuff();
 mockDatabaseHelper.AssertGetDataWasCalled();
 }
 } public class Example { 
 DatabaseHelper mDatabaseHelper;
 
 public Example(DatabaseHelper databaseHelper) {
 mDatabaseHelper = databaseHelper;
 } public void doStuff() {
 ...
 mDatabaseHelper.loadUsers();
 ...
 }
 }
  12. - High-level modules should not depend on low-level modules. Both

    should depend on abstractions. - Abstractions should not depend on details. Details should depend on abstractions. Dependency Inversion Principle
  13. A depends on B

  14. A is coupled to B

  15. - Bare bones ebook reader - Supports PDF files -

    Prints book contents to screen Ebook Reader
  16. CoolEBookReader PDFReader DisplayPrinter

  17. public class CoolEBookReader {
 public void loadPage(String bookUri, int PageNumber)

    {
 String pdfContent = getPdfContent(bookUri, PageNumber);
 displayPage(pdfContent);
 } }
  18. public class CoolEBookReader {
 enum BookType {
 PDF,
 EPUB
 }


    
 public void loadPage(BookType bookType, String bookUri, int PageNumber) {
 String pageContent;
 if (bookType == BookType.PDF) {
 pageContent = getPdfContent(bookUri, PageNumber);
 } else if (bookType == BookType.EPUB) {
 pageContent = getEpubContent(bookUri, PageNumber);
 } else {
 throw new IllegalArgumentException("Unknown book type");
 }
 displayPage(pageContent); 
 }
 }
  19. public class CoolEBookReader {
 enum BookType {
 PDF,
 EPUB
 


    }
 
 enum PrinterType {
 SCREEN,
 VOICE
 
 }
 
 public void loadPage(BookType bookType, PrinterType printerType, String bookUri, int PageNumber) {
 String pageContent;
 if (bookType == BookType.PDF) {
 pageContent = getPdfContent(bookUri, PageNumber);
 } else if (bookType == BookType.EPUB) {
 pageContent = getEpubContent(bookUri, PageNumber);
 } else {
 throw new IllegalArgumentException("Unknown book type");
 }
 
 if (printerType == PrinterType.SCREEN) {
 displayPage(pageContent);
 } else if (printerType == PrinterType.VOICE) {
 readAloudPage(pageContent);
 } else {
 throw new IllegalArgumentException("Unknown printer type");
 }
 }
 }
  20. - High-level modules should not depend on low-level modules. Both

    should depend on abstractions. - Abstractions should not depend on details. Details should depend on abstractions. Dependency Inversion
  21. CoolEBookReader PDFReader DisplayPrinter <<Interface>> Reader <<Interface>> Printer

  22. interface Reader {
 String read(String bookUri, int pagNumber); }
 


    interface Printer {
 void print(String pageContent);
 } public class CoolEBookReader {
 public void loadPage(Reader reader, Printer printer, String bookUri, int pageNumber) {
 String pageContent = reader.read(bookUri, pageNumber);
 printer.print(pageContent);
 }
 }
  23. interface Reader {
 String read(String bookUri, int pagNumber); }
 


    interface Printer {
 void print(String pageContent);
 } public class CoolEBookReader {
 public void loadPage(Reader reader, Printer printer, String bookUri, int pageNumber) {
 String pageContent = reader.read(bookUri, pageNumber);
 printer.print(pageContent);
 }
 } public class CoolEBookReader {
 public void loadPage(String bookUri, int PageNumber) {
 String pdfContent = getPdfContent(bookUri, PageNumber);
 displayPage(pdfContent);
 } }
  24. public class CoolEBookReaderTest {
 @Mock
 Reader mockReader;
 
 @Mock
 CoolEBookReader.Printer

    mockPrinter;
 
 @Test 
 public void TestExample_loadPage() {
 CoolEBookReader coolEBookReader = new CoolEBookReader();
 coolEBookReader.loadPage(mockReader, mockPrinter, "", 1);
 // Assert Reader.read() is called
 // Assert Printer.print() is called
 }
 }
  25. - Dependency Injection - Dependency Inversion - Where are these

    dependencies come from What’s next?
  26. - Handles object creation - Reduces boilerplate code - A

    central location for organizing dependencies - Implement common patterns natively (singleton, lazy loading, etc) Why use a framework?
  27. - Dagger 1 - Dagger 2 - Guice and RoboGuice

    - PicoContainer - Spring - Many, many, many, many more options Common Frameworks
  28. History lesson of DI on Android - RoboGuice - 2009

    - Spring for Android - 2012 - Dagger 1 - 2013 - Dagger 2 - 2015 - Tiger - 2016
  29. - @javax.inject.Inject - @Module - @Provides - @Component Dagger uses

    annotations
  30. public class ConnectionHelper {
 private Context mContext;
 
 public ConnectionHelper(Context

    context) {
 mContext = context;
 }
 
 public boolean isConnected() {
 ConnectivityManager connectivityManager =
 (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
 return connectivityManager.getActiveNetworkInfo().isConnected();
 }
 } ConnectionHelper connectionHelper = new ConnectionHelper(context);
 
 boolean isConnected = connectionHelper.isConnected();
  31. public class ConnectionHelper {
 private Context mContext;
 
 @Inject
 public

    ConnectionHelper(Context context) {
 mContext = context;
 }
 
 public boolean isConnected() {
 ConnectivityManager connectivityManager =
 (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
 
 return connectivityManager.getActiveNetworkInfo().isConnected();
 }
 } @Inject
  32. @Module
 public class AppModule {
 
 } @Module

  33. @Provides @Module
 public class AppModule {
 private final Context mContext;


    
 public AppModule(Context context) {
 mContext = context;
 } @Provides
 public Context providesContext() {
 return mContext;
 } @Provides
 public ConnectionHelper providesConnectionHelper(Context context) {
 return new ConnectionHelper(context);
 }
 }
  34. @Component(modules = {AppModule.class})
 public interface AppComponent {
 
 void inject(MainActivity

    activity);
 } @Component
  35. public class App extends Application {
 private AppComponent component;
 @Override


    public void onCreate() {
 super.onCreate();
 
 AppComponent component = DaggerAppComponent.builder()
 .appModule(new AppModule(this))
 .build();
 }
 
 public AppComponent getComponent() {
 return component;
 }
 } public class App extends Application {
 private AppComponent component;
 @Override
 public void onCreate() {
 super.onCreate();
 
 AppComponent component = DaggerAppComponent.builder()
 .appModule(new AppModule(this))
 .build();
 }
 
 public AppComponent getComponent() {
 return component;
 }
 }
  36. public class MainActivity extends AppCompatActivity {
 
 @Inject
 ConnectionHelper mConnectionHelper;


    
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 ((App) getApplication()).getComponent().inject(this);
 
 boolean isConnected = mConnectionHelper.isConnected();
 
 textView.setText(isConnected ? "Connected" : "Not Connected");
 }
 } public class MainActivity extends AppCompatActivity {
 
 @Inject
 ConnectionHelper mConnectionHelper;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 ((App) getApplication()).getComponent().inject(this);
 
 boolean isConnected = mConnectionHelper.isConnected();
 
 textView.setText(isConnected ? "Connected" : "Not Connected");
 }
 } @Inject - again
  37. - More annotations - Scopes - Lazy injections - others

    Fun stuff
  38. @Provides
 @Singleton
 public Context providesContext() {
 return context;
 } @Singleton

    @Singleton
 @Component(modules = {AppModule.class})
 public interface AppComponent {
 
 void inject(MainActivity activity); }
  39. @Provides
 @Reusable
 public ConnectionHelper providesConnectionHelper(Context context) {
 return new ConnectionHelper(context);


    } @Reusable @Singleton @Reusable
 @Component(modules = {AppModule.class})
 public interface AppComponent {}
  40. @Inject
 Lazy<ConnectionHelper> mConnectionHelper;
 ... mConnectionHelper.get().isConnected(); Lazy and Provider Injections @Inject


    Provider<ConnectionHelper> mConnectionHelper;
 ... mConnectionHelper.get().isConnected();
  41. @Target(ANNOTATION_TYPE)
 @Retention(RUNTIME)
 @Documented
 public @interface Qualifier {} @Qualifier @Qualifier
 @Documented


    @Retention(RUNTIME)
 public @interface Named {}
  42. @Provides @Singleton @Named(“Adapter 1”)
 public RestAdapter providesRestAdapter1() { return …;

    } @Qualifier @Provides @Singleton @Named(“Adapter 2”) public RestAdapter providesRestAdapter2() { return …; }
  43. @Qualifier @Inject @Named(“Adapter 1”) RestAdapter mRestAdapter;


  44. Subcomponents and Scopes - Subcomponents allow you to combine different

    modules at runtime - Scopes let you define the lifetime of a dependency
  45. Testing and dagger - Constructors used for injections can be

    used for testing - Test Modules for espresso
  46. Questions - @or_bar - orbar@tumblr.com - orbar1.tumblr.com