译文
Subcomponent
(子组件)是继承和扩展父Component
(组件)的对象图的component
.你可以使用它们将应用的对象图划分为子图,或者封装成不同的部分,而不是在一个component
中共用一个scope
(作用域)。
绑定在一个Subcomponent
中的对象可以基于任意绑定到它的parent component
或ancestor(祖先,上级) component
的对象,以及在自己的module
中绑定的对象。另一方面,parent Component
找中绑定的对象不能基于在Subcomponent
中绑定的对象。在一个Subcomponent
中绑定的对象也不能基于兄弟Subcomponent
中绑定的对象。
也就是说,Subcomponent
的父Component
的对象图是Subcomponent
本身的对戏那个图的子图。
声明一个Subcomponent
正如最高级的Component
,你可以写一个抽象类或接口,在其中声明一些抽象方法,这些方法返回你的应用要用到的一些类型,由此创建一个Subcomponent
. 在这里我们不使用@Component
标注一个Subcomponent
,而是使用@Subcomponent
并安装(install) @Modules
. 与Component builder
类似,一个@Subcomponent Builder
指定了一个提供必要modules
的接口用来构造Subcomponent
.
@Subcomponent(modules = RequestModule.class)
interface RequestComponent {
RequestHandler requestHandler();
@Subcomponent.Builder
interface Builder {
Builder requestModule(RequestModule module);
RequestComponent build();
}
}
添加一个Subcomponent
到parent Component
中
要添加一个Subcomponent
到一个parent Component
中,需要将这个Subcomponent
类添加到parent Component
安装的@Module
的subcomponent
属性中。然后就可以在parent内部请求Subcomponent
的builder
.
@Module(subcomponents = RequestComponent.class)
class ServerModule {}
@Singleton
@Component(modules = ServerModule.class)
interface ServerComponent {
RequestRouter requestRouter();
}
@Singleton
class RequestRouter {
@Inject RequestRouter(
Provider<RequestComponent.Builder> requestComponentProvider) {}
void dataReceived(Data data) {
RequestComponent requestComponent =
requestComponentProvider.get()
.data(data)
.build();
requestComponent.requestHandler()
.writeResponse(200, "hello, world");
}
}
Subcomponents之Scope(作用域)
将应用的Component
拆分到Subcomponent
的理由之一就是使用Scopes
.一般情况下,unscoped
的绑定,注入类型的每个使用者会获得一个新的,单独的实例。但是,如果绑定是scoped
,那么在该scope
的生命期间,那个binding
的所有使用者拿到的是该绑定类型的同一实例。
标准的scope
是@Singleton
(单例). 单例的bindings
的所有用户拿到的都是相同实例。
在Dagger中,通过使用@Scope
注解一个Component
可以与一个Scope
关联。那样,Component
的实现例持有所有scoped
的对象的引用,因此他们可以被复用。带有scope
注解的@Provides
方法的Modules
只会安装在标注有同样scope
的Component
中。
带有@Inject
构造方法的类型也可以使用Scope
注解标注。这些隐式bindings
可以被任意使用了该注解的Component
或者他的SubComponent
使用。Scoped
的实例会在正确的scope
中被绑定。
任何Subcomponent
不能关联和祖先Comonent
同样的scope
,虽然没有关联的两个Subcomponent可以使用同样的scope
,这是因为没有关于scoped
对象的存储位置的冲突(即使两个Subcomponent
使用相同的scope注解,他们仍然能有效持有不同scope
的实例)。
例如,在下面的component
树,BadChildComponent
与它的父亲RootComponent
有同样的@RootScope
注解,这就错啦。但是SiblingComponentOne
和SiblingComponentTwo
都可以使用@ChildScope
,因为一个(SiblingComponent
)中的binding
不会与另一个(SiblingComponent
)相同类型的binding
混淆。
@RootScope @Component
interface RootComponent {
BadChildComponent.Builder badChildComponent(); // ERROR!
SiblingComponentOne.Builder siblingComponentOne();
SiblingComponentTwo.Builder siblingComponentTwo();
}
@RootScope @Subcomponent
interface BadChildComponent {...}
@ChildScope @Subcomponent
interface SiblingComponentOne {...}
@ChildScope @Subcomponent
interface SiblingComponentTwo {...}
因为一个Subcomponent
是在其父亲内部创建的,其生命周期严格小于它的父亲。这意味着,将Subcomponent
的scopes
视为”更小”,将父级Component
的scopes
视为”更大”是有意义的。事实上,你几乎总是要让根Component
使用@Singleton
注解。
下面的实例中,RootComponent
在@Singleton
注解中。@SessionScope
嵌套在@Singleton
scope
中,@RequestScope
嵌套在@SessionScope
中。注意FootRequestComponent
和BarRequestComponent
都用的是@RequestScope
,这是有效的,因为他们是兄弟,没有谁是另一方的前辈。
@Singleton @Component
interface RootComponent {
SessionComponent.Builder sessionComponent();
}
@SessionScope @Subcomponent
interface SessionComponent {
FooRequestComponent.Builder fooRequestComponent();
BarRequestComponent.Builder barRequestComponent();
}
@RequestScope @Subcomponent
interface FooRequestComponent {...}
@RequestScope @Subcomponent
interface BarRequestComponent {...}
Subcomponent之封装
使用Subcomponents
的另一个原因是封装应用中彼此不同的部分。例如,服务器中的两个service
(或者你应用的两个页面)共享一些bindings
,用于验证和授权,但双方其他的一些bindings
彼此毫不相关,为两个service
或页面创建隔离的两个Subcomponents
,然后将共有的bindings
放进parent Component
中。
在下面的例子中,Database
是在@singleton Component
中提供,但它所有的实现细节封装在DatabaseComponent
中。另外确保没有UI会不通过Database
访问DatabaseConnectionPool
去调度(Schedule
)他们自己的请求(queries),因为那个binding
只存在于Subcomponent
中。
@Singleton
@Component(modules = DatabaseModule.class)
interface ApplicationComponent {
Database database();
}
@Module(subcomponents = DatabaseComponent.class)
class DatabaseModule {
@Provides
@Singleton
Database provideDatabase(
@NumberOfCores int numberOfCores,
DatabaseComponent.Builder databaseComponentBuilder) {
return databaseComponentBuilder
.databaseImplModule(new DatabaseImplModule(numberOfCores / 2))
.build()
.database();
}
}
@Module
class DatabaseImplModule {
DatabaseImplModule(int concurrencyLevel) {}
@Provides DatabaseConnectionPool provideDatabaseConnectionPool() {}
@Provides DatabaseSchema provideDatabaseSchema() {}
}
@Subcomponent(modules = DatabaseImplModule.class)
interface DatabaseComponent {
@PrivateToDatabase Database database();
}
使用抽象工厂方法定义Subcomponent
除了@Module.subcomponents
, 通过在parent Component
中声明一个返回Subcomponent
的抽象工厂方法也可以将subcomponent
安装到parent中。如果Subcomponent
需要一个没有public
无参构造方法的module
,并且该module
没有安装到parent Component
中,那么该工厂方法必须有一个该Module类型的参数。工厂方法可以有其他任何安装在Subcomponent
上但没有安装在parent component
上的module
参数(Subcomponent
将自动共享它与其parent之间共享的module
实例)。可选地,parent component
上的抽象方法可以返回@Subcomponent.Builder
,并且不需要将module
列为参数。
使用@Module.subcomponent
更好,因为它允许Dagger检测该Subcomponent
曾经是否请求过。通过parent component
上的方法安装Subcomponent
是对那个Component
的显示请求,计时那个方法从未调用过。Dagger无法检测到这个,于是必须生成该Subcomponent
,计时你从未使用到它。
细节
扩展multibindings
(多绑定,复式绑定)
如他其他bindings
一样,parent component
中的multibindings
对于Subcomponent
中的bindings
是可见的。但是Subcomponent
也可以添加multibindings
到maps
中,并设置绑定到它们的parent. 这样的绑定(Any such additional contributions )只对subcomponent
内部或其subcomponent
的bindings
可见,在parent内是不可见的。
@Component(modules = ParentModule.class)
interface Parent {
Map<String, Integer> map();
Set<String> set();
Child child();
}
@Module
class ParentModule {
@Provides @IntoMap
@StringKey("one") static int one() {
return 1;
}
@Provides @IntoMap
@StringKey("two") static int two() {
return 2;
}
@Provides @IntoSet
static String a() {
return "a"
}
@Provides @IntoSet
static String b() {
return "b"
}
}
@Subcomponent(modules = ChildModule.class)
interface Child {
Map<String, Integer> map();
Set<String> set();
}
@Module
class ChildModule {
@Provides @IntoMap
@StringKey("three") static int three() {
return 3;
}
@Provides @IntoMap
@StringKey("four") static int four() {
return 4;
}
@Provides @IntoSet
static String c() {
return "c"
}
@Provides @IntoSet
static String d() {
return "d"
}
}
Parent parent = DaggerParent.create();
Child child = parent.child();
assertThat(parent.map().keySet()).containsExactly("one", "two");
assertThat(child.map().keySet()).containsExactly("one", "two", "three", "four");
assertThat(parent.set()).containsExactly("a", "b");
assertThat(child.set()).containsExactly("a", "b", "c", "d");
重复的modules
当同样的module
类型安装在一个component
和任意一个它的subcomponent
中,每个这样的component
将自动使用该module
的同一实例。这意味着如果你调用一个重复module
的sbcomponent builder
方法或者调用一个以重复module
为参数的subcomponent
工厂方法,就会出错(前者在编译时无法检测,是一个运行时错误)。
@Component(modules = {RepeatedModule.class, ...})
interface ComponentOne {
ComponentTwo componentTwo(RepeatedModule repeatedModule); // COMPILE ERROR!
ComponentThree.Builder componentThreeBuilder();
}
@Subcomponent(modules = {RepeatedModule.class, ...})
interface ComponentTwo { ... }
@Subcomponent(modules = {RepeatedModule.class, ...})
interface ComponentThree {
@Subcomponent.Builder
interface Builder {
Builder repeatedModule(RepeatedModule repeatedModule);
ComponentThree build();
}
}
DaggerComponentOne.create().componentThreeBuilder()
.repeatedModule(new RepeatedModule()) // UnsupportedOperationException!
.build();
解析
暂无