上一篇文章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();
}
}
第二個問題:什么時候注入?
注入時機有兩種:
- 當對應的A_Factory中的get() 方法被調(diào)用的時候;
- 主動調(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也沒那么復雜和神秘。