在Dagger2快速入门中,我们通过注入了Logger,实现了打日志,假设要为Logger增加上传日志功能,那么现在Logger需要一个网络库组件Retrofit实例,这个实例不应该每次都初始化,应该是全局的,所以Logger代码改造如下:
public class Logger {
private Retrofit retrofit;
public Logger(Retrofit retrofit) {
this.retrofit = retrofit;
}
public void log(String msg) {
System.out.println(msg);
}
public void uploadLog() {
//异步上传日志
retrofit.create(UploadApiServices.class).uploadLog("").enqueue(new Callback>() {
@Override
public void onResponse(Call> call, Response> response) {
// TODO: 2020-06-23 上传成功
}
@Override
public void onFailure(Call> call, Throwable t) {
// TODO: 2020-06-23 上传失败
}
});
}
interface UploadApiServices {
@POST("api/v1/uploadLog")
Call> uploadLog(@Body String json);
}
}
由于构造方法改了,相应的提供Logger的地方也需要改一下,在提供Logger的方法中,增加retrofit参数。
@Module
public class SubModule {
@Provides
public Logger providerLogger(Retrofit retrofit) {
return new Logger(retrofit);
}
}
显然,Retrofit如何注入没有提供给Dagger2,因此编译时会报错
[Dagger/MissingBinding] retrofit2.Retrofit cannot be provided without an nject constructor or an @Provides-annotated method.
简单的解决办法是:在这个Module中,提供一个构造Retrofit的方法providerRetrofit,代码如下:
@Module
public class SubModule {
@Provides
public Logger providerLogger(Retrofit retrofit) {
return new Logger(retrofit);
}
@Provides
public Retrofit providerRetrofit() {
return new Retrofit.Builder().baseUrl("http://a.b.c/api/")
.addCallAdapterFactory(null)
.addConverterFactory(null)
.build();
}
}
但是这种写法,将Retrofit和Logger的注入耦合到一起了,当前Logger的生命周期与Retrofit不一致时,则会导致Retrofit会被频繁创建。因此在处理模块横向依赖时,有4种解决办法:
- 重新定义一个NetModule,提供Retrofit的注入方法,而这个Module需要includes NetModule
NetModule代码 :
@Module
public class NetModule {
@Provides
public Retrofit providerRetrofit() {
return new Retrofit.Builder().baseUrl("http://a.b.c/api/")
.build();
}
}
SubModule代码
@Module(includes = {NetModule.class})
public class SubModule {
@Provides
public Logger providerLogger(Retrofit retrofit) {
return new Logger(retrofit);
}
}
Module是如何提供给SubModule的呢?由于CoffeeShopComponent需要SubModule,而SubModule也需要NetModule,因此CoffeeShopComponent构建的时候需要传入SubModule和NetModule,由于本次NetModule的构造方法是无参数的,则不需要处理。如果是有参数的,则需要在构建CoffeeShopComponent时传入NetModule实例
- 在现有的CoffeeShopComponent中的modules增加NetModule
增加了NetModule的Component代码如下:
@Component(modules = {SubModule.class, NetModule.class})
public interface CoffeeShopComponent {
void inject(Street street);
}
- 新增一个NetComponent,且修改CoffeeShopComponent新增依赖NetComponent。由于NetComponent是被依赖的,所以需要申明哪些对象对于依赖方是可见的,增加一个接口方法来申明即可。NetComponent代码如下:
@Component(modules = {NetModule.class})
public interface NetComponent {
Retrofit getRetrofit();
}
修改CoffeeShopComponent,增加依赖NetComponent,代码如下:
@Component(modules = {SubModule.class}, dependencies = NetComponent.class)
public interface CoffeeShopComponent {
void inject(Street street);
}
由于CoffeeShopComponent依赖了NetComponent,因此在生成CoffeeShopComponent时,需要提供NetComponent实例。
public class Street {
@Inject
Logger logger;
public Street() {
}
public void byCoffee() {
NetComponent netComponent = DaggerNetComponent.builder().netModule(new NetModule("test")).build();
DaggerCoffeeShopComponent.builder().subModule(new SubModule()).netComponent(netComponent).build().inject(this);
logger.log("inject success");
}
}
- Component除了可以依赖,还可以继承。在这个case中,Retrofit本身应该是全局的,可以做成顶层Component,因此我们将NetComponent改成AppComponent,其他Component可以继承该AppComponent。
使用继承时不需要显示申明对外暴露哪些对象,同时,父Component需要添加获取SubComponent.Builder的抽象方法来获取SubComponent,因此AppComponent代码如下:
@Component(modules = {NetModule.class})
public interface AppComponent {
CoffeeShopComponent.Builder getCoffeeShopComponentBuilder();
}
子Component需要使用@Subcomponent注解,同时需要提供一个Builder接口,供父Component来生成Component,Builder接口中需提供一个返回子Component的抽象接口方法,因此CoffeeShopComponent代码如下:
@Subcomponent(modules = {SubModule.class})
public interface CoffeeShopComponent {
@Subcomponent.Builder
interface Builder {
CoffeeShopComponent build();
}
void inject(Street street);
}
子Component时需要通过父Component来生成,使用代码如下:
public class Street {
@Inject
Logger logger;
public Street() {
}
public void byCoffee() {
AppComponent netComponent = DaggerAppComponent.builder().netModule(new NetModule("test")).build();
netComponent.getCoffeeShopComponentBuilder().build().inject(this);
logger.log("inject success");
}
protected void a(){
}
}