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

上一篇文章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 create(
    Provider tasksRemoteDataSourceProvider,
    Provider 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 {
  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 {

      public static MembersInjector create(
      Provider 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 {
  @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 implements Provider, Lazy {
 @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  Provider create(Factory factory) {
    if (factory == null) {
      throw new NullPointerException();
    }
    return new ScopedProvider(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 providePersonProvider;

  private Provider provideUserProvider;

  @Override
  public BComponent bcomponent(BModule bModule) {
    return new BComponentImpl(bModule);
  }

  private final class BComponentImpl implements BComponent {
    private final BModule bModule;

    private MembersInjector 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也没那么复杂和神秘。

上一篇:Daggger2 概念解读、使用姿势及源码分析(1)

你可能感兴趣的:(Daggger2 概念解读、使用姿势及源码分析(2))