前言
??Dagger是幫助實現(xiàn)依賴注入的庫,雖然很多人都知道依賴注入對于架構(gòu)設(shè)計的重要性,但是Dagger學(xué)習(xí)曲線十分陡峭,官方文檔更是看了幾遍也很難消化。本文旨在通過一篇文章來讓大家看懂并上手Dagger。
??Dagger最早由JakeWharton在square公司開發(fā)。后來轉(zhuǎn)由Google維護(hù)并發(fā)展為Dagger2。Dagger2區(qū)別于Dagger1的地方主要在于兩個,一個是由運行時通過反射構(gòu)建依賴關(guān)系變?yōu)榫幾g期通過注解生成依賴關(guān)系,另一個是出錯時有更好地提醒(當(dāng)然這也是因為Dagger2在編譯期間根據(jù)注解生成好了可讀性較好的代碼帶來的優(yōu)勢)。 轉(zhuǎn)載請注明來源「Bug總柴」
參考
初學(xué)者建議先不要看官方文檔,可以先看這幾篇博客:
依賴注入
在學(xué)習(xí)Dagger之前,我們先來了解一下依賴注入。
什么是依賴注入
??依賴注入,顧名思義,就是說當(dāng)代碼執(zhí)行過程中需要某個服務(wù)對象的時候,不是通過當(dāng)前代碼自己去構(gòu)造或者去查找獲取服務(wù)對象,而是通過外部將這個服務(wù)對象傳給當(dāng)前代碼。
??這樣做的好處在于當(dāng)服務(wù)對象構(gòu)建或者獲取方法改變時,不需要改變調(diào)用方的代碼,這也是S.O.L.I.D原則中開發(fā)封閉原則的具體表現(xiàn)。
如何實現(xiàn)依賴注入
在不使用Dagger等依賴注入庫的情況下,我們可以通過以下三種方式手動實現(xiàn)依賴注入。
- 構(gòu)造器依賴注入
// Constructor
Client(Service service) {
// Save the reference to the passed-in service inside this client
this.service = service;
}
- Setter方法依賴注入
// Setter method
public void setService(Service service) {
// Save the reference to the passed-in service inside this client.
this.service = service;
}
- 接口依賴注入
// Service setter interface.
public interface ServiceSetter {
public void setService(Service service);
}
// Client class
public class Client implements ServiceSetter {
// Internal reference to the service used by this client.
private Service service;
// Set the service that this client is to use.
@Override
public void setService(Service service) {
this.service = service;
}
}
Dagger2基本概念
Dagger2可以理解成就是在編譯階段根據(jù)注解構(gòu)建一個依賴關(guān)系圖,然后根據(jù)依賴關(guān)系圖之間的依賴關(guān)系生成對象工廠類,在需要的地方注入對象。如何使用注解構(gòu)造一個依賴關(guān)系圖是Dagger2使用的關(guān)鍵。在了解注解之前,我們先來認(rèn)識一下以下三個概念:
bindings
bindings的概念是告訴Dagger注入器如何能得到一個具體類。有幾種方法可以表示當(dāng)前代碼可以提供某個類型的對象:
- 通過使用
@Provides注解的非抽象方法返回一個類對象
@Provides
public Fruit providerApple() {
return new Apple();
}
- 通過
@Binds注解的抽象方法,該抽象方法返回接口或抽象類,參數(shù)是一個該接口或者抽象類的具體實現(xiàn)類
@Binds
abstract Fruit bindApple(Apple apple);
- 通過
@Inject注解的構(gòu)造方法
public class Apple implements Fruit {
@Inject
public Apple() {
}
}
- 通過
multibindings(@MapKey后面提到)或者producers(暫不細(xì)說)提供
modules
module是一個只有@Provides和@Binds方法的類,用于集合所有的依賴關(guān)系。同時module可以通過inculdes來引入其他module從而得到其他module的依賴關(guān)系集合。例如:
@Module(includes = ProjectModule.class)
public abstract class FruitModule {
@Binds
abstract Fruit bindApple(Apple apple);
@Provides
static Desk provideDesk() {
return new Desk();
}
}
components
component是被@Component標(biāo)注的接口或者抽象類,Dagger會負(fù)責(zé)實例化一個component。component中指定需要的modules,代表著這次依賴構(gòu)建所有需要的全部依賴關(guān)系都可以從modules中找到。compoent中的方法只能是無參的,且這個無參方法的返回值就是Dagger最終需要構(gòu)建得到的實體。可以說構(gòu)建component中無參方法的返回值對象就是整個依賴關(guān)系查找的起源點。在構(gòu)建這個實體時,如果遇到依賴,就會從modules中不斷地傳遞查找,直到所有的依賴都被找到為止。如果中間有某些依賴沒有注明實例化方式,Dagger會在編譯期間報錯。具體component的一個例子如下:
@Component(modules = {FruitModule.class, ProjectModule.class})
public interface FruitComponent {
FruitShop inject();
}
bindings\modules\components的依賴關(guān)系圖可以表示為下圖所示

Dagger2注解
知道上面的概念可以看懂基本的Dagger代碼。不過Dagger有非常多幫助完成依賴關(guān)系圖構(gòu)建的注解,只有把這些注解都弄懂了,才能真正看懂Dagger2的代碼。下面兩個圖可以看到一些常用的注解:


