简体   繁体   English

在Android模块化项目中使用Dagger 2.11

[英]Using Dagger 2.11 with android modular project

I started working on a new Android project from scratch. 我从头开始研究一个新的Android项目。 After understanding the project scope and requested features, I've came up with a modular architecture (basically wrapping every feature into a feature or android module) that looks as following 了解项目范围和所需功能后,我提出了一个模块化架构(基本上将每个功能包装到功能或android模块中),如下所示 在此处输入图片说明

Everything looks perfect until I wanted to introduce dagger to glue all the modules. 一切看起来都很完美,直到我想引入匕首粘合所有模块为止。 The problem is that I want every module to has its own dagger component/subcomponent and it's modules in order to provide dependencies and expose them the graph to be using by other component or the parent one. 问题是我希望每个模块都有自己的匕首组件/子组件,并且这些模块是为了提供依赖关系并将它们公开给其他组件或父组件使用的图形。

Google official dagger documentation states that subcomponents has direct access to parent component dependencies' and not vice versa. Google官方的匕首文档指出,子组件可以直接访问父组件依赖关系,反之亦然。 However, in my case the base component require dependencies from the data module and this latter itself require dependencies from the network module. 但是,在我的情况下,基本组件需要数据模块的依赖关系,而后者本身需要网络模块的依赖关系。

is there any solution for this problem knowing that i want every android module to have its own sub-component preferably? 知道我希望每个android模块最好有自己的子组件,对此问题有解决方案吗? If not, is there any solution anyway? 如果没有,有什么解决办法吗?

Thank you. 谢谢。

Edit: Here is how my project structure looks like 编辑:这是我的项目结构的样子

在此处输入图片说明

And this is how I setup my dagger graph 这就是我设置我的匕首图的方式

在此处输入图片说明

My AppComponent(Dagger root) 我的AppComponent(匕首根)

@Singleton
@Component(modules = {
    AppModule.class,
    ActivityBuilder.class,
    AndroidSupportInjectionModule.class
})
public interface AppComponent {

void inject(CatApp application);

@Component.Builder
interface Builder {
    @BindsInstance
    Builder application(Application application);

    AppComponent build();
}
}

My App Module 我的应用程序模块

@Module(subcomponents = DataComponent.class)
public class AppModule {

@Provides
@Singleton
Context provideContext(Application application) {
    return application.getApplicationContext();
}
}

My DataComponent (located at the data android module) 我的DataComponent(位于数据android模块)

@Subcomponent(modules = DataModule.class)
public interface DataComponent {

@Subcomponent.Builder
interface Builder {
    DataComponent build();
}
}

Data module (located at data android module) that should provide the implementation of SystemManager 数据模块(位于数据android模块),应提供SystemManager的实现

@Module(subcomponents = NetworkComponent.class)
public class DataModule {

@Provides
@Singleton
ISystemManager provideSystemManager(SystemManager systemManager) {
    return systemManager;
}
}

Network Component (located at Network Android Module) 网络组件(位于网络Android模块中)

@Subcomponent(modules = NetworkModule.class)
public interface NetworkComponent {

@Subcomponent.Builder
interface Builder {
    NetworkComponent build();
}
}

Network Module (located at Network Android Module) and should provide implementation of INetWorkManager 网络模块(位于网络Android模块上),应提供INetWorkManager实现

@Module
public class NetworkModule {

@Provides
@Singleton
INetworkManager provideNetworkManager(NetworkManager networkManager) {
    return networkManager;
}
}

I am using @Inject annotation at all constructors so my configurations is all setup but the issue is that dagger doesn't compiles these subcomponent for some reason and I get this error when compiled: 我在所有构造函数上都使用@Inject批注,因此我的配置已全部完成,但问题是dagger出于某些原因无法编译这些子组件,并且在编译时出现此错误:

Error:(27, 8) error: [dagger.android.AndroidInjector.inject(T)] com.github.andromedcodes.network.INetworkManager cannot be provided without an @Provides-annotated method.
com.github.andromedcodes.network.INetworkManager is injected at
com.github.andromedcodes.data.SystemManager.<init>(networkManager)
com.github.andromedcodes.data.SystemManager is injected at
com.github.andromedcodes.data.di.DataModule.provideSystemManager(systemManager)
com.github.andromedcodes.domain.managers.ISystemManager is injected at
com.github.andromedcodes.domain.interactors.CheckSystemAvailability.<init>(systemManager)
com.github.andromedcodes.domain.interactors.CheckSystemAvailability is injected at
com.github.andromedcodes.chasseautrsor.views.Splash.SplashPresenter.<init>(checkSystemAvailability)
com.github.andromedcodes.chasseautrsor.views.Splash.SplashPresenter is injected at
com.github.andromedcodes.chasseautrsor.di.SplashModule.bindSplashPresenter(presenter)
com.github.andromedcodes.chasseautrsor.views.Contract.Presenter is injected at
com.github.andromedcodes.mvp.BaseActivity.mPresenter
com.github.andromedcodes.chasseautrsor.views.SplashScreenActivity is injected at
dagger.android.AndroidInjector.inject(arg0)

