Daggger2 概念解读、使用姿势及源码分析(2)

356 阅读6分钟

上一篇文章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();
  }
}

第二个问题:什么时候注入?

注入时机有两种:

  1. 当对应的A_Factory中的get() 方法被调用的时候;
  2. 主动调用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,最终在引用的时候,就分别通过这两个工厂类提供实例。

结论

知其然更要知其所以然,了解了背后的机制,你会发现dagger2也没那么复杂和神秘。