下面我們來一一介紹一下。
@Inject
@Inject是javax.inject包中的注解,可以用于對類的構(gòu)造函數(shù)、成員變量和方法。
用于類構(gòu)造器中表示該類在依賴注入時使用被注解的構(gòu)造器創(chuàng)建對象。
例如:
public class FruitShop {
@Inject
public FruitShop() {
}
}
表示當(dāng)其他地方依賴于FruitShop對象時,會使用FruitShop的默認(rèn)構(gòu)造方法進(jìn)行創(chuàng)建。當(dāng)被@Inject注解的構(gòu)造函數(shù)是有參數(shù)的,那么Dagger會同時對其參數(shù)進(jìn)行注入。例如:
public class FruitShop {
@Inject
public FruitShop(Desk desk) {
}
}
當(dāng)需要構(gòu)建依賴關(guān)系時,在創(chuàng)建FruitShop的時候回對參數(shù)desk進(jìn)行注入。
在生成的FruitShop_Factory.java代碼中,可以看到以下方法:
public final class FruitShop_Factory implements Factory<FruitShop> {
private final Provider<Desk> deskProvider;
public FruitShop_Factory(Provider<Desk> deskProvider) {
this.deskProvider = deskProvider;
}
public static FruitShop provideInstance(Provider<Desk> deskProvider) {
FruitShop instance = new FruitShop(deskProvider.get());
return instance;
}
}
@Inject用于構(gòu)造函數(shù)需要注意兩點:
- 每個類只允許一個構(gòu)造方法注解為
@Inject,例如
public class FruitShop {
// 由于有另外的構(gòu)造函數(shù)注解了@Inject,這里不能再使用@Inject,否則編譯會出錯Error: Types may only contain one @Inject constructor.
public FruitShop() {
}
@Inject
public FruitShop(Location location) {
}
}
-
javax.inject.Inject文檔中說明當(dāng)被注解的構(gòu)造函數(shù)是public且無參的默認(rèn)構(gòu)造函數(shù)@Inject可以省略。但是實際Dagger2項目中,需要被注入的對象必須擁有@Inject注解的構(gòu)造方法或者通過@Porvides注解的方法提供,否則會報錯Error: cannot be provided without an @Inject constructor or an @Provides-annotated method.。這一點Dagger的處理與javax.inject.Inject描述表現(xiàn)不一致。
用于成員變量中表示該成員變量作為依賴需要被注入
例如:
public class FruitShop {
@Inject
Fruit apple;
}
表示FruitShop中需要依賴水果apple,并希望由外部注入進(jìn)來。
編譯之后我們會看到一個FruitShop_MembersInjector.java的類,里面會有一個這樣的方法:
public final class FruitShop_MembersInjector implements MembersInjector<FruitShop> {
// Dagger生成代碼中會通過MembersInjector給我們對象需要的屬性進(jìn)行注入
public static void injectApple(FruitShop instance, Fruit apple) {
instance.apple = apple;
}
}
對于屬性注解需要注意被注解的屬性不能是final或者被private修飾符修飾。其中的原因在上面instance.apple = apple;代碼中不言而喻。
在生成的FruitShop_Factory.java代碼中,可以看到以下方法:
public final class FruitShop_Factory implements Factory<FruitShop> {
private final Provider<Fruit> appleProvider;
public FruitShop_Factory(Provider<Fruit> appleProvider) {
this.appleProvider = appleProvider;
}
public static FruitShop provideInstance( Provider<Fruit> appleProvider) {
FruitShop instance = new FruitShop();
FruitShop_MembersInjector.injectApple(instance, appleProvider.get());
return instance;
}
}
用于方法中表示依賴于方法參數(shù)的類型會被注入
例如:
public class FruitShop {
Desk mDesk;
@Inject
public void setDesk(Desk desk) {
this.mDesk = desk;
}
}
被注解的setDesk()方法有一個Desk類型的參數(shù),意味著需要對Desk進(jìn)行依賴注入。Dagger生成的代碼如下所示:
public final class FruitShop_Factory implements Factory<FruitShop> {
private final Provider<Desk> deskProvider;
public FruitShop_Factory(Provider<Desk> deskProvider) {
this.deskProvider = deskProvider;
}
public static FruitShop provideInstance(Provider<Desk> deskProvider) {
FruitShop instance = new FruitShop();
FruitShop_MembersInjector.injectSetDesk(instance, deskProvider.get());
return instance;
}
}
public final class FruitShop_MembersInjector implements MembersInjector<FruitShop> {
public static void injectSetDesk(FruitShop instance, Desk desk) {
instance.setDesk(desk);
}
}
@Inject用于注解方法需要注意被注解的方法不能是private的。被注解的方法支持擁有多個參數(shù)。如果標(biāo)注在public方法上,Dagger2會在構(gòu)造方法執(zhí)行之后立即調(diào)用這個方法。
@Provides & @Module & @Component
使用@Inject來標(biāo)記依賴的注入不是什么時候都可以的,例如第三方api的代碼我們是不能修改的,沒辦法通過@Inject注解第三方api類的構(gòu)造器,從而沒辦法對第三方api的對象進(jìn)行構(gòu)建和依賴注入。這個時候我們可以使用@Provides來提供對應(yīng)的依賴。而@Provides必須放到一個被@Module注解的類中。例如:
// 通過在module中使用@Provides表示提供依賴的方法
@Module
public class FruitModule {
@Provides
Fruit provideApple() {
return new Apple();
}
}
// 使用@Inject說明需要依賴注入的地方
public class FruitShop {
// 這里需要提供一個Fruit類型的依賴
@Inject
Fruit apple;
@Inject
public FruitShop() {
}
}
// 將需要用到依賴的地方FruitShop和提供依賴的地方FruitModule綁定在一起
@Component(modules = FruitModule.class)
public interface FruitComponent {
FruitShop inject();
}
這里在module中聲明了一個可以提供Apple類依賴的方法provideApple()。并且component將依賴的需求方和提供方都綁定在了一起。我們來看生成的代碼
public final class FruitModule_ProvideAppleFactory implements Factory<Fruit> {
private final FruitModule module;
public FruitModule_ProvideAppleFactory(FruitModule module) {
this.module = module;
}
@Override
public Fruit get() {
return provideInstance(module);
}
public static Fruit provideInstance(FruitModule module) {
return proxyProvideApple(module);
}
public static FruitModule_ProvideAppleFactory create(FruitModule module) {
return new FruitModule_ProvideAppleFactory(module);
}
public static Fruit proxyProvideApple(FruitModule instance) {
return Preconditions.checkNotNull(
instance.provideApple(), "Cannot return null from a non-@Nullable @Provides method");
}
}
這段生成的代碼實際上是提供Apple類工廠FruitModule_ProvideAppleFactory,能夠通過provideApple()提供Apple對象。以下的代碼中,component通過傳遞FruitModule_ProvideAppleFactory對象到FruitShop_Factory中完成對FruitShop的依賴注入
public final class DaggerFruitComponent implements FruitComponent {
private FruitModule_ProvideAppleFactory provideAppleProvider;
private void initialize(final Builder builder) {
this.provideAppleProvider = FruitModule_ProvideAppleFactory.create(builder.fruitModule);
this.fruitShopProvider = DoubleCheck.provider(FruitShop_Factory.create(provideAppleProvider));
}
}
通過@Provides @Module @Component 三個注解就可以完成最基本的依賴注入關(guān)系圖的構(gòu)造,從而使用Dagger給依賴進(jìn)行注入。這里需要注意:
- 通過
@Provides注解的方法不能返回null,否則會報NullPointerException。如果@Provides方法可能返回null,那需要加上注入@Nullable,同時在需要依賴注入的地方加上@Nullable標(biāo)注。 - 一般module類都使用XXXModule命名,而provide方法一般都使用provideXXX命名方式。
@Binds
@Binds的作用和@Provides的作用是一樣的,是提供接口依賴的一種簡潔表示的方式。例如下面這個例子:
@Module
public class FruitModule {
@Provides
Fruit provideApple() {
return new Apple();
}
}
使用@Binds可以簡化為:
@Module
abstract public class FruitModule {
@Binds
abstract Fruit bindApple(Apple apple);
}
表示當(dāng)需要依賴Furit接口時,使用Apple實例對象進(jìn)行注入。需要注意的是,使用@Binds標(biāo)注的方法必須有且僅有一個方法參數(shù),且這個方法參數(shù)是方法返回值的實現(xiàn)類或者子類。
@Component
因為Componet較為復(fù)雜,拿出來再單獨說一下。Component的聲明如下:
public @interface Component {
Class<?>[] modules() default {};
Class<?>[] dependencies() default {};
@interface Builder {}
}
這代表著@Component的標(biāo)簽中除了可以指定modules之外還可以通過dependencies引用其他的component。在被@Component注解的類必須是接口或者抽象類,這個被注解的類中可以包含以下三個東西:
- 表示需要提供的依賴的方法,例如:
// 表示需要注入依賴生成SomeType類對象
SomeType getSomeType();
// 表示需要注入依賴生成Set<SomeType>對象,multibinding后面會介紹
Set<SomeType> getSomeTypes();
// 表示需要注入生成一個Qualifier為PortNumber的int整形,Qualifier后面會介紹
@PortNumber int getPortNumber();
// 表示需要注入依賴生成Provider<SomeType>對象,Provider<>后面介紹
Provider<SomeType> getSomeTypeProvider();
// 表示需要注入依賴生成Lazy<SomeType>對象,Lazy<>后面會介紹
Lazy<SomeType> getLazySomeType();
- 表示需要注入成員依賴的方法,
// 表示需要將someType中標(biāo)記為依賴的屬性和方法進(jìn)行注入
void injectSomeType(SomeType someType);
// 表示需要將someType中標(biāo)記為依賴的屬性和方法進(jìn)行注入,并返回SomeType對象
SomeType injectAndReturnSomeType(SomeType someType);
- 構(gòu)造Component的Builder
Dagger生成Component實現(xiàn)類時,會自動根據(jù)Bulder模式生成所需要Builder類。當(dāng)Component所依賴的Module為非抽象且默認(rèn)構(gòu)造函數(shù)為private時,則Dagger會生成對應(yīng)的有傳入module方法的Builder類,例如:
@Component(modules = ProjectModule.class)
public interface FruitComponent {
FruitShop inject();
}
@Module
public class ProjectModule {
private Desk mDesk;
private ProjectModule(){}
public ProjectModule(Desk desk){
mDesk = desk;
}
@Provides
public Desk provide() {
return mDesk;
}
}
則在生成的DaggerFruitComponent中會有以下Builder方法
public final class DaggerFruitComponent implements FruitComponent {
private ProjectModule projectModule;
public static final class Builder {
private ProjectModule projectModule;
private Builder() {}
public FruitComponent build() {
if (projectModule == null) {
throw new IllegalStateException(ProjectModule.class.getCanonicalName() + " must be set");
}
return new DaggerFruitComponent(this);
}
public Builder projectModule(ProjectModule projectModule) {
this.projectModule = Preconditions.checkNotNull(projectModule);
return this;
}
}
}
在調(diào)用時需要傳入依賴的module:
FruitShop fruitShop = DaggerFruitComponent
.builder()
.projectModule(new ProjectModule(new Desk()))
.build()
.inject();
當(dāng)Component所依賴的module和其他Componet都不需要使用有參的構(gòu)造函數(shù)的話,Component可以使用簡潔的create()方法,例如將上面的module改為:
@Module
public class ProjectModule {
@Provides
public Desk provide() {
return new Desk();
}
}
則生成的componet會是這樣的:
public final class DaggerFruitComponent implements FruitComponent {
public static FruitComponent create() {
return new Builder().build();
}
public static final class Builder {
private ProjectModule projectModule;
private Builder() {}
public FruitComponent build() {
if (projectModule == null) {
this.projectModule = new ProjectModule();
}
return new DaggerFruitComponent(this);
}
public Builder projectModule(ProjectModule projectModule) {
this.projectModule = Preconditions.checkNotNull(projectModule);
return this;
}
}
}
在調(diào)用時僅需調(diào)用create()方法既可
FruitShop fruitShop = DaggerFruitComponent.create().inject();
@Qualifier
在上面了解完@Inject之后,大家可能有個疑惑,使用@Inject注入的對象如果是接口或者抽象類怎么辦呢?在不同的地方可能需要不同的接口或者抽象類的實現(xiàn),怎么讓Dagger知道我究竟需要的哪種實現(xiàn)類呢?例如:
public class FruitShop {
@Inject
Fruit apple;
@Inject
Fruit orange;
}
??這里代碼需要對apple和orange進(jìn)行注入,但是對于Fruit的注入只能聲明一個,所以這個地方apple和orange要么都被注入成class Apple implements Fruit或者class Orange implements Fruit。
??@Qualifier這個時候就能作為一個限定符派上用場了。@Qualifier是加在注解之上的注解(也稱為元注解),當(dāng)需要注入的是接口或者抽象類,就可以使用@Qualifier來定義一個新的注解用來表明對應(yīng)需要的依賴關(guān)系。使用@Qualifier可以實現(xiàn)指定apple需要用Apple注入,orange需要使用Orange類注入。例如我們可以這樣實現(xiàn)
// 首先定義一個表示水果類型的注解
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface FruitType {
String value() default "";
}
// 接著使用這個注解表示對應(yīng)依賴關(guān)系
@Module
public abstract class FruitModule {
@Binds @FruitType("apple")
abstract Fruit bindApple(Apple apple);
@Binds @FruitType("orange")
abstract Fruit bindOrange(Orange orange);
}
// 使用時標(biāo)記相應(yīng)的注解既可
public class FruitShop {
@Inject @FruitType("apple")
Fruit apple;
@Inject @FruitType("orange")
Fruit orange;
}
除了可以聲明value為String的注解外,還可以傳入其他類型,例如我們需要一張顏色是紅色的桌子:
@java.lang.annotation.Documented
@java.lang.annotation.Retention(RUNTIME)
@javax.inject.Qualifier
public @interface DeskColor {
Color color() default Color.RED;
public enum Color { RED, WHITE }
}
在Module中可以指定具體生成的對象:
@Module
public class ProjectModule {
@Provides @DeskColor(color = DeskColor.Color.RED)
public Desk provideDesk() {
return new Desk("RED");
}
}
在使用時再進(jìn)行標(biāo)記既可:
public class FruitShop {
@Inject
public FruitShop() {
}
@Inject @DeskColor(color = DeskColor.Color.RED)
Desk desk;
}
通過@Qualifier定義注解可以實現(xiàn)對同一個接口或抽象類的指定不同對象注入。
@Named
了解完@Qualifier之后再看看@Name的聲明:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
/** The name. */
String value() default "";
}
可以看出除了接口名稱不一樣之外,其余的和上面定義的@FruitType是一致的,所以其實@Named只是系統(tǒng)定義好的,參數(shù)為String的默認(rèn)限定符。將上面代碼中的@FruitType改成@Named能達(dá)到一樣的效果。
@Scope和@Singleton
@Scope是另一個元注解,它的作用是告訴注入器要注意對象的重用的生命周期。其中@Scope的聲明如下:
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Scope {}
我們再看@Singleton的聲明:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
可以發(fā)現(xiàn)@Singleton就是被@Scope聲明的注解,可以作為一個生命周期的注解符。
例如我們需要注入一個Desk類,我們希望一個FruitShop對應(yīng)只有一個Desk,正常的情況下我們是這樣聲明的:
public class FruitShop {
@Inject
public FruitShop() {
}
@Injec
Desk desk;
@Inject
Desk desk2;
public String checkDesk() {
return desk == desk2 ? "desk equal" : "desk not equal";
}
}
@Module
public class ProjectModule {
@Provides
public Desk provideDesk() {
return new Desk();
}
}
@Component(modules = ProjectModule.class)
public interface FruitComponent {
FruitShop inject();
}
在Main函數(shù)中執(zhí)行
public class Main {
public static void main(String[] args) {
FruitShop fruitShop = DaggerFruitComponent.create().inject();
System.out.println(fruitShop.checkDesk());
}
}
得到的結(jié)果是
desk not equal
Process finished with exit code 0
當(dāng)然這不是我們希望得到的結(jié)果,下面我們來用@Singleton改造一下如下:
public class FruitShop {
@Inject
public FruitShop() {
}
@Injec
Desk desk;
@Inject
Desk desk2;
public String checkDesk() {
return desk == desk2 ? "desk equal" : "desk not equal";
}
}
@Module
public class ProjectModule {
@Provides
@Singleton
public Desk provideDesk() {
return new Desk();
}
}
@Singleton
@Component(modules = ProjectModule.class)
public interface FruitComponent {
FruitShop inject();
}
現(xiàn)在Rebuild之后再運行一下:
desk equal
Process finished with exit code 0
和之前不同的地方在于我們隊Component和module中的provide方法都加了@Singleton標(biāo)記。我們來看看對比下前后生成的代碼有什么區(qū)別:

