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

Understanding Dependency Injection in Android

Understanding Dependency Injection in Android

Dependency injection (DI) frameworks became quite popular among Android developers in the past few years. Why is that? What are the concepts behind those frameworks and why were they built in the first place? What are the (real) advantages of using a DI framework? Can we have DI without those frameworks? This talk aims to answer those questions by discussing the foundation of Android architecture and the common mistakes that Android developers make when using DI frameworks.

Marcelo Benites

October 18, 2017
Tweet

More Decks by Marcelo Benites

Other Decks in Programming

Transcript

  1. Constructor public class Car { private final Engine engine; public

    Car(Engine engine) { this.engine = engine; } public void start() { engine.turnOn(); } public void stop() { engine.turnOff(); } }
  2. Constructor • Engine instantiation is delegated to another class. •

    Car behavior can be changed without changing its implementation. • We can have GasEngine or ElectricEngine if we make Engine an interface. • We can easily mock Engine or create a StubEngine for testing purposes. public class Car { private final Engine engine; public Car(Engine engine) { this.engine = engine; } public void start() { engine.turnOn(); } public void stop() { engine.turnOff(); } }
  3. Limitations • No access to Android components' constructors. • No

    way to inject dependencies in Activity, Service, BroadcastReceiver, ContentProvider or Application.
  4. Android Design • Android OS and third party applications can

    access components you choose to export. • Every application have its own Process. • Overhead: Inter-process Communication (IPC). • Parcel and Binder help ease the burden of IPC. • Even when communicating between components within your own application Binder is used under the hood.
  5. • User taps application icon. • Process is created for

    application. • Application component is instantiated. • Application onCreate is called. • Launcher Activity component is instantiated. • Activity onCreate is called.
  6. Application's onCreate method is the entry point of an Android

    application just like main method is the entry point of a Java application.
  7. public class CarActivity extends AppCompatActivity { private Car car; @Override

    protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_car); car = new Car(new ElectricEngine()); } } How can we inject Car in CarActivity?
  8. public class CarActivity extends AppCompatActivity { private Car car; @Override

    protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_car); car = ((CarApplication)getApplication()).getCar(); } } How can we inject Car in CarActivity?
  9. Configuration Changes • Now Car is independent of CarActivity lifecycle.

    • The state of Car will be kept across configuration changes (such as screen orientation and language).
  10. View State x Model State • User changes Engine state

    to on. • A screen orientation change occurs. • It is CarActivity's responsibility to save and restore the state of its views. • Once the user taps the confirmation button Car's state is changed and it will be kept irrespective of CarActivity's lifecycle. • Car's (model) state and CarActivity's (view) state are different although sometimes they can be similar.
  11. public class CarService extends Service { ... @Override public void

    onCreate() { super.onCreate(); car = ((ApplicationComponent) getApplication()).getCar(); timer = new Timer(); updateCarTask = new TimerTask() { @Override public void run() { if (car.isStarted()) { car.stop(); } else { car.start(); } } }; timer.schedule(updateCarTask, 0, 15000); }...
  12. public class Car extends Observable {... public void start() {

    engine.turnOn(); setChanged(); notifyObservers(); } public void stop() { engine.turnOff(); setChanged(); notifyObservers(); }... }
  13. public class CarActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable

    Bundle savedInstanceState) {... carObserver = new Observer() { @Override public void update(final Observable observable, Object args) { updateView(); } }; car.addObserver(carObserver); } private void updateView() { engineSpinner.setSelection(car.isStarted()? 0: 1); }...
  14. Activity - Service Communication • Easy communication between Service and

    Activity, • Easily replicated to BroadcastReceiver. ◦ Car may change due to a system or third party application event.
  15. Real-World Application • Multiple and complex classes in model layer.

    ◦ Car, Pilot, Engine, Garage, CarPersistence, CarService, CarManager and so on... • Persistency. • Networking. • Memory Constraints • Performance • Multiple Processes
  16. Dependency Injection Frameworks • Reduce boilerplate code. • Standardize object

    creation. • Ease the replacement of dependencies for Functional (UI) Testing. • Ease object's lifecycle management. (Dagger Scopes) ◦ Should NOT be used for business logic. • Can NOT completely replace Factories, Builders etc..