How can I fix this issue knowing that I want to provide ISystemManager implementation at Data android Module and INetworkManager at Network Android Module? 知道要在Data android Module上提供ISystemManager实现和在Network Android Module上提供INetworkManager时,如何解决此问题?

Thank you. 谢谢。

Subcomponents automatically have access to objects bound in the parent components' graph, which makes sense, because subcomponents have exactly one parent component—there's no ambiguity. 子组件自动访问父组件图中绑定的对象,这很有意义,因为子组件只有一个父组件,因此没有歧义。 Parent components do not have automatic access to subcomponents' graph because you can create as many subcomponent instances as you'd like; 父组件无法自动访问子组件的图,因为您可以创建任意数量的子组件实例。 it's not clear which instance you're trying to access. 目前尚不清楚您要访问哪个实例。 In general, unless you need different variations on an object graph (which you'd do with private modules or child injectors in Guice) or unless you wanted to hide implementation details (eg internal network objects), you may be better off installing your modules all in the same Component and skipping the subcomponent strategy. 通常,除非您需要在对象图上使用不同的变体(在Guice中使用私有模块或子注入器来完成),或者除非您想隐藏实现细节(例如内部网络对象),否则最好安装模块全部都在同一个组件中,并跳过子组件策略。

However, if you do want to separate your graph or create multiple subcomponent instances, you could also create a subcomponent instance in a scoped @Provides method . 但是,如果您确实想分离图形或创建多个子组件实例,则也可以在作用域@Provides方法中创建一个子组件实例 That way NetworkComponent has a separate graph with private bindings, but can also use dependencies you expose in AppComponent, and you can also ensure that there is exactly one copy of NetworkComponent and its relevant bindings in your graph. 这样,NetworkComponent就有一个带有私有绑定的单独图,但也可以使用您在AppComponent中公开的依赖项,并且还可以确保图中完全存在NetworkComponent及其相关绑定的一个副本。 You'll also need to put a getter (provision method or factory method) on the Subcomponent, so you can access some of its bindings from outside, in exactly the same way that you need a getter or injector method on a @Component for it to be useful. 您还需要在子组件上放置一个吸气剂(提供方法或工厂方法),以便可以从外部访问其某些绑定,就像您在@Component上需要一个吸气剂或注入器方法一样有用。

@Subcomponent(modules = NetworkModule.class)
public interface NetworkComponent {

  /** Allow anyone with a NetworkComponent instance to get the INetworkManager. */
  INetworkManager getINetworkManager();

  @Subcomponent.Builder
  interface Builder {
    NetworkComponent build();
  }
}

/**
 * Creates a singleton NetworkComponent. Install this in AppComponent's module,
 * or in your data module if that encapsulates network calls.
 */
@Singleton @Provides NetworkComponent networkComponent(
    NetworkComponent.Builder builder) {
  return builder.build();
}

/** Make the INetworkManager accessible, but not the NetworkManager impl. */
@Provides static provideNetworkManager(NetworkComponent networkComponent) {
  return networkComponent.getINetworkManager();  // add this to NetworkComponent
}

For further reference, see the "Subcomponents for Encapsulation" section on in the Dagger 2 docs on Subcomponents: 有关更多参考,请参阅Dagger 2文档中有关子组件的“用于封装的子组件”部分

Another reason to use subcomponents is to encapsulate different parts of your application from each other. 使用子组件的另一个原因是彼此封装了应用程序的不同部分。 For example, if two services in your server (or two screens in your application) share some bindings, say those used for authentication and authorization, but each have other bindings that really have nothing to do with each other, it might make sense to create separate subcomponents for each service or screen, and to put the shared bindings into the parent component. 例如,如果服务器中的两个服务(或应用程序中的两个屏幕)共享一些绑定,例如用于身份验证和授权的绑定,但是每个绑定都具有实际上彼此无关的其他绑定,则可能有意义每个服务或屏幕的单独子组件,并将共享绑定放入父组件。

In the following example, the Database is provided within the @Singleton component, but all of its implementation details are encapsulated within the DatabaseComponent. 在下面的示例中,@ @Singleton组件中提供了数据库,但是其所有实现细节都封装在DatabaseComponent中。 Rest assured that no UI will have access to the DatabaseConnectionPool to schedule their own queries without going through the Database since that binding only exists in the subcomponent. 请放心,由于该绑定仅存在于子组件中,因此没有UI可以访问DatabaseConnectionPool来调度自己的查询而无需通过数据库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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