上一篇文章Daggger2 使用姿势及源码分析(1)
讲述了Dagger2的使用姿势,以及连接器component、提供者 Provider、工厂生产者Factory、成员注入器 MembersInjector,这些组件的相互作用揭示了Dagger2背后工作的原理。这篇文章我将继续解读Dagger2,为大家展示以下几个关键词背后的机制:
Inject, Singleton, Scope, Qualifier
[TOC]
Inject
Inject,即注入,该注解标示地方表示需要通过DI框架来注入实例。Inject有三种方式,分别是Constructor injection、Fields injection、Methods injection。
Constructor injection
如果在构造器中声明了@Inject,Dagger2会尝试在创建实例时注入构造所需的参数,这就必须为这些参数提供一种途径使得dagger可以创建这些实例。这就需要再Module中显式的@Provides参数。关系简单图示如下:
Constructor Inject --> Create parameters --> Component --> Module
以TasksRepository为例:
@Inject
TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
@Local TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = tasksRemoteDataSource;
mTasksLocalDataSource = tasksLocalDataSource;
}
在TasksRepositoryModule提供两个实例的创建方式
@Singleton
@Provides
@Local
TasksDataSource provideTasksLocalDataSource(Context context) {
return new TasksLocalDataSource(context);
}
@Singleton
@Provides
@Remote
TasksDataSource provideTasksRemoteDataSource() {
return new FakeTasksRemoteDataSource();
}
Dagger会为TasksRepository创建一个工厂类:TasksRepository_Factory
public static Factory<TasksRepository> create(
Provider<TasksDataSource> tasksRemoteDataSourceProvider,
Provider<TasksDataSource> tasksLocalDataSourceProvider) {
return new TasksRepository_Factory(tasksRemoteDataSourceProvider, tasksLocalDataSourceProvider);
}
以上,可以看出,Constructor injection影响的是类实例的创建工厂。
这里涉及到两个相同的实例TasksDataSource,一个是remote,一个是local,它们通过scope来区分,后面会讲述scope的原理。
Fields injection
这是最常用的注入方式,这种注入方式要了解的是Dagger是怎样注入的,是什么时候注入的。
第一个问题,如何注入?
1.拥有Fields injection行为的类A,Dagger会为它生成一个注入器A_MembersInjector;
注入器的结构如下:
public interface MembersInjector<T> {
void injectMembers(T instance);}
2.注入器A_MembersInjector需要实现方法: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() 方法被调用的时候;
- 主动调用Component中定义的inject()方法时
第一种方式,查看TaskDetailPresenter_Factory可知,简单的理解就是,当Dagger主动通过工厂类创建实例的时候,需要调用注入器注入该类依赖的属性和方法。源码如下:
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中创建Component实例,然后通过调用Component中inject()方式注入属性和方法。源码如下:
调用方式:
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
同Fields injection类似,Methods injection也是通过注入器在实例创建好的时候调用injectMembers() 方法注入的,需要注意的是,方法注入可能涉及到参数的注入,这些参数要求可以在Dagger的Provider中查找到。用Dagger2的原文文档就是 “All method parameters are provided from dependencies graph.”
Singleton
单例的实现依赖于特殊的Provider机制,在Module中以@Provides注解声明的方法都会创建一个工厂类以提供实例,比如在TasksRepositoryModule
中的TasksDataSource provideTasksLocalDataSource(Context context),生成了TasksRepositoryModule_ProvideTasksLocalDataSourceFactory;在TaskDetailPresenterModule
中TaskDetailContract.View provideTaskDetailContractView()
生成了TaskDetailPresenterModule_ProvideTaskDetailContractViewFactory;
它们的本质是一样的,都是一样的工厂类提供者。
不同的地方在于,在Module中以@Singleton声明的方法,在提供实例的时候,并不是直接调用工厂类的get() 方法,而是通过ScopedProvider的get()方法类创建实例,而这个ScopedProvider就保证了实例的单例:
ScopedProvider源码如下, 其get方法就是一个典型的单例模式实现:
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底层的实现,也就是上文中提到的ScopedProvider的应用。
这里有一个误区,很多文章喜欢用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();
}
}
实验的表现:
user1 和 user2 是同一个实例,user3 不同于user1是另外一个实例;
person1 和person2 不是同一个实例。
现象解释:
User声明为@Singleton, user1和user2来源于同一个component(UserComponent),所以是同一个实例;
user3来源于PresenterComponent,尽管User被声明为@Singleton,但它却是一个新的实例;
Person是个普通的实例,没有声明为@Singleton,尽管person1和person2 来源于同一个component,但是却创建了两次,所以不是同一个实例。
所以结论就是scope没有想象中的那么牛逼,它并不能控制实例的生命周期,并不是@Singleton就是全局单例,@FragmentScope @ActivityScope @ApplicationScope就是其字面表达的意思。它的本质都是依赖于component的生命周期的。
Qualifier
@Qualifier annotation helps us to create “tags” for dependencies which have the same interface。这句话说的很形象,@Qualifier就是一个tag。想象一下,如果在Module中你需要provide两个TasksDataSource,你就需要通过@Qualifier来区分了。示例如下:
定义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来区分:
@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。同时,这将产生一种副作用——子component同时具备两种不同生命周期的scope。子Component具备了父Component拥有的Scope,也具备了自己的Scope。
Subcomponent其功能效果优点类似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中你会发现只能@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的创建方式。做完这两个修改之后,就可以在BActivity中注入Person和User了。
那么,采用Subcomponent之后,@Subcomponent标注之后的Component不再生成DaggerXXXComponent的实现类,要获取BComponent实例,你需要通过DaggerBBaseComponent.builder().build().bcomponent(new BModule()); 即需要通过父Component实例来获取。究竟发生什么了呢?观察源码可知:
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的实现类在BBaseComponent中,并且BComponent用到的两个实例Person和User,是通过BBaseComponent中的providePersonProvider 和 provideUserProvider获取的。这就解释了上文中提到的“子Component具备了父Component拥有的Scope,也具备了自己的Scope”。
结论
知其然更要知其所以然,了解了背后的机制,你会发现dagger2也没那么复杂和神秘。