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

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

[TOC]

Inject

Inject,即注入,該注解標示地方表示需要通過DI框架來注入實例。Inject有三種方式,分別是Constructor injection、Fields injection、Methods injection。

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

以TasksRepository為例:

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

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

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

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

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

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

以上,可以看出,Constructor injection影響的是類實例的創(chuàng)建工廠。

這里涉及到兩個相同的實例TasksDataSource,一個是remote,一個是local,它們通過scope來區(qū)分,后面會講述scope的原理。

Fields injection
這是最常用的注入方式,這種注入方式要了解的是Dagger是怎樣注入的,是什么時候注入的。

第一個問題,如何注入?

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

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

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

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();
  }
}

第二個問題:什么時候注入?

注入時機有兩種:

  1. 當對應的A_Factory中的get() 方法被調(diào)用的時候;
  2. 主動調(diào)用Component中定義的inject()方法時

第一種方式,查看TaskDetailPresenter_Factory可知,簡單的理解就是,當Dagger主動通過工廠類創(chuàng)建實例的時候,需要調(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()));
  }
}

第二種方式,這種方式的典型應用是,在Activity中創(chuàng)建Component實例,然后通過調(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也是通過注入器在實例創(chuàng)建好的時候調(diào)用injectMembers() 方法注入的,需要注意的是,方法注入可能涉及到參數(shù)的注入,這些參數(shù)要求可以在Dagger的Provider中查找到。用Dagger2的原文文檔就是 “All method parameters are provided from dependencies graph.”

Singleton

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

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

ScopedProvider源碼如下, 其get方法就是一個典型的單例模式實現(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底層的實現(xiàn),也就是上文中提到的ScopedProvider的應用。

這里有一個誤區(qū),很多文章喜歡用Scope來控制對象的作用域,其實是有問題的。拿@Singleton來講,并不是用@Singleton標注一個類之后,你在任何地方注入該類的時候都是單例的。Scope真正要表達的是類、Component、Module一體的關系。

這里,我簡單做了一個實驗,代碼如下:

@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();
    }

}

實驗的表現(xiàn):
user1 和 user2 是同一個實例,user3 不同于user1是另外一個實例;
person1 和person2 不是同一個實例。

現(xiàn)象解釋:
User聲明為@Singleton, user1和user2來源于同一個component(UserComponent),所以是同一個實例;
user3來源于PresenterComponent,盡管User被聲明為@Singleton,但它卻是一個新的實例;
Person是個普通的實例,沒有聲明為@Singleton,盡管person1和person2 來源于同一個component,但是卻創(chuàng)建了兩次,所以不是同一個實例。

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

Qualifier

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

定義Qualifier:

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

}

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

}

在Module中標識:

@Module
public class TasksRepositoryModule {

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

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

}

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

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

實際上,在Dagger2自動生成的代碼中,它會對Qualifier標識的方法生成不同的工廠類,如上就分別對應TasksRepositoryModule_ProvideTasksRemoteDataSourceFactory

TasksRepositoryModule_ProvideTasksLocalDataSourceFactory,最終在引用的時候,就分別通過這兩個工廠類提供實例。

Subcomponent

如果一個Component的功能不能滿足你的需求,你需要對它進行拓展,一種辦法是使用Component(dependencies=××.classs)。另外一種是使用@Subcomponent,Subcomponent用于拓展原有component。同時,這將產(chǎn)生一種副作用——子component同時具備兩種不同生命周期的scope。子Component具備了父Component擁有的Scope,也具備了自己的Scope。

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

下面用實際例子來理解:

方式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中你會發(fā)現(xiàn)只能@Inject Person person. 你不能User,因為BBaseComponent并沒有顯式的提供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標注,并在父Component中顯式的提供子Component的創(chuàng)建方式。做完這兩個修改之后,就可以在BActivity中注入Person和User了。
那么,采用Subcomponent之后,@Subcomponent標注之后的Component不再生成DaggerXXXComponent的實現(xiàn)類,要獲取BComponent實例,你需要通過DaggerBBaseComponent.builder().build().bcomponent(new BModule()); 即需要通過父Component實例來獲取。究竟發(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的實現(xiàn)類在BBaseComponent中,并且BComponent用到的兩個實例Person和User,是通過BBaseComponent中的providePersonProvider 和 provideUserProvider獲取的。這就解釋了上文中提到的“子Component具備了父Component擁有的Scope,也具備了自己的Scope”。

結(jié)論

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

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

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容