Daggger2 概念解讀栗恩、使用姿勢及源碼分析(2)

上一篇文章Daggger2 使用姿勢及源碼分析(1)
講述了Dagger2的使用姿勢绢陌,以及連接器component挨下、提供者 Provider、工廠生產(chǎn)者Factory脐湾、成員注入器 MembersInjector臭笆,這些組件的相互作用揭示了Dagger2背后工作的原理。這篇文章我將繼續(xù)解讀Dagger2秤掌,為大家展示以下幾個(gè)關(guān)鍵詞背后的機(jī)制:
Inject愁铺, Singleton, Scope闻鉴, Qualifier

[TOC]

Inject

Inject茵乱,即注入,該注解標(biāo)示地方表示需要通過DI框架來注入實(shí)例孟岛。Inject有三種方式瓶竭,分別是Constructor injection、Fields injection渠羞、Methods injection斤贰。

Constructor injection
如果在構(gòu)造器中聲明了@Inject,Dagger2會(huì)嘗試在創(chuàng)建實(shí)例時(shí)注入構(gòu)造所需的參數(shù)次询,這就必須為這些參數(shù)提供一種途徑使得dagger可以創(chuàng)建這些實(shí)例荧恍。這就需要再M(fèi)odule中顯式的@Provides參數(shù)。關(guān)系簡單圖示如下:
Constructor Inject --> Create parameters --> Component --> Module

以TasksRepository為例:

@Inject
TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
            @Local TasksDataSource tasksLocalDataSource) {
    mTasksRemoteDataSource = tasksRemoteDataSource;
    mTasksLocalDataSource = tasksLocalDataSource;
}

在TasksRepositoryModule提供兩個(gè)實(shí)例的創(chuàng)建方式

@Singleton
@Provides
@Local
TasksDataSource provideTasksLocalDataSource(Context context) {
    return new TasksLocalDataSource(context);
}

@Singleton
@Provides
@Remote
TasksDataSource provideTasksRemoteDataSource() {
    return new FakeTasksRemoteDataSource();
}

Dagger會(huì)為TasksRepository創(chuàng)建一個(gè)工廠類:TasksRepository_Factory

public static Factory<TasksRepository> create(
    Provider<TasksDataSource> tasksRemoteDataSourceProvider,
    Provider<TasksDataSource> tasksLocalDataSourceProvider) {
    return new TasksRepository_Factory(tasksRemoteDataSourceProvider, tasksLocalDataSourceProvider);
}

以上屯吊,可以看出送巡,Constructor injection影響的是類實(shí)例的創(chuàng)建工廠摹菠。

這里涉及到兩個(gè)相同的實(shí)例TasksDataSource,一個(gè)是remote授艰,一個(gè)是local辨嗽,它們通過scope來區(qū)分,后面會(huì)講述scope的原理淮腾。

Fields injection
這是最常用的注入方式糟需,這種注入方式要了解的是Dagger是怎樣注入的,是什么時(shí)候注入的谷朝。

第一個(gè)問題洲押,如何注入?

1.擁有Fields injection行為的類A圆凰,Dagger會(huì)為它生成一個(gè)注入器A_MembersInjector;
注入器的結(jié)構(gòu)如下:

public interface MembersInjector<T> {
  void injectMembers(T instance);}

2.注入器A_MembersInjector需要實(shí)現(xiàn)方法:injectMembers(T instance)杈帐,該方法通過Provider注入具體的實(shí)例

TaskDetailActivity通過Fields injection注入TaskDetailPresenter

public class TaskDetailActivity extends AppCompatActivity {

    @Inject TaskDetailPresenter mTaskDetailPresenter;

}

注入器TaskDetailActivity_MembersInjector的定義如下:

public final class TaskDetailActivity_MembersInjector
    implements MembersInjector<TaskDetailActivity> {

      public static MembersInjector<TaskDetailActivity> create(
      Provider<TaskDetailPresenter> mTaskDetailPresenterProvider) {
    return new TaskDetailActivity_MembersInjector(mTaskDetailPresenterProvider);
  }

  @Override
  public void injectMembers(TaskDetailActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.mTaskDetailPresenter = mTaskDetailPresenterProvider.get();
  }
}

第二個(gè)問題:什么時(shí)候注入?

注入時(shí)機(jī)有兩種:

  1. 當(dāng)對應(yīng)的A_Factory中的get() 方法被調(diào)用的時(shí)候专钉;
  2. 主動(dòng)調(diào)用Component中定義的inject()方法時(shí)

