简体   繁体   中英

How can I build a Dagger-based Android library without forcing consuming applications to use Dagger?

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…

eg: 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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM