我开始在我正在开发的应用程序中使用Dagger 2,但我对Dagger 2如何工作有一些疑问。
我得到了@Provides方法背后的所有逻辑和用于初始化依赖关系的@Inject批注,但@Inject批注对类构造函数有点不好意思。
例如:
我是我的应用程序,我有一个模块定义,ContextModule,用于检索我的应用程序的上下文:
ContextModule.java
@Module
public class ContextModule {
private final Context context;
public ContextModule(Context context) {
this.context = context;
}
@Provides
public Context context() {
return this.context;
}
}
我的BaseActivityComponent使用此模块:
BaseActivityComponent.java
@BaseActivityScope
@Component(modules = ContextModule.class)
public interface BaseActivityComponent {
void injectBaseActivity(BaseActivity baseActivity);
}
到目前为止很好..然后我有一个AuthController类,这取决于上下文,我想在我的BaseActivity中注入它。所以在我的AuthControllers.class中我有类似的东西:
public class AuthController {
private Context context;
@Inject
public AuthController(Context context) {
this.context = context;
}
public void auth() {
// DO STUFF WITH CONTEXT
}
}
我将它注入我的BaseActivity,如:
public class BaseActivity extends AppCompatActivity {
@Inject
AuthController authController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BaseActivityComponent component = DaggerBaseActivityComponent.builder()
.contextModule(new ContextModule(this))
.build();
component.injectBaseActivity(this);
authController.auth();
}
}
现在我的问题是,dagger如何知道我的AuthControllers是BaseActivity的依赖?只是声明
@Inject
AuthController authController;
就像我创建一个类似于:
的ControllerModule一样@Module(includes = ContextModule.class)
public class ControllerModule {
@Provides
AuthController authController(Context context) {
return new AuthController(context);
}
}
然后在我的BaseActivityComponent中,我将添加我的AuthController getter并将我的依赖模块更改为ControllersModule:
@BaseActivityScope
@Component(modules = ControllersModule.class)
public interface BaseActivityComponent {
void injectBaseActivity(BaseActivity baseActivity);
AuthController getAuthController();
}
当我调用injectBaseActivity(this)时,它“告诉”匕首所有@Inject注释都是我的类的依赖项,然后它搜索我的项目以寻找与该类型匹配的@Inject注释构造函数?
我认为关于Dagger 2的一个好处是模块文件可以用作我的依赖关系三的“文档”。但是如果只是在我控制的所有构造函数中添加@Inject,将来不会有点混乱,因为你不知道实际上取决于什么? (我的意思是,你知道什么取决于什么,你只需要浏览很多文件才能真正找到答案)
在构造函数中使用@Inject注释或何时在Modules文件中添加@Provides方法时是否有最佳实践? 我在构造函数中使用@Inject得到了我不需要在模块文件中更改构造函数定义,但是有任何缺点吗?
感谢。
答案 0 :(得分:35)
当我调用injectBaseActivity(this)时,它“告诉”匕首所有@Inject注释都是我的类的依赖项,然后它在我的项目中搜索与该类型匹配的@Inject注释构造函数?
完全。但是当你调用injectBaseActivity
时它没有完成,但这一切都发生在编译期间。这是注释处理的一种方式(另一种方法是在运行时使用反射)。
当您构建项目时,您在build.gradle文件中包含的dagger-annotation-processor(作为依赖项)将被调用,其中包含由@Inject
注释注释的所有字段,类等的列表,用它构建一个依赖图。然后它解析图形,生成源代码,提供图形上项目的所有依赖项。
injectBaseActivity
只执行之前生成的代码,并将所有依赖项分配给对象。它是正确的源代码,您可以阅读和调试。
这是编译步骤的原因 - 简单地说就是性能和验证。 (例如,如果您有一些依赖循环,则会出现编译错误)
dagger如何知道我的AuthControllers是BaseActivity的依赖?
@Inject
AuthController authController;
通过注释字段@Inject
dagger知道你想要一个AuthController
。到现在为止还挺好。现在,dagger将寻找一些方法来提供控制器,在组件内查找它,组件依赖项和组件模块。它还将查看是否可以自己提供类,因为知道有关其构造函数。
如果你没有在任何模块中包含它,dagger如何知道对象构造函数?
@Inject
public AuthController(Context context) { /**/ }
通过使用注释注释构造函数,您还告诉dagger有一个名为AuthController
的类,并且您需要一个上下文来实例化它。它与将其添加到模块基本相同。
如果您没有将@Provides
注释添加到构造函数的源代码,或者对象需要进一步初始化,则应使用模块@Inject
方法。或者在你的情况下...
[...]模块文件可以用作我的依赖树[...]
的“文档”
是的,当然你可以这样做。但是随着项目的增长,你将不得不维护很多不必要的代码,因为在构造函数上使用简单的注释也可以做到这一点。
在构造函数中使用@Inject注释或何时在Modules文件中添加@Provides方法时是否有最佳实践?
如果您想为不同的上下文提供不同的版本(例如,以两种不同的方式实现接口),还有@Binds
注释告诉dagger您希望将哪个类作为实现提供。
除此之外,我相信你应该尽可能使用构造函数注入。如果发生了变化,您不必触及代码的任何其他部分,只需编写的代码就少,因此可以包含错误的地方更少。
Dagger也可以并且通过了解更多来优化很多,如果你实现了不必要的代码,它将不得不使用你引入的开销
当然最终完全取决于你认为最好的。毕竟你必须使用你的代码;)