12

I'm working on setting up Dagger 2 into my android project. It is my first time with this framework and everything goes well so far. But I'm seeing different approaches on the way you can set up this framework in your project and I wonder which one is better, because I compare both and for me the result is kind of the same.

I followed this guide: https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2

Searching on Internet all of them use this approach. It use @Module and @Component to define the dependencies.

and your application ends up like this:

public class MyApp extends Application {

    private NetComponent mNetComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        // Dagger%COMPONENT_NAME%
        mNetComponent = DaggerNetComponent.builder()
                // list of modules that are part of this component need to be created here too
                .appModule(new AppModule(this)) // This also corresponds to the name of your module: %component_name%Module
                .netModule(new NetModule("https://api.github.com"))
                .build();

        // If a Dagger 2 component does not have any constructor arguments for any of its modules,
        // then we can use .create() as a shortcut instead:
        //  mNetComponent = com.codepath.dagger.components.DaggerNetComponent.create();
    }

    public NetComponent getNetComponent() {
       return mNetComponent;
    }
}

But I found another way (I haven't tested it):https://google.github.io/dagger/android.html And It looks like completely different, using different classes and annotations. It uses something like this:

@Subcomponent(modules = ...)
 public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
   @Subcomponent.Builder
   public abstract class Builder extends AndroidInjector.Builder<YourActivity> {}
 }

@Module(subcomponents = YourActivitySubcomponent.class)
 abstract class YourActivityModule {
   @Binds
   @IntoMap
   @ActivityKey(YourActivity.class)
   abstract AndroidInjector.Factory<? extends Activity>
       bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder);
 }

 @Component(modules = {..., YourActivityModule.class})
 interface YourApplicationComponent {}

public class YourApplication extends Application implements HasDispatchingActivityInjector {
   @Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

   @Override
   public void onCreate() {
     super.onCreate();
     DaggerYourApplicationComponent.create()
         .inject(this);
   }

   @Override
   public DispatchingAndroidInjector<Activity> activityInjector() {
     return dispatchingActivityInjector;
   }
 }

So, my questions are:

  1. which one is better?

  2. What are the reasons for choosing one approach instead of the other?

David Rawson
  • 20,912
  • 7
  • 88
  • 124
Leandro Ocampo
  • 1,894
  • 18
  • 38
  • Can you elaborate on what is different? – Shmuel Mar 20 '17 at 00:17
  • also, take a look at this https://github.com/googlesamples/android-architecture/tree/todo-mvp-dagger/ in general I'd follow whatever google recommends. They maintain Dagger 2 :D – Shmuel Mar 20 '17 at 00:19
  • I attached the main differences in the code to give a better idea what I'm talking about!! – Leandro Ocampo Mar 20 '17 at 00:44
  • @Shmuel the example repo's code you have given is the same I've written. But what this link (https://google.github.io/dagger/android.html) shows is something different. As you can see that url comes from google too =P. Maybe I'm missing something. – Leandro Ocampo Mar 20 '17 at 00:50
  • 5
    You'll find a dozen different ways that Dagger is implemented all ranging in complexity. The last example looks like is generifying the injection process and using Subcomponents - to be honest I try to use the KISS principle. With that in mind which of the 2 will suit your purpose whilst achieving the same goal. To me the headaches that DI are supposed to solve with this library are being counter balanced by more and more complex implementations of it. – Mark Mar 20 '17 at 00:50

1 Answers1

22

The method of setting up Dagger 2 for Android that is now prescribed in official Dagger 2 documentation has a number of advantages and should be preferred. The advantages are just those elaborated there, namely:

  1. Copy-pasting code makes it hard to refactor later on. As more and more developers copy-paste that block, fewer will know what it actually does.

  2. More fundamentally, it requires the type requesting injection (FrombulationActivity) to know about its injector. Even if this is done through interfaces instead of concrete types, it breaks a core principle of dependency injection: a class shouldn’t know anything about how it isinjected.

Let's apply those reasons to your first example.

Reason 1

Assume we have an Activity that wants to use your NetComponent. Let's call it NetActivity. The onCreate(Bundle savedInstanceState) method of that NetActivity will look something like this:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ((MyApp) getApplicationContext()).getNetComponent().inject(this);
}

This code has all the visual appeal of toenail clippings scattered on oatmeal (not my simile) and will end up copy-pasted in all of the injection site Activities where you use NetComponent. If you use more complicated components, such as this example from the docs:

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  // DO THIS FIRST. Otherwise frombulator might be null!
  ((SomeApplicationBaseType) getContext().getApplicationContext())
     .getApplicationComponent()
     .newActivityComponentBuilder()
     .activity(this)
     .build()
     .inject(this);
 // ... now you can write the exciting code

}

Even worse. It can easily degenerate into a magical piece of code that must be copied and pasted throughout the injection sites. If it changes, it's easy to forget to update just one site and have your app crash.

Reason 2

One of the great advantages of dependency injection is injection sites need not know or care about their injectors, just as dependencies do not know or care about their dependents. To return to ourNetActivity, we have:

((MyApp) getApplicationContext()).getNetComponent().inject(this);

The Activity "knows" about its injector (NetComponent), and the Activity is now coupled with a concretion MyApp and the method getNetComponent() from the same. If either of these classes changes, NetActivity will have to change as well.

The advantages of following the new way of injection inside Activity and Fragments available in Dagger versions 2.10 and forward are just the opposite of these disadvantages:

  1. You end up with less copy-paste code
  2. Types requesting injection no longer have to know or care about their injectors or the sources of their injectors.

Furthermore, as pointed out in this blog, preferring subcomponents over dependent components reduces the method count of your app.

While using subcomponents may initially seem more difficult, there are some clear advantages. However, for the purposes of learning Dagger dependent components may be initially easier to understand. If the second example is too complicated initially, you can graduate to the preferred method when you have gained finesse.

Community
  • 1
  • 1
David Rawson
  • 20,912
  • 7
  • 88
  • 124
  • Hi David. I saw those advantages described in the page. Though the second advantage makes sense, the first one doesn't: "Copy-pasting code makes it hard to refactor later on. As more and more developers copy-paste that block, fewer will know what it actually does." You end up changing this code "((MyApp) getApplication()).getNetComponent().inject(this);" for this one "AndroidInjection.inject(this);" so you have to paste code anyway. – Leandro Ocampo Mar 20 '17 at 12:41
  • @LeandroOcampo you are right there will still be some copy/paste. But with the second approach, not quite as much. Thanks for the accept anyway – David Rawson Mar 21 '17 at 07:15
  • 5
    I think the difference is that people can probably infer what `AndroidInjection.inject(this)` does, because it's kept simple. In the former example, and in the `// DO THIS FIRST` example from the docs, it's a lot more complex, and requires more understanding of what that code does. When things are more complex like that, it means people who do copy/paste it probably won't even make an effort to understand it, and they're only copying it because it's "_what you're supposed to do_". `Injector.inject(this);` is as easy to understand as `super.onCreate()` which we all do without question already. – Joe Aug 22 '17 at 19:48
  • @Joe that's a good point. Do you want to turn it into an answer? – David Rawson Aug 22 '17 at 19:51