第一種方式挑童,查看TaskDetailPresenter_Factory可知,簡單的理解就是跃须,當(dāng)Dagger主動(dòng)通過工廠類創(chuàng)建實(shí)例的時(shí)候站叼,需要調(diào)用注入器注入該類依賴的屬性和方法。源碼如下:

public final class TaskDetailPresenter_Factory implements Factory<TaskDetailPresenter> {
  @Override
  public TaskDetailPresenter get() {
    return MembersInjectors.injectMembers(
        taskDetailPresenterMembersInjector,
        new TaskDetailPresenter(
            taskIdProvider.get(), tasksRepositoryProvider.get(), taskDetailViewProvider.get()));
  }
}

第二種方式菇民,這種方式的典型應(yīng)用是尽楔,在Activity中創(chuàng)建Component實(shí)例,然后通過調(diào)用Component中inject()方式注入屬性和方法第练。源碼如下:

調(diào)用方式:
DaggerTaskDetailComponent.builder()
                .taskDetailPresenterModule(new TaskDetailPresenterModule(taskDetailFragment, taskId))
                .tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent()).build()
                .inject(this);


DaggerTaskDetailComponent中的源碼:
@Override
public void inject(TaskDetailActivity taskDetailActivity) {
    taskDetailActivityMembersInjector.injectMembers(taskDetailActivity);
}

Methods injection

同F(xiàn)ields injection類似阔馋,Methods injection也是通過注入器在實(shí)例創(chuàng)建好的時(shí)候調(diào)用injectMembers() 方法注入的,需要注意的是娇掏,方法注入可能涉及到參數(shù)的注入呕寝,這些參數(shù)要求可以在Dagger的Provider中查找到。用Dagger2的原文文檔就是 “All method parameters are provided from dependencies graph.”

Singleton

單例的實(shí)現(xiàn)依賴于特殊的Provider機(jī)制婴梧,在Module中以@Provides注解聲明的方法都會(huì)創(chuàng)建一個(gè)工廠類以提供實(shí)例壁涎,比如在TasksRepositoryModule
中的TasksDataSource provideTasksLocalDataSource(Context context),生成了TasksRepositoryModule_ProvideTasksLocalDataSourceFactory志秃;在TaskDetailPresenterModule
中TaskDetailContract.View provideTaskDetailContractView()
生成了TaskDetailPresenterModule_ProvideTaskDetailContractViewFactory;
它們的本質(zhì)是一樣的嚼酝,都是一樣的工廠類提供者浮还。

不同的地方在于,在Module中以@Singleton聲明的方法闽巩,在提供實(shí)例的時(shí)候钧舌,并不是直接調(diào)用工廠類的get() 方法担汤,而是通過ScopedProvider的get()方法類創(chuàng)建實(shí)例,而這個(gè)ScopedProvider就保證了實(shí)例的單例:

ScopedProvider源碼如下, 其get方法就是一個(gè)典型的單例模式實(shí)現(xiàn):

public final class ScopedProvider<T> implements Provider<T>, Lazy<T> {
 @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
  @Override
  public T get() {
    // double-check idiom from EJ2: Item 71
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          instance = result = factory.get();
        }
      }
    }
    return (T) result;
  }

  /** Returns a new scoped provider for the given factory. */
  public static <T> Provider<T> create(Factory<T> factory) {
    if (factory == null) {
      throw new NullPointerException();
    }
    return new ScopedProvider<T>(factory);
  }
}

Scope

Scope是一種作用域的描述洼冻。

@Singleton就是一種Scope崭歧,并且是最長的scope。

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

@Singleton能起到作用得益于Dagger2底層的實(shí)現(xiàn)撞牢,也就是上文中提到的ScopedProvider的應(yīng)用率碾。

這里有一個(gè)誤區(qū),很多文章喜歡用Scope來控制對象的作用域屋彪,其實(shí)是有問題的所宰。拿@Singleton來講,并不是用@Singleton標(biāo)注一個(gè)類之后畜挥,你在任何地方注入該類的時(shí)候都是單例的仔粥。Scope真正要表達(dá)的是類、Component蟹但、Module一體的關(guān)系躯泰。

這里,我簡單做了一個(gè)實(shí)驗(yàn)华糖,代碼如下:

@Singleton
public class User {
}

public class Person {
}


@Module
public class UserModule {

    @Provides
    @Singleton
    User provideUser() {
        return new User();
    }

    @Provides
    Person providePerson() {
        return new Person();
    }
}

