Dagger 2 : Component.Builder 这篇博客是系列博客Dagger 2 Android : Defeat the Dahaka的其中一部分。如果你想了解更多有关 Dahaka
的内容,可以去查看其它博客,它们教我们如何去打败 Dahaka
以及说明了写这些博客的原因。
这里说明下 Dahaka
是个什么鬼。达哈卡(Dahaka)是游戏《波斯王子:武者之心》中的一个怪物(在这个系列博客里面指的是各个对象之间的依赖关系),同时也是隐藏结局里的最终BOSS。反正两者都很让人头疼。里面还有个重要的道具--时之匕首,其实也有暗指 Dagger
的意思,看来原作者是这游戏的忠实粉,不过她的印式英语真是很难听懂(后面有YouTube的视频)。
我们从一个简单的例子开始。 定义一个 AppComponent
代码如下:
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
SharedPreferences getSharedPrefs();
}
复制代码
AppModule
的代码:
@Module
public class AppModule {
Application application;
public AppModule(Application application) {
this.application = application;
}
@Provides
Application providesApplication() {
return application;
}
@Provides
@Singleton
public SharedPreferences providePreferences() {
return application.getSharedPreferences(DATA_STORE,
Context.MODE_PRIVATE);
}
}
复制代码
可以在我们的App类中的onCreate实例化 Component
:
@Override
public void onCreate() {
super.onCreate();
AppComponent appComponent = DaggerAppComponent.builder()
.appMoudle(new AppMoudle(this))
.build();
}
复制代码
下面是 Dagger
自动帮我们生成的 Builder
类:
DaggerAppComponent 类
public final class DaggerAppComponent implements AppComponent {
public static Builder builder() {
return new Builder();
}
...
public static final class Builder {
private AppMoudle appMoudle;
private Builder() {}
public AppComponent build() {
if (appMoudle == null) {
throw new IllegalStateException(AppMoudle.class.getCanonicalName() + " must be set");
}
return new DaggerAppComponent(this);
}
public Builder appMoudle(AppMoudle appMoudle) {
this.appMoudle = Preconditions.checkNotNull(appMoudle);
return this;
}
}
...
}
复制代码
其中 Builder
类的 appMoudle
成员变量。它通过appMoudle
方法传递进来的。 在 App
的 OnCreate
方法中:
AppComponent appComponent = DaggerAppComponent.builder()
.appMoudle(new AppMoudle(this))
.build();
复制代码
上面的代码都是Daggeer
自动生成的 DaggerAppComponent
类代码,可以在 app/build/generated/source/apt/debug/你的包名/DaggerAppComponent.java
目录下找到
回到本文的重点:@Component.Builder
能干吗? 它可以让我们自定义上面 Dagger
生成的 Builer
类。 我们先看下 @Component.Builder
的定义。
A builder for a component. Components may have a single nested static abstract class or interface annotated with @Component.Builder. If they do, then the component's generated builder will match the API in the type.--source
让我们在 AppComponent
中使用 @Component.Builder
:
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
SharedPreferences getSharedPrefs();
@Component.Builder
interface Builder {
AppComponent build();
Builder appModule(AppModule appModule);
}
}
复制代码
通过使用 Component.Builder
来注解接口,Dagger
会自动生成跟上面完全相同的 Builder
类。可以自己去查看生成的 DaggerAppComponent
类。
这样还达不到自定义的目的。如何实现自定义Builder
类? 这时候我们就需要使用到 @BindsInstance
注解了。
什么是 @BindsInstance
? 下面是它的定义:
Marks a method on a component builder or subcomponent builder that allows an instance to be bound to some type within the component.
WHAAT? 这是什么鬼,其实我也不能理解,哈哈。
@BindsInstance methods should be preferred to writing a @Module with constructor arguments and immediately providing those values.--source
这句话大概意思:我们经常通过构造函数来给某些成员变量赋值,在上面的 AppModule
也是通过构造函数来给成员 application
初始化。@BindsInstance
它可以让我们省去写这类构造函数,通过它能够为类的成员变量赋值。
public AppModule(Application application) {
this.application = application;
}
复制代码
现在我们去掉 AppModule
的构造函数以及 @Provides
注解的 providesApplication
函数:
@Module
public class AppModule {
@Provides
@Singleton
public SharedPreferences providePreferences(
Application application) {
return application.getSharedPreferences(
"store", Context.MODE_PRIVATE);
}
}
复制代码
这是我们通过 @Component.Builder
自定义的 AppComponent
类:
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
SharedPreferences getSharedPrefs();
@Component.Builder
interface Builder {
AppComponent build();
@BindsInstance Builder application(Application application);
}
}
复制代码
跟前面的代码相比,我们去掉了 Builder appModule(AppModule appModule);
函数,并添加了一行 @BindsInstance Builder application(Application application);
通过它我们就可以为 AppModule
类里面的成员方法提供 application
参数。下面会一步步说明是如何实现的。
首先看下初始化的改变:
初始化 DaggerAppComponent
DaggerAppComponent appComponent = DaggerAppComponent.builder()
.application(this)
.build();
复制代码
注意:这里我们少了
appModule(AppModule appModule)
, 并通过AppModule
默认构造函数(即无参构造函数)去生成AppModule
实例(该实例是Builder
类的成员变量)。
下面是修改后 Dagger
生成 DaggerAppComponent
类的代码:
public final class DaggerAppComponent implements AppComponent {
...
private static final class Builder implements AppComponent.Builder {
private AppMoudle appMoudle;
private Application application;
@Override
public AppComponent build() {
if (appMoudle == null) {
this.appMoudle = new AppMoudle();
}
if (application == null) {
throw new IllegalStateException(Application.class.getCanonicalName() + " must be set");
}
return new DaggerAppComponent(this);
}
@Override
public Builder application(Application application) {
this.application = Preconditions.checkNotNull(application);
return this;
}
}
}
复制代码
这时我们发现Builder
类有两个成员变量了,其中 this.appMoudle = new AppMoudle();
,所以成员变量 appMoudle
通过默认构造函数生成, 另一个成员变量 application
是并通过 @BindsInstance
注解过的方法 public Builder application(Application application)
来初始化的。
注意:前面的
Builder
类是没有application
成员变量的,它是AppMoudle
类的成员变量。
现在 AppMoudle
类没有了 application
成员变量了,那么 AppMoudle
类里面的成员方法 public SharedPreferences providePreferences(Application application)
的 application
参数如何来的?
下面看 Dagger
根据 AppMoudle
自动生成的 *_ProvidePreferencesFactory
类: AppMoudle_ProvidePreferencesFactory 类
public final class AppMoudle_ProvidePreferencesFactory implements Factory<SharedPreferences> {
private final AppMoudle module;
private final Provider<Application> applicationProvider;
public AppMoudle_ProvidePreferencesFactory(
AppMoudle module, Provider<Application> applicationProvider) {
assert module != null;
this.module = module;
assert applicationProvider != null;
this.applicationProvider = applicationProvider;
}
@Override
public SharedPreferences get() {
return Preconditions.checkNotNull(
module.providePreferences(applicationProvider.get()),
"Cannot return null from a non-@Nullable @Provides method");
}
public static Factory<SharedPreferences> create(
AppMoudle module, Provider<Application> applicationProvider) {
return new AppMoudle_ProvidePreferencesFactory(module, applicationProvider);
}
}
复制代码
还有 DaggerAppComponent
类:
private Provider<Application> applicationProvider;
private Provider<SharedPreferences> providePreferencesProvider;
private DaggerAppComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static AppComponent.Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.applicationProvider = InstanceFactory.create(builder.application);
this.providePreferencesProvider =
DoubleCheck.provider(
AppMoudle_ProvidePreferencesFactory.create(builder.appMoudle, applicationProvider));
}
@Override
public void inject(MainActivity mainActivity) {
MembersInjectors.<MainActivity>noOp().injectMembers(mainActivity);
}
@Override
public SharedPreferences getShasredPrefs() {
return providePreferencesProvider.get();
}
复制代码
- 可以发现
application
参数是通过成员applicationProvider
的get()
方法拿到的。 applicationProvider
又是通过create
方法传递进来的。create
方法是在DaggerAppComponent
类的initialize
方法中被调用了。DaggerAppComponent
类的applicationProvider
则是通过Builder
类的application
成员变量生成的(this.applicationProvider = InstanceFactory.create(builder.application);
)。Builder
类的application
成员变量是通过@BindsInstance
注解的方法传入的。
然后一切就通了。
总结:
- 如果使用
@Component.Builder
和@BindsInstance
来自定义Builder
类,那么被@BindsInstance
注解方法里面的参数在Builder
类中都有对应的成员变量。 - 可以通过
Builder
类中的成员变量在Component
中生成对应的Provider<T> **Provider
成员,通过**Provider
成员的get()
就可以拿到依赖的实例了。
使用场景:
- 如果我们的
Moudle
某个被@Provides
注解的provide**(xxx obj)
函数使用了某种类型的对象作为参数(比如application
),而这个参数如果不想通过Moudle
构造函数传递进来,那么就可以使用@Component.Builder
来自定义Builder
类,并通过@BindsInstance
注解的方法来提供这个参数obj
。 - 或者直接给
Component
直接添加一个对象依赖的提供函数。
根据这个总结比如我们添加一个全局的 Gson
对象: 1.AppComponent
void inject(MainActivity mainActivity);
SharedPreferences getShasredPrefs();
Gson getGson();
@Singleton
@Component.Builder
interface Builder{
AppComponent build();
@BindsInstance
Builder application(Application application);
@BindsInstance
Builder gson(Gson gson);
复制代码
2.App
类里面:
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.application(this)
.gson(new Gson())
.build();
}
复制代码
3.然后在我们的MainActivity
中:
String json = "{\"name\":\"Pikachu\", \"id\":25}";
@Inject
Gson mgson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerLearnApp.getAppComponent().inject(this);
}
@Override
protected void onResume() {
super.onResume();
Pokemon pokemon = mgson.fromJson(json, Pokemon.class);
Log.i(TAG, "pokemon name:" + pokemon.getName());
}
复制代码
通过这种方式可以去掉 @Module
注解 AppMoudle
类里面的 provideGson
方法,同时还去掉了 @Singleton
避免了 Lazy
加载,但是效果却是一样的。
@Module
abstract class ExecutorModule {
@Provides
@Singleton
@Background Executor provideGson() {
return Executors.newCachedThreadPool(4);
}
}
复制代码
Subcomponent.Builder
@Subcomponent.Builder
也是类似的,不过它是在 subcomponents
中使用。