前言
警告!這不是一個乾貨的文章!
個人認為,學技術不宜太浮躁。對於一項技術一味地追求乾貨其實並不一定有用,貨太乾容易噎著,哈哈~不如循序漸進慢下來,一點點去體會技術的前因後果。(這個系列適合於:瞭解但沒有在專案裡大規模應用Dagger2的讀者)
出來混遲早要還的,技術債Dagger2:Android篇(上)
出來混遲早要還的,技術債Dagger2:Android篇(中)@Scope、@Singleton
本以為閱讀一些文件,寫一些Demo就能駕馭工作中的專案...我錯了,我再也不會有這麼愚蠢的想法了... 這麼多依賴關係,誰扛得住啊!所以還是一點點來吧。
正文
前倆篇文章過後,我猜大家對下面的程式碼已經很熟悉了:
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
SharedPreferences getSharedPrefs();
}
@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);
}
}
DaggerAppComponent appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
複製程式碼
很基本,很簡單的Dagger應用。不過大家有沒有感覺到這個appModule(new AppModule(this))
特別煩?安利我用的時候,說依賴注入,看不見new。我哼哧哼哧寫這麼多,這不還是new出來的?
那麼問題來了,是不是可以不需要appModule(new AppModule(this))
呢?當然可以。用過Dagger-Android
的朋友,肯定很清楚,的確看不到任何new。那麼這篇文章,我們們就來看看如何徹底不用new。
當然這也是Dagger-Android的原理
@Component.Builder
做這一切的前提是這個註解。這個註解是幹啥的呢?說白了,給Component提供Module的依賴。
我們們先來想一個問題,下面程式碼存在的意義:
DaggerAppComponent appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
複製程式碼
Dagger為我們所需的依賴生成例項,那麼必然需要構建整個依賴關係網。Dagger就算是再神奇,也不可能憑空把整個我們所需要依賴關係構建出來,所以需要我們適時的“提供和引導”。那麼new AppModule(this)
就是給Dagger進行提供,因為從我們上述的程式碼中,Dagger是不知道該怎麼去例項化這個AppModule
,因此需要我們對其進行提供。
那麼話又說回來,我們在上述的程式碼中,告訴它如何去例項化AppModule
,不就可以避免我們手動去new AppModule(this)
了麼?
沒錯,@Component.Builder
就是做這個的。
累死了,繞了一圈不知道大家理沒理解@Component.Builder存在的含義了。
AppModule(Application application)
對於我們的AppModule
來說,例項化它的關鍵是如何提供一個Application
。
public AppModule(Application application) {
this.application = application;
}
複製程式碼
對於Dagger也是如此,它不能例項化AppModule
的原因是它不知道或者說沒辦法去獲取一個Application
例項。因此,對於Dagger來說,我們應該給它提供Application
的例項,而非AppModule
。
開始改造
改造需要一步步來:第一步,我們使用@Component.Builder
去改造AppComponent
:
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
SharedPreferences getSharedPrefs();
// 改造內容
@Component.Builder
interface Builder {
AppComponent build();
// 此時還未改造這行程式碼
Builder appModule(AppModule appModule);
}
}
複製程式碼
現在我們告訴了Dagger,你要以
@Component.Builder
註解的介面那樣去例項化我們所需的AppComponent
。
是不是發現這裡新增的方法和DaggerAppComponent
生成的程式碼很像?沒錯Dagger預設例項化AppComponent
就是以這種程式碼進行的。
不過,對於我們這個AppModule
來說,我們不需要關係它是怎麼初始化(因為我們只需要它所提供給我們的依賴)。對於Dagger來說也是如此:
Dagger想要為我們提供被@Provides
標註的依賴,只需要擁有Application
例項即可。因為只要擁有Application
例項Dagger就有辦法例項化AppModule
,直接new即可。
所以這裡我們需要一種方式來告訴Dagger:我要提供給你,在@Module
中需要的內容。對於我們們的這個demo來說,我們們需要用一種方式把Dagger所需要的Application
給他。
而做到這一點的就是@BindsInstance
。
@BindsInstance
按照官網的介紹,此註解用於標識Component Builder/SubComponent Builder中的某個方法,該方法允許將例項繫結到Component中。
所以對於我們的AppModule
來說,它只需要提供@Provides
的內容就可以了!,它所需要的,Dagger會按照我們的“指示”,注入進來。也就是這個樣子:
@Module
public class AppModule {
@Provides
@Singleton
// 外部傳入Application例項(Dagger會按照我們的“指示”,注入進來)
public SharedPreferences providePreferences(Application application) {
return application.getSharedPreferences("store", Context.MODE_PRIVATE);
}
}
複製程式碼
而我們的AppConponent
這樣就可以了:
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
SharedPreferences getSharedPrefs();
@Component.Builder
interface Builder {
AppComponent build();
@BindsInstance
// 此時的application就會被,注入到AppModule的providePreferences方法中
Builder application(Application application);
}
}
複製程式碼
build過後,我們初始化DaggerAppComponent
,只需如此寫:
DaggerAppComponent appComponent = DaggerAppComponent.builder()
.application(this)
.build();
複製程式碼
那麼Dagger是如何為我們生成DaggerAppComponent
的呢?
public final class DaggerAppComponent implements AppComponent {
private Provider<Application> applicationProvider;
private Provider<SharedPreferences> providePreferencesProvider;
private DaggerAppComponent(Builder builder) {
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(
AppModule_ProvidePreferencesFactory.create(builder.appModule, applicationProvider));
}
@Override
public void inject(MainActivity mainActivity) {}
@Override
public SharedPreferences getSharedPrefs() {
return providePreferencesProvider.get();
}
private static final class Builder implements AppComponent.Builder {
private AppModule appModule;
private Application application;
@Override
public AppComponent build() {
if (appModule == null) {
this.appModule = new AppModule();
}
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;
}
}
}
複製程式碼
對於AppModule
來說,只是簡單的new出來,當我們需要@Provides
時,只是將所需的application
傳進去。而我們的application
以成員變數的身份呆在了DaggerAppComponent
“體內”。
尾聲
這篇內容,其實並不是為了去講@Component.Builder
和@BindsInstance
。而是儘可能的通過它們,來加深大家去Component和Module的理解。去感受Dagger的實現,更好的去理解依賴注入。對與我們而言,需要用其形,學其神。
這樣我們才不會受制於框架,而是駕馭框架。