上一期介绍了@Scope
的含义和用法,明白了Dagger2只认这么一个标注,而且认为标注的Component
为单例。那么假如我们想要实现真正的@PerActivity
,即Activity
范围内为单例,或者是其余的自定义范围标注,要如何做到呢?
范围标注也没有魔法,换言之不是标了@PerActivity
它就自动实现Activity
内单例。更确切地讲,是因为我们希望Activity
内单例,而且Dagger也会帮我们实现Activity
内单例,才给它标注@PerActivity
。
这里就不得不提Component dependency
与Subcomponent
。它们是不同的东西,但功能非常类似,都能使Component
“层次化”。“层次化”的含义是,Component
出现上下级之分,下级Component
依赖于上级Component
的参数与提供函数,然而又可以使用不同的范围标注。(事实上,Dagger要求下级Component
必须使用不同的范围标注,道理也很简单,相同范围内上级Component
功能完全被下级覆盖,没有存在的必要性。)
由于篇幅所限,本期只介绍Component dependency
方法。
所谓Component dependency
就是在上级Component
内创建提供函数,让下级Component
通过标注的形式来依赖上级Component
(如果还有下下级,需要上级的参数,还可继承上级的接口Component
,这样下下级也可以访问到上级的提供函数)。
如此一来,我们可以让Activity
拥有自己的专属Component
即ActivityComponent
,然后每次onCreate
的时候从上级AppComponent
里面造一个下级ActivityComponent
,这样还是只需要在初始化上级``AppComponent时提供一次构造参数(当然如果下级
ActivityComponent加入了新的
Module的话还是要提供新参数),同时又因为下级
ActivityComponent是独立的,如果只在此
Activity里面使用,就真正实现了
@PerActivity`。
接下来我们将:创建AppComponent
,@PerApplication
标注,负责注入ClassA
;
创建ActivityComponent
,@PerActivity
标注,负责注入ClassB
。ActivityComponent
依赖于AppComponent
。
共同代码
无论如何,先定义好两个范围标注:
@Scope
@Documented
@Retention(RUNTIME)
public @interface PerApplication {}
@Scope
@Documented
@Retention(RUNTIME)
public @interface PerActivity {}
然后是ClassA
与ClassB
的代码,都在构造器内添加Log以帮助了解构造器的调用:
public class ClassA {
private int a;
private int b;
private static final String TAG = "ClassA";
public ClassA(int a, int b) {
this.a = a;
this.b = b;
Log.d(TAG, "classA constructor called");
}
public int getA() {
return a;
}
public int getB() {
return b;
}
}
public class ClassB {
private static final String TAG = "ClassB";
private ClassA classA;
private int a;
public ClassB(ClassA classA, int a) {
this.classA = classA;
this.a = a;
Log.d(TAG, "classB constructor called");
}
public ClassA getClassA() {
return classA;
}
public int getA() {
return a;
}
}
再把之前关于ClassBComponent
与ModuleB
的代码删掉。注意运行前rebuild
一下,避免之前生成的代码造成影响。
Component Dependency
主体代码
使用这种方式,上级Component
可以暴露自己的组件给下级Component
,方法就是创建一个返回想要暴露的类型的函数,名字还是无所谓。比如这里的AppComponent
是这样:
@PerApplication
@Component(modules = AppModule.class)
public interface AppComponent {
void inject(MyApp myApp);
ClassA getClassA();
}
对应的AppModule
:
@Module
public class AppModule {
private int a;
private int b;
public AppModule(int a, int b) {
this.a = a;
this.b = b;
}
@Provides
@Named("a")
int provideIntA() {
return a;
}
@Provides
@Named("b")
int provideIntB() {
return b;
}
@PerApplication
@Provides
ClassA provideClassA(@Named("a") int a, @Named("b") int b) {
return new ClassA(a, b);
}
}
注意在AppComponent
里面我们通过加入ClassA getClassA();
暴露出ClassA
的@Provides
函数给其下级,这样下级就可以从上级获取ClassA
而不用自己再写一遍。
然后就是ActivityComponent
:
@PerActivity
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}
注意这种声明依赖的形式。
有时候我们会看到有些项目中,下级Component
不仅在标注上声明了dependencies
,还继承了上级Component
的接口。其实接口继承就是单纯地继承上级中的暴露函数,往往这个Component
还有下级。
如果是最下级的Component
,并没有理由这么做。
然后是ActivityModule
:
@Module
public class ActivityModule {
private int c;
public ActivityModule(int c) {
this.c = c;
}
@Provides
public int provideC() {
return this.c;
}
@PerActivity
@Provides
public ClassB provideClassB(ClassA classA, int c) {
return new ClassB(classA, c);
}
}
然后就是MyApp
代码:
public class MyApp extends Application {
private static MyApp appInstance = null;
private static AppComponent appComponent = null;
@Inject ClassA classa;
@Override
public void onCreate() {
super.onCreate();
appInstance = this;
appComponent = DaggerAppComponent.builder().appModule(new AppModule(2, 3)).build();
appComponent.inject(this);
}
public static MyApp getAppInstance() {
return appInstance;
}
public static AppComponent getAppComponent() {
return appComponent;
}
}
MainActivity
代码:
public class MainActivity extends AppCompatActivity {
@Inject ClassB classB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerActivityComponent.builder()
.activityModule(new ActivityModule(4))
.appComponent(MyApp.getAppComponent())
.build().inject(this);
}
}
重复一遍:删除之前ClassBComponent
与ModuleB
的代码,rebuild
再运行。运行发现两个Log分别被打印一次。这说明了A类和B类都只实例化了一次,也就是说B类的注入确实依赖了之前注入的A类。
生成代码
现在有5个工厂类,AppModule_ProvideIntAFactory
,AppModule_ProvideIntBFactory
,AppModule_ProvideClassAFactory
,ActivityModule_ProvideCFactory
与ActivityModule_ProvideClassBFactory
。
两个注入器MyApp_MembersInjector
与MainActivity_MembersInjector
。这些代码都和之前的非常雷同。
我们还是把更多精力投入在两个关键类DaggerAppComponent
与DaggerActivityComponent
:
public final class DaggerAppComponent implements AppComponent {
private Provider provideIntAProvider;
private Provider provideIntBProvider;
private Provider provideClassAProvider;
private MembersInjector myAppMembersInjector;
private DaggerAppComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideIntAProvider = AppModule_ProvideIntAFactory.create(builder.appModule);
this.provideIntBProvider = AppModule_ProvideIntBFactory.create(builder.appModule);
this.provideClassAProvider =
DoubleCheck.provider(
AppModule_ProvideClassAFactory.create(
builder.appModule, provideIntAProvider, provideIntBProvider));
this.myAppMembersInjector = MyApp_MembersInjector.create(provideClassAProvider);
}
@Override
public void inject(MyApp myApp) {
myAppMembersInjector.injectMembers(myApp);
}
@Override
public ClassA getClassA() {
return provideClassAProvider.get();
}
public static final class Builder {
private AppModule appModule;
private Builder() {}
public AppComponent build() {
if (appModule == null) {
throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
}
return new DaggerAppComponent(this);
}
public Builder appModule(AppModule appModule) {
this.appModule = Preconditions.checkNotNull(appModule);
return this;
}
}
}
这个类就本身而言和上一期的差距不是很大,可以看出还是用了DoubleCheck
封装,说明确实Dagger把@PerApplication
也看做单例。
唯一的区别在于,多了getClassA()
这个函数。这是自然的,因为这个类说到底是我们定义的AppComponent
接口的实现类,自然要实现里面的抽象方法。
接下来这个就更值得仔细研究了:
public final class DaggerActivityComponent implements ActivityComponent {
private Provider getClassAProvider;
private Provider provideCProvider;
private Provider provideClassBProvider;
private MembersInjector mainActivityMembersInjector;
private DaggerActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.getClassAProvider =
new daggerplay_com_daggerplay_components_AppComponent_getClassA(builder.appComponent);
this.provideCProvider = ActivityModule_ProvideCFactory.create(builder.activityModule);
this.provideClassBProvider =
DoubleCheck.provider(
ActivityModule_ProvideClassBFactory.create(
builder.activityModule, getClassAProvider, provideCProvider));
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideClassBProvider);
}
@Override
public void inject(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
public static final class Builder {
private ActivityModule activityModule;
private AppComponent appComponent;
private Builder() {}
public ActivityComponent build() {
if (activityModule == null) {
throw new IllegalStateException(ActivityModule.class.getCanonicalName() + " must be set");
}
if (appComponent == null) {
throw new IllegalStateException(AppComponent.class.getCanonicalName() + " must be set");
}
return new DaggerActivityComponent(this);
}
public Builder activityModule(ActivityModule activityModule) {
this.activityModule = Preconditions.checkNotNull(activityModule);
return this;
}
public Builder appComponent(AppComponent appComponent) {
this.appComponent = Preconditions.checkNotNull(appComponent);
return this;
}
}
private static class daggerplay_com_daggerplay_components_AppComponent_getClassA
implements Provider {
private final AppComponent appComponent;
daggerplay_com_daggerplay_components_AppComponent_getClassA(AppComponent appComponent) {
this.appComponent = appComponent;
}
@Override
public ClassA get() {
return Preconditions.checkNotNull(
appComponent.getClassA(), "Cannot return null from a non-@Nullable component method");
}
}
}
这个类创造了一个新的内部类,这里是daggerplay_com_daggerplay_components_AppComponent_getClassA
。名字倒是其次,其作用就是接受AppComponent
的实例,然后封装成为ClassA
的工厂。我们在MainActivity
里面传入的AppComponent
的实例正是被如此转化成为了依赖工厂来提供ClassA
实例。另外由于AppComponent
也使用了@PerActivity
标注,注入时还是使用上一期介绍的单例注入方法,有兴趣的同学可以自己动手尝试。
至此,我们明白了如果根据环境的结构层次来对Dagger2进行分级以及拓展,不用担心AppComponent
里面注入太多Application层并用不到的东西,也不用担心在写Activity的时候还要把Application里面的东西都再重复一遍了,是不是很有用呢?
下期继续介绍Subcomponent
的使用。