I'm working on an Android library that is basically a client for some REST services I've written. I have several storage classes, network queues, parsers, and so on, and like many such classes, they have dependencies on Context
or on things like SharedPreferences
that are constructed from Context
. These objects are all hidden behind a facade class, so consumers of my library don't see them or interact with them directly.
For my own sanity, I would like to use Dagger 2 for dependency injection to manage instances of these classes INTERNALLY within my library. However, I don't want to force apps using my library to use Dagger themselves; just because I chose to use Dagger doesn't mean my users should have to.
All the tutorials I've seen seem to expect that I'm building an application, not just a library. Many of these tutorials tell me I should make my Application
class inherit from DaggerApplication
. In my case, though, I don't have an Application
(or any Activity
or Service` classes) in my library at all, and I don't want my users to have to use Dagger's base classes.
So how can I use Dagger without "leaking" it out of my library? I've found a partial answer here, but I'm not sure how adapt the author's "wrapper" pattern to handle my dependency on Context
. Can I just pass a context into the wrapper's getComponent()
method, or will Dagger be able to obtain a Context reference some other way?
A library is almost like an Application (when it comes to Dagger). Yes, you don’t have an application
object, but you don’t really need one.
As a consumer of your Library, I expect it to be simple to use, so I don’t want to know what dagger is at all (or if you internally use it).
Let your users pass a Context
when they call your library for the first time (for example). Have a DaggerInjector
(I think your sample calls it wrapper) that has a static reference to your Component
interface.
Example (and as such, just a generic example):
public class DaggerInjector {
private static YourComponent component;
private DaggerInjector() {
super();
}
public static YourComponent getComponent() {
return component;
}
public static YourComponent buildComponent(Context context) {
component = DaggerYourComponent
.builder()
.yourModule(new YourModule(context))
.build();
return component;
}
}
Your “module” can look like:
@Module
public class YourModule {
private Context context;
public YourModule(Context context) {
this.context = context;
}
@Provides
@Singleton
final Context providesContext() {
return context;
}
}
To use it:
Have your users call a method (or you call it yourself the first time if the component is null):
DaggerInjector.buildComponent(context);
This will ensure the Dagger component is initialized and the code is generated. Understand that calling buildComponent
is an expensive task (Dagger has to do a lot!) so only do it once (unless you need to re-initialize the library with different values known at runtime only).
Some libraries simply ask for a context in every call, so that is not out of the question; you could, then, initialize dagger the first time you're called (by checking if getComponent() is null in the injector).
After your DaggerInjector.getComponent()
is not null anymore, you can now add @Inject
and the appropriate "injectable" stuff…
e.g.: in YourModule
you could have:
@Provides
SomeObject providesSomeObject() {
return new SomeObject();
}
// THIS “Context” here is automatically injected by Dagger thanks to the above.
@Provides
@Singleton
SomeOtherObject providesSomeOtherObject(Context context) {
return new SomeOtherObject(context); //assume this one needs it
}
and in any "injectable" object (that is, an object that has an inject
method in your component…) you can do:
public class AnObjectThatWantsToInjectStuff {
@Inject
SomeObject someObject;
@Inject
SomeOtherObject someOtherObject;
public AnObjectThatWantsToInjectStuff() {
super();
DaggerInjector.getComponent().inject(this);
// you can now use someObject and someOtherObject
}
}
For the above to work, you need in YourComponent
(which is an Interface) code like this:
void inject(AnObjectThatWantsToInjectStuff object);
(otherwise calling DaggerInjector.getComponent().inject(this)
will fail at compile time)
Notice I never passed a context to YourInjectableContext
, Dagger already knows how to obtain it.
Be careful with leaks tho. I recommend that you store context.getApplicationContext()
instead of just plain Context
for all/most cases (unless you explicitly need an Activity context for inflating layouts/theme purposes, the application context supplied by the consuming application is all you need).