Java – Dagger 2 – inject non-Android classes

Dagger 2 – inject non-Android classes… here is a solution to the problem.

Dagger 2 – inject non-Android classes

I’m implementing Dagger 2 in my Android app. I set it up via :

AppComponent.java

@Singleton
@Component(modules = {
  AndroidInjectionModule.class,
  AndroidSupportInjectionModule.class,
  ActivityBuilder.class,
  AppModule.class,
  DataBaseDaoModule.class
})

public interface AppComponent {
  @Component.Builder
  interface Builder {
    @BindsInstance
    Builder application(Application aApplication);

AppComponent build();
  }

Application application();
  void inject(MyApplication aApplication);
}

AppInjector.java

ublic class AppInjector {

public static void init(MyApplication aApplication) {

Initialize dagger and inject the aApplication
    DaggerAppComponent.builder().application(aApplication).build().inject(aApplication);

aApplication.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
      @Override
      public void onActivityCreated(Activity aActivity, Bundle aBundle) {
        handleActivity(aActivity);
      }

@Override
      public void onActivityStarted(Activity aActivity) {
      }

@Override
      public void onActivityResumed(Activity aActivity) {
      }

@Override
      public void onActivityPaused(Activity aActivity) {
      }

@Override
      public void onActivityStopped(Activity aActivity) {
      }

@Override
      public void onActivitySaveInstanceState(Activity aActivity, Bundle aBundle) {
      }

@Override
      public void onActivityDestroyed(Activity aActivity) {
      }
    });
  }

private static void handleActivity(Activity aActivity) {
    if (aActivity instanceof HasActivityInjector) {
      AndroidInjection.inject(aActivity);
      Timber.d("injected Activity");
    }
    if (aActivity instanceof FragmentActivity) {
      ((FragmentActivity) aActivity).getSupportFragmentManager()
        .registerFragmentLifecycleCallbacks(
          new FragmentManager.FragmentLifecycleCallbacks() {
            @Override
            public void onFragmentCreated(FragmentManager fm, Fragment f,
                                          Bundle savedInstanceState) {
              if (f instanceof Injectable) {
                Timber.d("injected Fragment");
                AndroidSupportInjection.inject(f);
              }
            }
          }, true);
    }
  }
}

AppModule.java

Module(includes = ViewModelModule.class)
class AppModule {

@Singleton
  @Provides
  ApiService providesApiService(OkHttpClient aClient, MyInterceptor aInterceptor) {

Build a Retrofit object here
  }

@Singleton
  @Provides
  OkHttpClient providesOkHTTPClient(MyInterceptor aInterceptor) {
   Setup OKHTTP here
  }
}

Finally, in the onCreate method in MyApplication.Java, I just call AppInjector:AppInjector.init(this) like this;

All of this works, and anything I put into an AppComponent module, I can inject into Activities, Fragments, and ViewModels.

However, in some cases I need a utility class that relies on Application, for contex – I use the utility to teach classes in different places. Or I’ll have a Manager class that depends on the application, or needs something from the AppModule. However, since I use these classes outside of Activities, Fragments, and ViewModels, I can’t inject directly. How would I provide my utility class with their dependencies and any other type of class – like the manager class?

My first thought was to create a UtilityComponent and a ManagerCompoent or something, but I didn’t know how to get them to work with anything in AppModuel or through my AppComponent.

Solution

Please do not use component.inject(myObject) for everything only. Always prefer constructor injection or providing it from a module that can perform additional setup steps. .inject(myObject) is for framework components that you do not have access to the constructor.

My first thought was to create a UtilityComponent and a ManagerCompoent of sorts, however I have no idea how I would get them to work with anything in AppModuel or through my AppComponent.

You don’t need separate components. See below.

However, since I use these classes outside of Activities, Fragments and ViewModels I cannot just inject.

This has nothing to do with injecting. You’re talking about scope, it sounds like your utility is @Singleton. Your AppComponent is an @Singleton-scope component, so it can also be used to provide your utilities.

However, I have cases where I would need a utility class, that depends on Application, for context

If they are part of the @Singleton components that can access your application, they can also be provided anywhere else. No more components or anything are needed. Just declare your dependencies and don’t overthink it.


Just declare your util, comment it with @Singleton and mark the constructor with @Inject for constructor injection. @Singleton Ensure that it will be provided by your AppComponent and that it can access the Applications it depends on.

@Singleton public class MyUtil {

private Application application;

@Inject public MyUtil(Application application) {
    this.application = application;
  }

}

You can then inject it into your activities, fragments, or even into other utilities…

@Singleton public class MyUtilWrapper {

private MyUtil myUtil;

@Inject public MyUtilWrapper(MyUtil myUtil) {
    this.myUtil = myUtil;
  }

}

You can inject one or both into your activity or fragment….

@Inject MyUtil myUtil;
@Inject MyUtilWrapper myUtilWrapper;

void onCreate(..) {
  AndroidInjection.inject(this);
}

You don’t need any modules, methods, or components to provide simple classes. Just make sure to add the correct range!

Related Problems and Solutions