可以看出,兩次生成的代碼中,只有DaggerFruitComponent有區(qū)別,其中的區(qū)別在于在Component中provideDeskProvider在有@Singleton標(biāo)注的例子中是單例的存在:
private void initialize(final Builder builder) {
// DoubleCheck.provider就是用了雙重檢驗的單例模式提供單例
this.provideDeskProvider =
DoubleCheck.provider(ProjectModule_ProvideDeskFactory.create(builder.projectModule));
}
private FruitShop injectFruitShop(FruitShop instance) {
FruitShop_MembersInjector.injectDesk(instance, provideDeskProvider.get());
FruitShop_MembersInjector.injectDesk2(instance, provideDeskProvider.get());
return instance;
}
其中DoubleCheck.get()方法使用雙重判斷獲取單例。
public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
@Override
public T get() {
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get();
instance = reentrantCheck(instance, result);
/* Null out the reference to the provider. We are never going to need it again, so we
* can make it eligible for GC. */
provider = null;
}
}
}
return (T) result;
}
}
而在沒有使用@Singletion的例子中,并沒有使用單例來提供Desk對象:
private void initialize(final Builder builder) {
this.projectModule = builder.projectModule;
}
private FruitShop injectFruitShop(FruitShop instance) {
FruitShop_MembersInjector.injectDesk(
instance, ProjectModule_ProvideDeskFactory.proxyProvideDesk(projectModule));
FruitShop_MembersInjector.injectDesk2(
instance, ProjectModule_ProvideDeskFactory.proxyProvideDesk(projectModule));
return instance;
}
通過上面的例子可以看出@Scope是用來定義需要的依賴對象在一個Component依賴關(guān)系圖生成中是否需要重用,且重用的范圍在一個Component對象的引用范圍內(nèi)。至于@Scope的意義在于可以在Components之間的依賴中使得依賴對象在不同的Components中重用。Components之間的依賴會在后面介紹。
這里需要注意幾點:
-
@Scope注解的注解不能用于標(biāo)注依賴的構(gòu)造函數(shù) - 沒有被
@Scope注解的注解(如@Singleton)注解的componet不能存在被@Scope注解的注解(如@Singleton)注解的方法(有點繞,可以理解成沒有標(biāo)志為@Singleton的componet不能擁有標(biāo)志為@Singleton的方法) - 如果componet定義了一個scope,那么這個componet里面只能存在沒有scoped的依賴關(guān)系,或者擁有跟componet一樣scope的依賴關(guān)系
- 使用Componet的調(diào)用方需要負(fù)責(zé)重用范圍的定義,例如希望有一個全局的單例,那么則需要保存一個擁有全局生命周期的component依賴生成類對象。
@Reusable
與@Singleton類似的,@Reusable也是被@Scope注釋的注釋。與@Singleton不同的是,@Reusable只表示Dagger生成的對象可以被緩存起來,從而節(jié)省內(nèi)存消耗,但是不能保證對象的單例性質(zhì)。我們將上面例子中的@Singleton改成@Reusable
@Module
public class ProjectModule {
@Provides
@Reusable
public Desk provideDesk() {
return new Desk();
}
}
rebuild之后我們來看生成的代碼
public final class DaggerFruitComponent implements FruitComponent {
private void initialize(final Builder builder) {
this.provideDeskProvider =
SingleCheck.provider(ProjectModule_ProvideDeskFactory.create(builder.projectModule));
}
private FruitShop injectFruitShop(FruitShop instance) {
FruitShop_MembersInjector.injectDesk(instance, provideDeskProvider.get());
FruitShop_MembersInjector.injectDesk2(instance, provideDeskProvider.get());
return instance;
}
}
其中SingleCheck.get()方法如下:
public final class SingleCheck<T> implements Provider<T> {
@Override
public T get() {
Object local = instance;
if (local == UNINITIALIZED) {
// provider is volatile and might become null after the check, so retrieve the provider first
Provider<T> providerReference = provider;
if (providerReference == null) {
// The provider was null, so the instance must already be set
local = instance;
} else {
local = providerReference.get();
instance = local;
// Null out the reference to the provider. We are never going to need it again, so we can
// make it eligible for GC.
provider = null;
}
}
return (T) local;
}
}
與剛剛的區(qū)別是由DoubleCheck.provider變成了SingleCheck.provider,從代碼實現(xiàn)可以看出@Reusable并不是嚴(yán)格的單例模式,只是對對象進(jìn)行了緩存。
@Component的dependencies和@SubComponent
雖然獨立的沒有scope范圍的component已經(jīng)非常實用了,但是在某些情況可能需要用到多個不同scope的不同componet。不同的Component之間可以通過指定依賴關(guān)系來聯(lián)系起來。Components之間的關(guān)聯(lián)可以采取兩種方式:指定指定dependencies或者SubComponet。下面我們來看看二者的區(qū)別。
-指定dependencies
當(dāng)一個Component需要從另一個Componet中獲得依賴的時候,可以使用@Component(dependencies = {XXXComponent.class})來引用其他component的依賴。需要注意的是,被引用的Component需要顯示暴露出給外部的依賴,不然編譯會報錯。看下面這個例子。
@Singleton
@Component(modules = {FruitModule.class})
public interface FruitComponent {
FruitShop inject();
// 對外暴露的依賴,表示其他Component可以從這個Component中
// 獲得@FruitType為apple的類型為Fruit的依賴
@FruitType("apple")
Fruit getApple();
}
在有了水果的依賴之后,我們創(chuàng)建一個果汁的依賴關(guān)系:
public interface Juice {
String name();
}
public class AppleJuice implements Juice {
private Fruit mApple;
// 這里要構(gòu)建一個蘋果汁,需要用到蘋果,這個依賴需要從FruitComponent中獲得
@Inject
public AppleJuice(@FruitType("apple") Fruit apple) {
mApple = apple;
}
@Override
public String name() {
return mApple.name();
}
}
@Module
abstract public class JuiceModule {
@OtherScop
@Binds @JuiceType("appleJuice")
abstract Juice bindAppleJuice(AppleJuice appleJuice);
}
// 這里通過指定dependencies,指出JuiceComponent需要FruitComponent作為依賴
@OtherScop
@Component(dependencies = {FruitComponent.class}, modules = {JuiceModule.class})
public interface JuiceComponent {
JuiceShop inject();
}
public class JuiceShop {
@Inject
public JuiceShop(){}
// 構(gòu)建果汁商店需要一個蘋果汁,Dagger將負(fù)責(zé)構(gòu)建
@Inject
@JuiceType("appleJuice")
public Juice appleJuice;
public String getJuice() {
return appleJuice.name();
}
}
上面的代碼,關(guān)注JuiceComponent類,這個類本身依賴關(guān)系圖從JuiceModule中獲得,而JuiceModule類只是聲明了JuiceType為appleJuice的Juice類通過創(chuàng)建AppleJuice獲得。而觀察AppleJuice類需要一個FruitType為apple的Fruit類作為依賴。這個Fruit類的依賴并不能從JuiceComponent中獲得,因此我們指定擁有這個Fruit類依賴的dependencies = {FruitComponent.class}。在FruitComponent類中需要顯示聲明其可以提供FruitType為apple的Fruit類如下:
@FruitType("apple")
Fruit getApple();
因此通過指定dependencies = {FruitComponent.class}構(gòu)成了完整的依賴關(guān)系鏈,我們可以如下構(gòu)建一個JuiceShop:
public static void main(String[] args) {
JuiceShop juiceShop = DaggerJuiceComponent
.builder()
.fruitComponent(DaggerFruitComponent.create())
.build()
.inject();
System.out.println(juiceShop.getJuice());
}
Dagger會給我們生成DaggerJuiceComponent,并通過fruitComponent()方法,放入DaggerFruitComponent的依賴。我們來看看Dagger生成的DaggerJuiceComponent具體是如何使用DaggerFruitComponent來生成依賴的:
public final class DaggerJuiceComponent implements JuiceComponent {
private com_shen_example_di_FruitComponent_getApple getAppleProvider;
private DaggerJuiceComponent(Builder builder) {
initialize(builder);
}
private void initialize(final Builder builder) {
// 保存fruitComponent到com_shen_example_di_FruitComponent_getApple內(nèi)部類中
this.getAppleProvider = new com_shen_example_di_FruitComponent_getApple(builder.fruitComponent);
// 將保存有fruitComponent的內(nèi)部類傳遞給AppleJuice構(gòu)造工廠
this.appleJuiceProvider = AppleJuice_Factory.create(getAppleProvider);
}
private static class com_shen_example_di_FruitComponent_getApple implements Provider<Fruit> {
private final FruitComponent fruitComponent;
com_shen_example_di_FruitComponent_getApple(FruitComponent fruitComponent) {
this.fruitComponent = fruitComponent;
}
// 通過fruitComponent創(chuàng)建apple
@Override
public Fruit get() {
return Preconditions.checkNotNull(
fruitComponent.getApple(), "Cannot return null from a non-@Nullable component method");
}
}
public static final class Builder {
private FruitComponent fruitComponent;
public JuiceComponent build() {
return new DaggerJuiceComponent(this);
}
public Builder fruitComponent(FruitComponent fruitComponent) {
this.fruitComponent = Preconditions.checkNotNull(fruitComponent);
return this;
}
}
}
public final class AppleJuice_Factory implements Factory<AppleJuice> {
private final Provider<Fruit> appleProvider;
public AppleJuice_Factory(Provider<Fruit> appleProvider) {
this.appleProvider = appleProvider;
}
@Override
public AppleJuice get() {
return provideInstance(appleProvider);
}
public static AppleJuice provideInstance(Provider<Fruit> appleProvider) {
// 最終通過調(diào)用保存有fruitComponent的get方法,
// 通過fruitComponent創(chuàng)建apple,并傳入給AppleJuice構(gòu)造函數(shù)中
return new AppleJuice(appleProvider.get());
}
public static AppleJuice_Factory create(Provider<Fruit> appleProvider) {
return new AppleJuice_Factory(appleProvider);
}
}
通過上述Dagger生成的代碼可以看出,通過dependencies方式指定Component依賴,Dagger會將依賴的Component通過組合方式傳入給目標(biāo)的Component,并在目標(biāo)Component需要創(chuàng)建依賴時,通過組合傳入的依賴Component進(jìn)行依賴類的構(gòu)建。再次強調(diào),如果沒有在依賴Component中聲明其對外暴露的依賴,會出現(xiàn)報錯。例如假設(shè)我們將上面的FruitComponent去掉getApple方法:
@Singleton
@Component(modules = {FruitModule.class})
public interface FruitComponent {
FruitShop inject();
}
那么在編譯時會出現(xiàn)報錯:
Error:(8, 8) java: [Dagger/MissingBinding] @com.shen.example.di.FruitType("apple") com.shen.example.fruit.Fruit cannot be provided without an @Provides-annotated method.
@com.shen.example.di.FruitType("apple") com.shen.example.fruit.Fruit is injected at
com.shen.example.juice.AppleJuice(apple)
com.shen.example.juice.AppleJuice is injected at
com.shen.example.di.JuiceModule.bindAppleJuice(appleJuice)
@com.shen.example.di.JuiceType("appleJuice") com.shen.example.juice.Juice is injected at
com.shen.example.JuiceShop.appleJuice
com.shen.example.JuiceShop is provided at
com.shen.example.di.JuiceComponent.inject()
-@SubComponent
@SubComponent聲明的接口或者抽象類,表示其本身的依賴關(guān)系圖是不完整的,必須通過依附于外部的Component才能獲得完整的依賴關(guān)系。使用@SubComponent有兩種方式,第一種是通過在被依賴的Component中聲明返回SubComponent類型的方法,并使用SubComponent中聲明的需要傳入?yún)?shù)的Module作為參數(shù)。第二種是在Component聲明的Module中,通過Module.subcomponents指定這個Module可以為哪些SubComponent提供依賴來源。
我們先看第一種方法,對比使用dependencies方式,只需要改變以下兩個類:
@Singleton
@Component(modules = {FruitModule.class})
public interface FruitComponent {
FruitShop inject();
// 通過在被依賴的Component中聲明返回SubComponent類型的方法,
// 并使用SubComponent中聲明的需要傳入?yún)?shù)的Module作為參數(shù)。
// 由于JuiceComponent沒有指定有參的Module,因此這里方法的參數(shù)可以為空
JuiceComponent juiceComponent();
}
// 將JuiceComponent標(biāo)注為Subcomponent,去掉dependencies指定
@OtherScop
@Subcomponent(modules = {JuiceModule.class})
public interface JuiceComponent {
JuiceShop inject();
}
我們可以如下構(gòu)建一個JuiceShop:
public static void main(String[] args) {
JuiceShop juiceShop = DaggerFruitComponent
.builder()
.build().juiceComponent()
.inject();
System.out.println(juiceShop.getJuice());
}
我們來看生成的DaggerFruitComponent
public final class DaggerFruitComponent implements FruitComponent {
// 通過FruitComponent中轉(zhuǎn)至JuiceComponent
@Override
public JuiceComponent juiceComponent() {
return new JuiceComponentImpl();
}
private final class JuiceComponentImpl implements JuiceComponent {
private AppleJuice_Factory appleJuiceProvider;
private Provider<Juice> bindAppleJuiceProvider;
private JuiceComponentImpl() {
initialize();
}
@SuppressWarnings("unchecked")
private void initialize() {
this.appleJuiceProvider = AppleJuice_Factory.create((Provider) Apple_Factory.create());
this.bindAppleJuiceProvider = DoubleCheck.provider((Provider) appleJuiceProvider);
}
// 最終會通過中轉(zhuǎn)得到的JuiceComponent,調(diào)用inject方法得到目標(biāo)對象
@Override
public JuiceShop inject() {
return injectJuiceShop(JuiceShop_Factory.newJuiceShop());
}
@CanIgnoreReturnValue
private JuiceShop injectJuiceShop(JuiceShop instance) {
JuiceShop_MembersInjector.injectAppleJuice(instance, bindAppleJuiceProvider.get());
return instance;
}
}
}
可以看出,當(dāng)使用@SubModule時,JuiceComponent被聲明為FruitComponent的內(nèi)部類,通過內(nèi)部中轉(zhuǎn)至JuiceComponent從而構(gòu)造出目標(biāo)對象。
第二種使用@Module.subcomponents,相比第一種SubComponent方法而言,不需要在在被依賴的Component中聲明返回SubComponent類型的方法,只需要在被依賴的Component對應(yīng)的Module中聲明subcomponent既可。同時對SubComponent要求有@Subcomponent.Builder。
我們看FruitComponent不在需要聲明返回JuiceComponent的方法
@Singleton
@Component(modules = {FruitModule.class})
public interface FruitComponent {
FruitShop inject();
// 不需要額外聲明SubComponent
//JuiceComponent juiceComponent();
}
@OtherScop
@Subcomponent(modules = {JuiceModule.class})
public interface JuiceComponent {
JuiceShop inject();
// 需要添加SubComponent.Builder
@Subcomponent.Builder
interface Builder {
JuiceComponent build();
}
}
同時對于JuiceComponent需要依賴的module添加subComponent依賴
// 對Module加入subcomponents = {JuiceComponent.class}
@Module(subcomponents = {JuiceComponent.class})
abstract public class FruitModule {
@Binds @FruitType("apple")
abstract Fruit bindApple(Apple apple);
@Binds @FruitType("orange")
abstract Fruit bindOrange(Orange orange);
}
這個時候可以在被依賴的Component生成產(chǎn)物的FruitShop中構(gòu)造出JuiceShop
public class FruitShop {
// 這里可以直接使用JuiceComponent.Builder,Provider的作用后面再說
@Inject
public Provider<JuiceComponent.Builder> juiceComponentProvider;
@Inject
public FruitShop() {}
public String juice() {
// 通過聲明需要注入一個JuiceComponent,從而獲得JuiceShop
JuiceShop juiceShop = juiceComponentProvider.get().build().inject();
return juiceShop.getJuice();
}
}
從生成的DaggerFruitComponent來看
public final class DaggerFruitComponent implements FruitComponent {
private Provider<JuiceComponent.Builder> juiceComponentBuilderProvider;
// 初始化juiceComponentBuilderProvider
private void initialize(final Builder builder) {
this.juiceComponentBuilderProvider =
new Provider<JuiceComponent.Builder>() {
@Override
public JuiceComponent.Builder get() {
return new JuiceComponentBuilder();
}
};
}
// 將juiceComponentBuilderProvider注入到FruitShop中
private FruitShop injectFruitShop(FruitShop instance) {
FruitShop_MembersInjector.injectJuiceComponentProvider(instance, juiceComponentBuilderProvider);
return instance;
}
private final class JuiceComponentBuilder implements JuiceComponent.Builder {
// 通過JuiceComponent.Builder生成JuiceComponentImpl,這就是為什么通過@Module.subcomponents一定要聲明Builder的原因。
@Override
public JuiceComponent build() {
return new JuiceComponentImpl(this);
}
}
// JuiceComponentImpl與第一種的SubComponent方法內(nèi)容類似,省略
private final class JuiceComponentImpl implements JuiceComponent {
// ……
}
}
-指定dependencies與SubComponent區(qū)別
- dependencies可以同時指定多個,而采用SubComponent只能有一個parent Component
- dependencies指定的Component與本身的Component是屬于組合關(guān)系,他們各自獨立,可以單獨使用。而SubComponent必須依賴于某個Component,Dagger不會對SubComponent生成DaggerXXXSubComponent類,而是在DaggerXXXComponent中定義了SubComponentImpl的內(nèi)部類。
- 調(diào)用生成對象的時候依賴方向不同。使用dependencies方式,需要外部依賴的Componet和被依賴的Componet之間相互獨立,會生成兩個DaggerXXXComponet,并且是通過需要依賴的Componet發(fā)起,通過引入外部的Component來構(gòu)建出最終的對象;而通過
@SubComponent方式則是只生成一個DaggerXXXComponent,由被依賴的Component發(fā)起,通過中轉(zhuǎn)至需要其依賴的內(nèi)部Component或者從依賴的Component生成對象內(nèi)部來構(gòu)建出最終對象。見如下代碼:
// dependencies方式
JuiceShop juiceShop = DaggerJuiceComponent
.builder()
.fruitComponent(DaggerFruitComponent.create())
.build()
.inject();
// @SubComponent第一種方式
JuiceShop juiceShop = DaggerFruitComponent
.builder()
.build().juiceComponent()
.inject();
// @SubComponent第二種方式
public class FruitShop {
@Inject
public Provider<JuiceComponent.Builder> juiceComponentProvider;
@Inject
public FruitShop() {}
public void createJuiceShop() {
JuiceShop juiceShop = juiceComponentProvider.get().build().inject();
}
}
??使用SubComponent的有兩個好處,第一個是可以對不同的component聲明不同的生命周期,規(guī)范對象存活的周期。第二個是為了更好的封裝,將相同的依賴放置到同一個component并依賴于它,而將不同的依賴封裝到不同的模塊。
??對于Component的依賴關(guān)系介紹到這里。在平常的使用中,如果module之間依賴較多的話,不建議采用@SubComponent第一種方式,因為這種方式每增加一個submodule都要在被依賴的component中聲明。如果被依賴的component比較穩(wěn)定,建議使用dependencies方式,這樣新增加一個依賴的component不用修改被依賴的component。而@SubComponent第二種方式僅適用于依賴的component是作為被依賴component的一個附屬情況下使用,因為subcomponent無法脫離被依賴component的構(gòu)建產(chǎn)物使用。不過第二種@SubComponent方式相對第一種方式而言,會讓Dagger知道SubComponent是否被使用,從而減少生成沒有被使用的SubComponent的代碼。
Lazy<> & Provider<>
依賴注入有三種模式,一種是最常見的直接注入(Direct Injection),還有就是懶注入(Lazy Injection)和提供者注入(Provider Injection)。直接注入模式下,被注入的對象會先生成,然后當(dāng)有需要被注入的地方時,將預(yù)先生成的對象賦值到需要的地方。Lazy注入只有當(dāng)get的時候才會創(chuàng)建對象,且生成之后對象會被緩存下來。Provider注入在每次get都會創(chuàng)建新的對象。
用官方的一個例子來說明。
@Module
public class CounterModule {
private int next = 100;
@Provides
Integer provideInteger() {
System.out.println("computing...");
return next++;
}
}
CounterModule可以提供一個整形變量,每次提供完之后會對這個變量加一。
/**
* 直接注入
*/
public class DirectCounter {
@Inject
Integer value;
void print() {
System.out.println("direct counter printing...");
System.out.println(value);
System.out.println(value);
System.out.println(value);
}
}
/**
* Provider注入
*/
public class ProviderCounter {
@Inject
Provider<Integer> provider;
void print() {
System.out.println("provider counter printing...");
System.out.println(provider.get());
System.out.println(provider.get());
System.out.println(provider.get());
}
}
/**
* Lazy注入
*/
public class LazyCounter {
@Inject
Lazy<Integer> lazy;
void print() {
System.out.println("lazy counter printing...");
System.out.println(lazy.get());
System.out.println(lazy.get());
System.out.println(lazy.get());
}
}
/**
* 多個Lazy注入,lazy與單例
*/
public class LazyCounters {
@Inject
LazyCounter counter1;
@Inject
LazyCounter counter2;
void print() {
System.out.println("lazy counters printing...");
counter1.print();
counter2.print();
}
}
我們將這幾種的Counter集合到一起并輸入
public class Counter {
@Inject
DirectCounter mDirectCounter;
@Inject
ProviderCounter mProviderCounter;
@Inject
LazyCounter mLazyCounter;
@Inject
LazyCounters mLazyCounters;
public void print() {
mDirectCounter.print();
mProviderCounter.print();
mLazyCounter.print();
mLazyCounters.print();
}
}
得到以下的輸入結(jié)果:
// 直接注入
computing...
direct counter printing...
100
100
100
// Provider注入
provider counter printing...
computing...
101
computing...
102
computing...
103
// Lazy注入
lazy counter printing...
computing...
104
104
104
// 多個Lazy注入
lazy counters printing...
lazy counter printing...
computing...
105
105
105
lazy counter printing...
computing...
106
106
106
從結(jié)果可以看出,直接注入會先計算一次得到需要被注入的依賴對象(這里是整型100),并在需要的地方都返回這個預(yù)先計算好的對象,因此都返回100。
Provider注入則會在每次get方法調(diào)用的地方都通過Module中的provider方法計算得到需要被注入的依賴對象,因此依次返回新計算的對象101、102、103。
Lazy注入與直接注入相似,只會計算一次需要被注入的依賴對象,但是與直接注入不同的是,Lazy注入只有在被調(diào)用get方法的時候才會進(jìn)行計算,因此可以看到lazy counter printing...先打印,然后才是computing...。
需要注意的是Lazy注入并不等同于單例模式,不同的LazyCounter的get方法會獲取到不同的對象。例如LazyCounters中通過兩個LazyCounter的get方法分別獲取到的是105和106,并且lazy counter printing...和computing...都打印了兩次。
@BindsInstance
當(dāng)構(gòu)建Component的時候,如果需要外部傳入?yún)?shù),我們有兩種方法,一種是通過構(gòu)建Module時通過Module的構(gòu)造函數(shù)傳入?yún)?shù),第二種是通過@BindsInstance方式,在構(gòu)建Component的時候通過Component.Builder來構(gòu)建Component。我們先看第一種方法:
// Pear對象需要一個String類型的名稱
public class Pear implements Fruit {
String customName;
public Pear(String name) {
customName = name;
}
@Override
public String name() {
if (customName != null && customName.length() > 0) {
return customName;
} else {
return "pear";
}
}
}
// ProjectModule的構(gòu)造函數(shù)接收一個String類型的參數(shù),并最終用于構(gòu)造Pear對象
@Module
public class ProjectModule {
String name;
public ProjectModule(String name) {
this.name = name;
}
@Provides @Name
public String provideName() {
return name;
}
@Provides @FruitType("pear")
public Fruit providerPear(@Nullable @Name String name) {
return new Pear(name);
}
}
// Component不需要特別的處理
@Singleton
@Component(modules = {ProjectModule.class})
public interface FruitComponent {
FruitShop inject();
}
public class FruitShop {
@Inject @FruitType("pear")
Fruit pear;
// 打印出Pear的名字
public String createFruit() {
return pear.get().name();
}
}
public class Main {
public static void main(String[] args) {
// 通過projectModule構(gòu)造傳遞參數(shù)
FruitShop fruitShop = DaggerFruitComponent
.builder()
.projectModule(new ProjectModule("cus_Pear"))
.build()
.inject();
System.out.println(fruitShop.createFruit());
}
}
上面代碼中,通過ProjectModule的構(gòu)建函數(shù)傳入了一個String對象參數(shù),并最后用于構(gòu)造Pear對象,最終會打印cus_Pear。對于這種在依賴關(guān)系圖中需要外部傳入?yún)?shù)的情況,可以使用@BindInstance來進(jìn)行優(yōu)化。優(yōu)化之后的代碼如下:
// ProjectModule中不需要另外聲明構(gòu)造函數(shù)
@Module
public class ProjectModule {
@Provides @FruitType("pear")
public Fruit providerPear(@Name String name) {
return new Pear(name);
}
}
@Component(modules = {ProjectModule.class})
public interface FruitComponent {
FruitShop inject();
// 通過Component.Builder并使用BindsInstance提供依賴需要參數(shù)
@Component.Builder
interface Builder {
@BindsInstance
Builder cusPearName(@Name String name);
FruitComponent build();
}
}
public class Main {
public static void main(String[] args) {
// 使用builder中的cusPearName方法傳入?yún)?shù)
FruitShop fruitShop = DaggerFruitComponent
.builder()
.cusPearName("cus_Pear")
.build()
.inject();
System.out.println(fruitShop.createFruit());
}
}
與第一種方法不同,這種方法并不需要使用Module的帶參數(shù)構(gòu)造方法來傳遞依賴所需的參數(shù),而是通過Component構(gòu)造時候在build的過程中通過cusPearName方法傳入依賴對象,邏輯更加清晰,并且減少了Module的復(fù)雜度。
使用@BindInstance注解的方法,如果參數(shù)沒有標(biāo)記為@Nullable則這個方法必須要調(diào)用,否則會報java.lang.IllegalStateException: java.lang.String must be set。傳入?yún)?shù)必須為非null,否則會報java.lang.NullPointerException at dagger.internal.Preconditions.checkNotNull(Preconditions.java:33)。如果這個參數(shù)是可選,則必須聲明為nullable,如下:
@Module
public class ProjectModule {
@Provides @FruitType("pear")
public Fruit providerPear(@Nullable @Name String name) {
return new Pear(name);
}
}
@Component(modules = {ProjectModule.class})
public interface FruitComponent {
FruitShop inject();
@Component.Builder
interface Builder {
@BindsInstance
Builder cusPearName(@Nullable @Name String name);
FruitComponent build();
}
}
在實際項目中,應(yīng)該盡量使用@BindInstance,而不是帶參數(shù)構(gòu)造函數(shù)的module。
@BindsOptionalOf
@MapKey
@Multibinds
@IntoMap @IntoSet @ElementsIntoSet
@StringKey @IntKey @LongKey @ClassKey
Dagger2的缺點
- 修改完相關(guān)依賴之后必須Rebuild才能生效
- 代碼檢索變得相對困難,對于接口或者抽象類沒辦法直觀看到具體生成的是哪個對象
Kodein - 編寫Dagger代碼時需要關(guān)注比較多的規(guī)則約束,且不太容易記憶(例如Component中的方法要求,以及Builder里的方法要求等)
最后
Dagger2是非常棒的依賴注入器,但是Dagger2使用存在上述的一些缺點,所以建議僅在如架構(gòu)關(guān)系之類的關(guān)鍵且依賴關(guān)系相對不經(jīng)常修改的地方使用,不建議在項目中大范圍使用。
例子代碼下載:
本文的代碼可以在github中下載:https://github.com/shenguojun/DaggerExample