@Singleton
@Component(modules = UserModule.class)
public interface UserComponent {
    User getUser();
    Person getPerson();

    void inject(MainActivity activity);
}

@Singleton
@Component(modules = UserModule.class)
public interface PresenterComponent {
    User getUser();
}

public class MainActivity extends AppCompatActivity {

    @Inject
    User user1;
    @Inject
    Person person1;

    @Inject
    Presenter presenter1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        UserComponent component = DaggerUserComponent.create();
        User user2 = component.getUser();
        Person person2 = component.getPerson();
        component.inject(this);

        PresenterComponent presenterComponent = DaggerPresenterComponent.create();
        User user3 = presenterComponent.getUser();
    }

}

實(shí)驗(yàn)的表現(xiàn):
user1 和 user2 是同一個(gè)實(shí)例麦向,user3 不同于user1是另外一個(gè)實(shí)例;
person1 和person2 不是同一個(gè)實(shí)例缅阳。

現(xiàn)象解釋:
User聲明為@Singleton, user1和user2來源于同一個(gè)component(UserComponent)磕蛇,所以是同一個(gè)實(shí)例;
user3來源于PresenterComponent十办,盡管User被聲明為@Singleton秀撇,但它卻是一個(gè)新的實(shí)例;
Person是個(gè)普通的實(shí)例向族,沒有聲明為@Singleton呵燕,盡管person1和person2 來源于同一個(gè)component,但是卻創(chuàng)建了兩次件相,所以不是同一個(gè)實(shí)例再扭。

所以結(jié)論就是scope沒有想象中的那么牛逼,它并不能控制實(shí)例的生命周期夜矗,并不是@Singleton就是全局單例泛范,@FragmentScope @ActivityScope @ApplicationScope就是其字面表達(dá)的意思。它的本質(zhì)都是依賴于component的生命周期的紊撕。

Qualifier

@Qualifier annotation helps us to create “tags” for dependencies which have the same interface罢荡。這句話說的很形象,@Qualifier就是一個(gè)tag。想象一下区赵,如果在Module中你需要provide兩個(gè)TasksDataSource惭缰,你就需要通過@Qualifier來區(qū)分了。示例如下:

定義Qualifier:

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Remote {

}

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Local {

}

在Module中標(biāo)識(shí):

@Module
public class TasksRepositoryModule {

    @Singleton
    @Provides
    @Local
    TasksDataSource provideTasksLocalDataSource(Context context) {
        return new TasksLocalDataSource(context);
    }

    @Singleton
    @Provides
    @Remote
    TasksDataSource provideTasksRemoteDataSource() {
        return new FakeTasksRemoteDataSource();
    }

}

使用的時(shí)候通過Qualifier來區(qū)分:

    @Inject
    TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
            @Local TasksDataSource tasksLocalDataSource) {
        mTasksRemoteDataSource = tasksRemoteDataSource;
        mTasksLocalDataSource = tasksLocalDataSource;
    }

實(shí)際上笼才,在Dagger2自動(dòng)生成的代碼中漱受,它會(huì)對Qualifier標(biāo)識(shí)的方法生成不同的工廠類,如上就分別對應(yīng)TasksRepositoryModule_ProvideTasksRemoteDataSourceFactory

TasksRepositoryModule_ProvideTasksLocalDataSourceFactory骡送,最終在引用的時(shí)候昂羡,就分別通過這兩個(gè)工廠類提供實(shí)例。

Subcomponent

如果一個(gè)Component的功能不能滿足你的需求各谚,你需要對它進(jìn)行拓展紧憾,一種辦法是使用Component(dependencies=××.classs)。另外一種是使用@Subcomponent昌渤,Subcomponent用于拓展原有component赴穗。同時(shí),這將產(chǎn)生一種副作用——子component同時(shí)具備兩種不同生命周期的scope膀息。子Component具備了父Component擁有的Scope般眉,也具備了自己的Scope。

Subcomponent其功能效果優(yōu)點(diǎn)類似component的dependencies潜支。但是使用@Subcomponent不需要在父component中顯式添加子component需要用到的對象甸赃,只需要添加返回子Component的方法即可,子Component能自動(dòng)在父Component中查找缺失的依賴冗酿。

下面用實(shí)際例子來理解:

方式1: Component(dependencies=××.classs)

@Module
public class BBaseModule {

    @Provides
    Person providePerson() {
        return new Person();
    }

    @Provides
    User provideUser() {
        return new User();
    }
}

@Component(modules = BBaseModule.class)
public interface BBaseComponent {

