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
, forcontext
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!