上一期主要介绍了在有一个参数的情况下Dagger如何运作。这一期将引入另一个int参数b
那么问题来了,上一期我们提到,@Provides
只认类型,不认名字,当有多个同类型存在时,Dagger如何分辨呢?
事实上,Dagger需要你来标注告诉它。这就涉及到@Qualifier
标注。Qualifier的含义正是限定词,而Dagger也提供了自带的@Named
的Qualifier来应对这种情况。
具体用法,就是帮助Dagger解决同类型造成的混淆,可能会出现在构造器里面,也可能出现在@Inject
两个同类型对象时。我们这里暂时还只是用来去除构造器里面同类型参数的问题。
主体代码
现在ClassA
看起来是这样:
public class ClassA {
private int a;
private int b;
@Inject
public ClassA(@Named("a") int a, @Named("b") int b) {
this.a = a;
this.b = b;
}
public int getA() {
return a;
}
public int getB() {
return b;
}
}
然后新的ModuleA
:
@Module
public class ModuleA {
private int a;
private int b;
public ModuleA(int a, int b) {
this.a = a;
this.b = b;
}
@Provides
@Named("a")
int provideIntA() {
return a;
}
@Provides
@Named("b")
int provideIntB() {
return b;
}
}
然后ClassAComponent
没有任何变化。
看看MainActivity
:
public class MainActivity extends AppCompatActivity {
@Inject ClassA classA;
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerClassAComponent.builder()
.moduleA(new ModuleA(2, 3))
.build().inject(this);
Log.d(TAG, classA.getA() + ":" + classA.getB());
}
}
运行可以看出打印了"2:3"。如果我们调换ModuleA
里两个@Named
标注,或者调换ClassA
里面的@Named
标注,可以发现打印为"3:2",这说明确实是ModuleA
按照标注来给ClassA
提供对应参数的。
生成代码
1.ClassA_Factory
public final class ClassA_Factory implements Factory {
private final Provider aProvider;
private final Provider bProvider;
public ClassA_Factory(Provider aProvider, Provider bProvider) {
assert aProvider != null;
this.aProvider = aProvider;
assert bProvider != null;
this.bProvider = bProvider;
}
@Override
public ClassA get() {
return new ClassA(aProvider.get(), bProvider.get());
}
public static Factory create(Provider aProvider, Provider bProvider) {
return new ClassA_Factory(aProvider, bProvider);
}
}
现在ClassA
有了两个参数,对应的就有两个Provider
。
2.ModuleA_ProvideIntAFactory
与ModuleA_ProvideIntBFactory
和上一期的一脉相承,还是很直白的把ModuleA
里面获取参数的方法封装成为一个工厂:
public final class ModuleA_ProvideIntAFactory implements Factory {
private final ModuleA module;
public ModuleA_ProvideIntAFactory(ModuleA module) {
assert module != null;
this.module = module;
}
@Override
public Integer get() {
return Preconditions.checkNotNull(
module.provideIntA(), "Cannot return null from a non-@Nullable @Provides method");
}
public static Factory create(ModuleA module) {
return new ModuleA_ProvideIntAFactory(module);
}
/** Proxies {@link ModuleA#provideIntA()}. */
public static int proxyProvideIntA(ModuleA instance) {
return instance.provideIntA();
}
}
public final class ModuleA_ProvideIntBFactory implements Factory {
private final ModuleA module;
public ModuleA_ProvideIntBFactory(ModuleA module) {
assert module != null;
this.module = module;
}
@Override
public Integer get() {
return Preconditions.checkNotNull(
module.provideIntB(), "Cannot return null from a non-@Nullable @Provides method");
}
public static Factory create(ModuleA module) {
return new ModuleA_ProvideIntBFactory(module);
}
/** Proxies {@link ModuleA#provideIntB()}. */
public static int proxyProvideIntB(ModuleA instance) {
return instance.provideIntB();
}
}
3. MainActivity_MembersInjector
无变化
4. DaggerClassAComponent
public final class DaggerClassAComponent implements ClassAComponent {
private Provider provideIntAProvider;
private Provider provideIntBProvider;
private Provider classAProvider;
private MembersInjector mainActivityMembersInjector;
private DaggerClassAComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideIntAProvider = ModuleA_ProvideIntAFactory.create(builder.moduleA);
this.provideIntBProvider = ModuleA_ProvideIntBFactory.create(builder.moduleA);
this.classAProvider = ClassA_Factory.create(provideIntAProvider, provideIntBProvider);
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(classAProvider);
}
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
public static final class Builder {
private ModuleA moduleA;
private Builder() {}
public ClassAComponent build() {
if (moduleA == null) {
throw new IllegalStateException(ModuleA.class.getCanonicalName() + " must be set");
}
return new DaggerClassAComponent(this);
}
public Builder moduleA(ModuleA moduleA) {
this.moduleA = Preconditions.checkNotNull(moduleA);
return this;
}
}
}
该文件的作用还是把一切整合起来。关键就是顺序问题,@Named
标注让initialize
方法知道要用什么顺序来构造。
到目前为止,ClassA
是我们可以随意更改的,但现实开发中很多时候我们并不能做到,比如其来自第三方库。也就是说我们无法对其构造器添加@Inject
或@Named
标注,那又怎么办呢?下期就会解决这个问题。