如何在不强制消费应用程序使用Dagger的情况下构建基于Dagger的Android库?

时间:2023-01-24 18:12:26

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.

我正在开发一个Android库,它基本上是我编写的一些REST服务的客户端。我有几个存储类,网络队列,解析器等,并且像许多这样的类一样,它们依赖于Context或者从Context构造的SharedPreferences之类的东西。这些对象都隐藏在Facade类后面,因此我的库的使用者不会看到它们或直接与它们交互。

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.

为了我自己的理智,我想使用Dagger 2进行依赖注入,以便在我的库中管理这些类的实例。但是,我不想强​​迫使用我的库的应用程序自己使用Dagger;仅仅因为我选择使用Dagger并不意味着我的用户应该这样做。

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.

我见过的所有教程似乎都期望我正在构建一个应用程序,而不仅仅是一个库。其中许多教程告诉我,我应该让我的Application类继承自DaggerApplication。但就我而言,我的库中根本没有应用程序(或任何Activity或Service`类),我不希望我的用户必须使用Dagger的基类。

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?

那么我怎样才能使用Dagger而不将其“泄漏”出我的库?我在这里找到了部分答案,但我不确定如何调整作者的“包装”模式来处理我对Context的依赖。我可以将上下文传递给包装器的getComponent()方法,还是Dagger能够以其他方式获取Context引用?

1 个解决方案

#1


7  

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.

库几乎就像一个应用程序(当谈到Dagger时)。是的,你没有应用程序对象,但你真的不需要它。

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.

让您的用户在第一次调用您的库时传递上下文(例如)。有一个DaggerInjector(我认为你的示例称之为包装器),它具有对Component接口的静态引用。

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

让您的用户调用方法(或者如果组件为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).

这将确保初始化Dagger组件并生成代码。理解调用buildComponent是一项昂贵的任务(Dagger必须做很多事情!)所以只做一次(除非您需要使用运行时已知的不同值重新初始化库)。

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

有些图书馆只是在每个电话中都要求一个上下文,所以这不是不可能的;然后,您可以在第一次调用时初始化dagger(通过检查注入器中的getComponent()是否为null)。

After your DaggerInjector.getComponent() is not null anymore, you can now add @Inject and the appropriate "injectable" stuff…

在您的DaggerInjector.getComponent()不再为null之后,您现在可以添加@Inject和相应的“可注入”内容......

e.g.: in YourModule you could have:

例如:你可以在YourModule中:

@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();
          DaggerComponent.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:

为了使上述工作,您需要在YourComponent(这是一个接口)代码中这样:

void inject(AnObjectThatWantsToInjectStuff object);

void inject(AnObjectThatWantsToInjectStuff对象);

(otherwise calling DaggerComponent.getComponent().inject(this) will fail at compile time)

(否则调用DaggerComponent.getComponent()。inject(this)将在编译时失败)

Notice I never passed a context to YourInjectableContext, Dagger already knows how to obtain it.

注意我从未向YourInjectableContext传递上下文,Dagger已经知道如何获取它。

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

泄漏时要小心。我建议您为所有/大多数情况存储context.getApplicationContext()而不仅仅是普通的Context(除非您明确需要一个Activity上下文来扩展布局/主题目的,消费应用程序提供的应用程序上下文就是您所需要的)。

#1


7  

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.

库几乎就像一个应用程序(当谈到Dagger时)。是的,你没有应用程序对象,但你真的不需要它。

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.

让您的用户在第一次调用您的库时传递上下文(例如)。有一个DaggerInjector(我认为你的示例称之为包装器),它具有对Component接口的静态引用。

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

让您的用户调用方法(或者如果组件为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).

这将确保初始化Dagger组件并生成代码。理解调用buildComponent是一项昂贵的任务(Dagger必须做很多事情!)所以只做一次(除非您需要使用运行时已知的不同值重新初始化库)。

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

有些图书馆只是在每个电话中都要求一个上下文,所以这不是不可能的;然后,您可以在第一次调用时初始化dagger(通过检查注入器中的getComponent()是否为null)。

After your DaggerInjector.getComponent() is not null anymore, you can now add @Inject and the appropriate "injectable" stuff…

在您的DaggerInjector.getComponent()不再为null之后,您现在可以添加@Inject和相应的“可注入”内容......

e.g.: in YourModule you could have:

例如:你可以在YourModule中:

@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();
          DaggerComponent.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:

为了使上述工作,您需要在YourComponent(这是一个接口)代码中这样:

void inject(AnObjectThatWantsToInjectStuff object);

void inject(AnObjectThatWantsToInjectStuff对象);

(otherwise calling DaggerComponent.getComponent().inject(this) will fail at compile time)

(否则调用DaggerComponent.getComponent()。inject(this)将在编译时失败)

Notice I never passed a context to YourInjectableContext, Dagger already knows how to obtain it.

注意我从未向YourInjectableContext传递上下文,Dagger已经知道如何获取它。

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

泄漏时要小心。我建议您为所有/大多数情况存储context.getApplicationContext()而不仅仅是普通的Context(除非您明确需要一个Activity上下文来扩展布局/主题目的,消费应用程序提供的应用程序上下文就是您所需要的)。