Android Dagger for Professionals Tutorial

By: TechYourChance

40   4   2093

Uploaded on 07/15/2017

This tutorial demonstrates how to implement Dependency Injection architectural pattern in Android using Dagger 2 dependency injection library.

Source code of the template application: https://github.com/techyourchance/android_application_template

The notes taken during this tutorial: https://www.techyourchance.com/advanced-dagger-tutorial/

Resources referenced in this video:
Dependency Injection in Android (blog post): https://www.techyourchance.com/dependency-injection-android/
Dagger 2 Tutorial (blog post): https://www.techyourchance.com/dagger-tutorial/
Liskov Substitution Principle and Context (blog post): https://www.techyourchance.com/liskov-substitution-principle/
MVP and MVC Architectures in Android (blog post): https://www.techyourchance.com/mvp-mvc-android-1/

Comments (5):

By anonymous    2017-09-20

Declaring a separate module for each Activity is not a good idea at all. Declaring separate component for each Activity is even worse. The reasoning behind this is very simple - you don't really need all these module/components (as you have already seen by yourself).

However, having just one component that is tied to Application's life-cycle and using it for injection into all Activities is also not the optimal solution (this is your friend's approach). It is not optimal because:

  1. It restricts you to just one scope (@Singleton or a custom one)
  2. The only scope you're restricted to makes the injected objects "application singletons", therefore mistakes in scoping or incorrect usage of scoped objects can easily cause global memory leaks
  3. You'll want to use Dagger2 in order to inject into Services too, but Services can require different objects than Activities (e.g. Services don't need presenters, don't have FragmentManager, etc.). By using a single component you loose the flexibility of defining different object graphs for different components.

So, a component per Activity is an overkill, but single component for the entire application is not flexible enough. The optimal solution is in between these extremes (as it usually is).

I use the following approach:

  1. Single "application" component that provides "global" objects (e.g. objects that hold global state which is shared between all components in the application). Instantiated in Application.
  2. "Controller" subcomponent of "application" component that provides objects which are required by all user-facing "controllers" (in my architecture these are Activities and Fragments). Instantiated in each Activity and Fragment.
  3. "Service" subcomponent of "application" component that provides objects which are required by all Services. Instantiated in each Service.

Following is an example of how you could implement the same approach.


Edit July 2017

I published a video tutorial that shows how to structure Dagger dependency injection code in Android application: Android Dagger for Professionals Tutorial.


Application scope:

@ApplicationScope
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {

    // Each subcomponent can depend on more than one module
    ControllerComponent newControllerComponent(ControllerModule module);
    ServiceComponent newServiceComponent(ServiceModule module);

}


@Module
public class ApplicationModule {

    private final Application mApplication;

    public ApplicationModule(Application application) {
        mApplication = application;
    }

    @Provides
    @ApplicationScope
    Application applicationContext() {
        return mApplication;
    }

    @Provides
    @ApplicationScope
    SharedPreferences sharedPreferences() {
        return mApplication.getSharedPreferences(Constants.PREFERENCES_FILE, Context.MODE_PRIVATE);
    }

    @Provides
    @ApplicationScope
    SettingsManager settingsManager(SharedPreferences sharedPreferences) {
        return new SettingsManager(sharedPreferences);
    }
}

Controller scope:

@ControllerScope
@Subcomponent(modules = {ControllerModule.class})
public interface ControllerComponent {

    void inject(CustomActivity customActivity); // add more activities if needed

    void inject(CustomFragment customFragment); // add more fragments if needed

    void inject(CustomDialogFragment customDialogFragment); // add more dialogs if needed

}



@Module
public class ControllerModule {

    private Activity mActivity;
    private FragmentManager mFragmentManager;

    public ControllerModule(Activity activity, FragmentManager fragmentManager) {
        mActivity = activity;
        mFragmentManager = fragmentManager;
    }

    @Provides
    @ControllerScope
    Context context() {
        return mActivity;
    }

    @Provides
    @ControllerScope
    Activity activity() {
        return mActivity;
    }

    @Provides
    @ControllerScope
    DialogsManager dialogsManager(FragmentManager fragmentManager) {
        return new DialogsManager(fragmentManager);
    }

    // @Provides for presenters can be declared here, or in a standalone PresentersModule (which is better)
}

And then in Activity:

public class CustomActivity extends AppCompatActivity {

    @Inject DialogsManager mDialogsManager;

    private ControllerComponent mControllerComponent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getControllerComponent().inject(this);

    }

    private ControllerComponent getControllerComponent() {
        if (mControllerComponent == null) {

            mControllerComponent = ((MyApplication)getApplication()).getApplicationComponent()
                    .newControllerComponent(new ControllerModule(this, getSupportFragmentManager()));
        }

        return mControllerComponent;
    }
}

Additional information on dependency injection:

Dagger 2 Scopes Demystified

Dependency Injection in Android

Original Thread

By anonymous    2017-09-20

What follows is not an answer to your question, but an explanation why you shouldn't be asking this question at all.

You should avoid injections into custom Views in general. The reasons for this are listed in this article.

Advantages of using Method Injection in this case [injection into custom Views] are:

  • Dependencies will need to be propagated from top level component (Activity or Fragment)
  • Method Injection does not open door to Single Responsibility Principle violation
  • No dependency on the framework
  • Better performance

The first advantage might come as a surprise because propagation from top level component is harder than adding annotation to fields, and involves more boilerplate code. This is surely a bad thing, right?. Not in this case. In fact, there are two good aspects associated with such a propagation of dependencies. First of all, the dependencies will be visible at the top level component. Therefore, just by looking at e.g. Fragment‘s fields, the reader of the code will immediately understand that this Fragment shows images. Such optimizations for readability makes the system more easily maintainable in the long term. Secondly, there are not many use cases in which sub-classes of View need additional dependencies. The fact that you need to actually work in order to provide these dependencies will give you a bit of time to think about whether providing them is a good design decision to start with.

The second advantage is related to collaborative construction. You might be very experienced software engineer yourself, but you’ll probably have also less experienced teammates. Or it is possible that you’ll leave the project one day, and the guy who will take over will not be as good as you. By injecting one single dependency using a framework, you basically open a door for other injections. Imagine that some data from SharedPreferences becomes required in custom View in order to e.g. fix a bug. One of the less experienced developers might decide that it is a good approach to inject SharedPreferences into custom View directly. Doing this violates Single Responsibility Principle, but that developer might not even be aware of such a concept. Therefore, in the long term, such injection “backdoors” can reduce design quality and lead to long debug sessions.

The third advantage of using Method Injection with custom Views is that you don’t couple the View to dependency injection framework. Just imagine that few years from now you (or some other poor guy) need to replace the framework. The fact that you’ll probably have tens of Activities and Fragments to start with will make your life miserable. If you’ll have additional tens or hundreds of custom Views to handle, then it might bring you into suicidal mood.

The last (but not least) advantage is performance. One screen can contain one Activity, several Fragments and tens of custom Views. Bootstrapping this number of classes using dependency injection framework might degrade application’s performance. It is especially true for reflection based frameworks, but even Dagger carries some performance cost.

In addition, I advice to avoid the new injection method that involves AndroidInjection class. It is discussed in this video tutorial.

Original Thread

By anonymous    2017-12-11

Using Activity wise components in every activity is not a good way. You can have a single Activity component for all you activites as a group. Have a look into this [link](https://www.youtube.com/watch?v=cx6pCIbOqtI&feature=youtu.be) and this [link](https://www.techyourchance.com/dagger-2-scopes-demystified/) and this [link](https://www.techyourchance.com/dependency-injection-android/). :)

Original Thread

Popular Videos 1201

Submit Your Video

If you have some great dev videos to share, please fill out this form.