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上下文来扩展布局/主题目的,消费应用程序提供的应用程序上下文就是您所需要的)。