2017-04-07 139 views
13

我开始在我正在开发的应用程序中使用Dagger 2,但我对于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(); 

    } 
} 

现在的问题是,如何做匕首知道我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注释是我的类的依赖关系,然后它搜索我的项目ct为@Inject注释的构造函数匹配该类型?

我认为Dagger 2的一个好处就是Module文件可以用作我的依赖关系的“文档”。但是,如果只是在我控制的所有构造函数中添加@Inject,将来会不会有点困惑,因为你不知道什么取决于什么? (我的意思是,你知道什么取决于什么,你只需浏览大量的文件,以真正找出)

有没有什么最佳做法,当在构造函数中使用@Inject注释或何时添加@Provides方法在模块文件? 我在构造函数中使用@Inject我不需要更改我的模块文件中的构造函数定义,但有什么缺点吗?

谢谢。

回答

23

当我打电话injectBaseActivity(这)为“告诉”匕首所有@Inject注释是我班的相关性,然后它搜索我的项目@Inject注释该类型相匹配的构造函数?

没错。但是在拨打injectBaseActivity时没有完成,但这一切都是在编译期间发生的。这是注释处理的一种方式(另一个使用运行时反射)。

当您构建项目时,您在build.gradle文件中包含的(作为依赖项的)匕首注释处理程序将被调用,并列出所有由@Inject注释注释的字段,类等,并构建依赖项图表与它。然后解析该图,生成提供图上项目的所有依赖关系的源代码。

injectBaseActivity只是执行之前生成的代码,并将所有依赖关系分配给对象。它是适当的源代码,您可以阅读和调试。

这是编译步骤—的原因简单地说就是—就是性能和验证。 (例如,如果你有一些依赖循环,你会得到一个编译错误)


如何匕首知道我AuthControllers是BaseActivity的依赖?

@Inject 
AuthController authController; 

通过注释字段@Inject匕首知道你想要一个AuthController。到现在为止还挺好。现在,匕首将寻找一些方法来提供控制器,在组件,组件依赖和组件模块中寻找它。它也将看看这个类是否可以自己提供,因为它知道它的构造函数的

如何匕首了解对象的构造函数,如果你不包括任何模块中?

@Inject 
public AuthController(Context context) { /**/ } 

通过与注入注释构造函数,你还告诉匕首有一个叫AuthController类,你需要一个上下文它被实例化。它基本上与将其添加到模块中相同。

模块@Provides如果您没有源代码,只需将@Inject注释添加到构造函数中,或者对象需要进一步初始化,则应该使用该方法。或者在你的情况......

[...]模块文件可以作为我的依赖关系树的“文档” [...]

是的,当然你可以去做。但是,随着你的项目的发展,你将不得不保持不必要的代码,很多,因为同样可能已经在构造一个简单的注解来实现。

当在构造函数中使用@Inject注释或何时在模块文件中添加@Provides方法时,是否有任何最佳做法?

如果要提供不同的版本不同的环境(例如以两种不同的方式实现一个接口)还有一个@Binds注释,它告诉匕首你希望提供哪个类作为实现。

除此之外,我相信你应该在可能的时候总是使用构造函数注入。如果更改了某些内容,则不必触碰代码的任何其他部分,而只需编写更少的代码,从而减少可能包含错误的地方。

而且匕首能够并且已经通过了解更多的优化了很多,如果你执行不必要的代码,它会跟你介绍


当然,最后的开销工作是一切都取决于什么你认为是最好的。毕竟它是必须与你的代码;)

+0

真棒的答案。你知道是否有可能用注入注释Kotlin默认构造函数(用类定义定义的构造函数)? –

+1

@saiedVanguard这是可能的:'类A @Inject构造函数(...)' –