上一篇文章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ī)有兩種:
- 當(dāng)對應(yīng)的A_Factory中的get() 方法被調(diào)用的時(shí)候专钉;
- 主動(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ù)雜和神秘座哩。