    Person getPerson();
}

@Component(dependencies = BBaseComponent.class, modules = BModule.class)
public interface BComponent {

    void inject(BActivity activity);
}

在BActivity中你會(huì)發(fā)現(xiàn)只能@Inject Person person. 你不能User埠对,因?yàn)锽BaseComponent并沒有顯式的提供User對象。

方式2:采用Subcomponent

@Component(modules = BBaseModule.class)
public interface BBaseComponent {

    Person getPerson();

    BComponent bcomponent(BModule bModule);
}

@Subcomponent(modules = BModule.class)
public interface BComponent {

    void inject(BActivity activity);
}

使用Subcomponent裁替,你需要將子Component用@Subcomponent標(biāo)注项玛,并在父Component中顯式的提供子Component的創(chuàng)建方式。做完這兩個(gè)修改之后弱判,就可以在BActivity中注入Person和User了襟沮。
那么,采用Subcomponent之后昌腰,@Subcomponent標(biāo)注之后的Component不再生成DaggerXXXComponent的實(shí)現(xiàn)類开伏,要獲取BComponent實(shí)例,你需要通過DaggerBBaseComponent.builder().build().bcomponent(new BModule()); 即需要通過父Component實(shí)例來獲取遭商。究竟發(fā)生什么了呢固灵?觀察源碼可知:

public final class DaggerBBaseComponent implements BBaseComponent {
  private Provider<Person> providePersonProvider;

  private Provider<User> provideUserProvider;

  @Override
  public BComponent bcomponent(BModule bModule) {
    return new BComponentImpl(bModule);
  }

  private final class BComponentImpl implements BComponent {
    private final BModule bModule;

    private MembersInjector<BActivity> bActivityMembersInjector;

    private BComponentImpl(BModule bModule) {
      this.bModule = Preconditions.checkNotNull(bModule);
      initialize();
    }

    @SuppressWarnings("unchecked")
    private void initialize() {

      this.bActivityMembersInjector =
          BActivity_MembersInjector.create(
              DaggerBBaseComponent.this.providePersonProvider,
              DaggerBBaseComponent.this.provideUserProvider);
    }

    @Override
    public void inject(BActivity activity) {
      bActivityMembersInjector.injectMembers(activity);
    }
  }
}

BComponent的實(shí)現(xiàn)類在BBaseComponent中,并且BComponent用到的兩個(gè)實(shí)例Person和User劫流,是通過BBaseComponent中的providePersonProvider 和 provideUserProvider獲取的巫玻。這就解釋了上文中提到的“子Component具備了父Component擁有的Scope暑认,也具備了自己的Scope”。

結(jié)論

知其然更要知其所以然大审,了解了背后的機(jī)制,你會(huì)發(fā)現(xiàn)dagger2也沒那么復(fù)雜和神秘座哩。

上一篇:Daggger2 概念解讀徒扶、使用姿勢及源碼分析(1)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市根穷,隨后出現(xiàn)的幾起案子姜骡,更是在濱河造成了極大的恐慌,老刑警劉巖屿良,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件圈澈,死亡現(xiàn)場離奇詭異,居然都是意外死亡尘惧,警方通過查閱死者的電腦和手機(jī)康栈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喷橙,“玉大人啥么,你說我怎么就攤上這事》∮猓” “怎么了悬荣?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疙剑。 經(jīng)常有香客問我氯迂,道長,這世上最難降的妖魔是什么言缤? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任嚼蚀,我火速辦了婚禮,結(jié)果婚禮上轧简,老公的妹妹穿的比我還像新娘驰坊。我一直安慰自己,他們只是感情好哮独,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布拳芙。 她就那樣靜靜地躺著,像睡著了一般皮璧。 火紅的嫁衣襯著肌膚如雪舟扎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天悴务,我揣著相機(jī)與錄音睹限,去河邊找鬼譬猫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛羡疗,可吹牛的內(nèi)容都是我干的染服。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼叨恨,長吁一口氣:“原來是場噩夢啊……” “哼柳刮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起痒钝,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤秉颗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后送矩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚕甥,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年栋荸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菇怀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒸其,死狀恐怖敏释,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情摸袁,我是刑警寧澤钥顽,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站靠汁,受9級(jí)特大地震影響蜂大,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝶怔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一奶浦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧踢星,春花似錦澳叉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至藏否,卻和暖如春瓶殃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背副签。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國打工遥椿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留基矮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓冠场,卻偏偏與公主長得像家浇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子碴裙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容