Dagger 2 : Component.Builder 這篇博客是系列博客Dagger 2 Android : Defeat the Dahaka的其中一部分。若是你想了解更多有關 Dahaka
的內容,能夠去查看其它博客,它們教咱們如何去戰勝 Dahaka
以及說明了寫這些博客的緣由。html
這裏說明下 Dahaka
是個什麼鬼。達哈卡(Dahaka)是遊戲《波斯王子:武者之心》中的一個怪物(在這個系列博客裏面指的是各個對象之間的依賴關係),同時也是隱藏結局裏的最終BOSS。反正二者都很讓人頭疼。裏面還有個重要的道具--時之匕首,其實也有暗指 Dagger
的意思,看來原做者是這遊戲的忠實粉,不過她的印式英語真是很難聽懂(後面有YouTube的視頻)。java
咱們從一個簡單的例子開始。 定義一個 AppComponent
代碼以下:android
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
SharedPreferences getSharedPrefs();
}
複製代碼
AppModule
的代碼:git
@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
:github
@Override
public void onCreate() {
super.onCreate();
AppComponent appComponent = DaggerAppComponent.builder()
.appMoudle(new AppMoudle(this))
.build();
}
複製代碼
下面是 Dagger
自動幫咱們生成的 Builder
類:json
DaggerAppComponent 類api
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
方法中:app
AppComponent appComponent = DaggerAppComponent.builder()
.appMoudle(new AppMoudle(this))
.build();
複製代碼
上面的代碼都是Daggeer
自動生成的 DaggerAppComponent
類代碼,能夠在 app/build/generated/source/apt/debug/你的包名/DaggerAppComponent.java
目錄下找到ide
回到本文的重點:@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
也是相似的,不過它是在 subcomponents
中使用。