Dagger2使用案例&原理分析

蓋亞光 / 文 发表于2018-01-02 22:34 次阅读 dagger2

Dagger2是目前流行的一个依赖注入框架。使用它可以降低我们程序中类与类之间的耦合。类实例的创建,初始化,销毁及相互依赖都交由dagger2来管理。我们只需要专注于类本身的业务逻辑,提高我们编写程序的便利性。

传统MVP案例

MVP是我们项目中经常使用的一个应用框架。Model层负责具体的业务逻辑,View层负责界面的展示,Presenter层负责协调Model层与View层,通过调用Model层的业务方法,更新View层的界面展示。现在假如我们由这样一个需求:要求我们获取一段信息在界面上展示。应用MVP框架,我们可以这样实现:

/**
 * 定义一个View层的统一接口
 */
public interface IView {

    void updateUI(String text);
}

/**
 * View层,负责界面的展示
 */
public class TestActivity extends AppCompatActivity implements IView {

    TestPresent mPresent;//Present层实例

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        mPresent=new TestPresent(this);//创建Present层的实例
        mPresent.updateUI();//调用Present层的更新UI的方法

    }

    @Override
    public void updateUI(String text) {
        ((TextView)findViewById(R.id.textview)).setText(text);
    }
}

/**
 * Present类,调用Model层的业务方法,更新View层的界面展示
 */
public class TestPresent {

    IView mView;//View层实例
    TestModel mModel;//Model层实例

    //构造方法,传入View层的实例
    public TestPresent(IView view){
        this.mView=view;
        this.mModel=new TestModel();//创建Model层的实例
    }

    //更新UI的方法
    public void updateUI(){
        mView.updateUI(mModel.getText());//调用Model层是业务逻辑,更新View层的界面展示
    }
}

/**
 * Model类,实现具体的业务逻辑
 */
public class TestModel {

    public TestModel(){
    }
    //返回需要展示的信息
    public String getText(){
        return "Dagger2应用实践...";
    }
}

通过以上代码,我们顺利的通过MVP框架实现了我们的需求,代码模块间分工明确,各模块只负责自己本模块的工作。但是这里有一个问题,模块之间相互依赖,一个模块都要依赖其它的模块才能完成实际的功能。根据单一职责原则,一个内聚性高的类,应该只做自己类本身的工作,在需要使用其它的类时,我们只要调用类实例的方法就可以了,实例的创建,初始化,销毁等工作,则不应该由本类负责。通过依赖注入框架,我们就可以只关注类本身的工作,依赖类的创建,初始化,销毁等工作,统统交给依赖注入框架来处理。

Dagger2案例

Dagger2是通过apt根据注解在编译时生成额外的代码实现依赖注入,所以要使用dagger2,首先我们需要在build.gradle(Project:xxx)中配置apt插件:

dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
        //添加apt插件
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }

在build.gradle(Module:app)中添加添加dagger2依赖:

apply plugin: 'com.android.application'
//添加如下代码,应用apt插件
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
    ...
    //dagger2
    compile 'com.google.dagger:dagger:2.4'
    apt 'com.google.dagger:dagger-compiler:2.4'
    //java注解
    compile 'org.glassfish:javax.annotation:10.0-b28'
}

使用Dagger2注解修改各个类:

/**
 * 定义一个View层的统一接口
 * View接口保持不变
 */
public interface IView {

    void updateUI(String text);
}

/**
 * View层,负责界面的展示
 */
public class TestActivity extends AppCompatActivity implements IView{

//当一个成员变量被@Inject注解修饰,并且它的类型构造函数也被@Inject注解修饰,dagger2就会自动实例化该成员类型,并注入到该成员变量
    @Inject
    TestPresent mPresent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        DaggerTestComponent.builder().testModule(new TestModule(this)).build().inject(this);//@Component负责连接起@Inject和@Module注解
        mPresent.updateUI();

    }

    @Override
    public void updateUI(String text) {
        ((TextView)findViewById(R.id.textview)).setText(text);
    }
}

/**
 * Present类,调用Model层的业务方法,更新View层的界面展示
 */
public class TestPresent {

    IView mView;
    @Inject
    TestModel mModel;//Dagger2遇到@Inject标记的成员属性,就会去查看该成员类的构造函数,如果构造函数也被@Inject标记,则会自动初始化,完成依赖注入。

    //TestPresent的构造函数也被@Inject注解修饰
    @Inject
    public TestPresent(IView view){
        this.mView=view;
    }

    public void updateUI(){
        mView.updateUI(mModel.getText());
    }
}

/**
 * Model类,实现具体的业务逻辑
 */
public class TestModel {

    //构造函数用@Inject修饰
    @Inject
    public TestModel(){
    }

    public String getText(){
        return "Dagger2应用实践...";
    }
}

/**
 * Module类提供那些没有构造函数的类的依赖,如第三方类库,系统类,接口类
 */
@Module
public class TestModule {

    private IView mView;

    public TestModule(IView iView){
        this.mView=iView;
    }

    //@Provides注解的方法,提供IView类的依赖。
    @Provides
    public IView provideIView(){
        return this.mView;
    }
}

/**
 *Component必须是一个接口类或者抽象
 */
@Component(modules = TestModule.class)
public interface TestComponent {

    void inject(TestActivity testActivity);
}

总结:

  • @Inject 如果一个成员变量被@Inject注解修饰,并且它的类型构造函数也被@Inject注解修饰,那么dagger2就可以帮我们实例化该成员类型,并注入。例如:TestActivity类中的mPresent以及TestPresent类中的mModel,都属于这种情况。
  • @Module 注解修饰的类负责提供那些没有构造函数的类的依赖,如第三方类库,系统类,接口类。
  • @Provides 注解修饰的方法,必须以provide开头后跟提供的依赖类型。负责提供具体类型的依赖,一个@Module修饰的类可以有多个@Provides修饰的方法。
  • @Component 注解修饰的接口或抽象类,负责在@Inject和@Module之间建立连接,当实例化@Inject注解的类时,遇到没有构造函数的成员变量依赖,则该依赖由@Module修饰的类提供。inject方法用于获取TestActivity的实例。

看到这里小伙伴们大概已经知道如何使用dagger2了。但对于代码为何这样写,dagger2内部是怎么帮我们实现各个类间的依赖注入的还不是很清楚。接下来,我们就进入到dagger2为我们生成类的源码中看看,dagger2分别为我们实现了那些类,这些类之间是如何协作来帮助我们实现依赖注入的。

Dagger2原理分析

DaggerTestComponent.builder().testModule(new TestModule(this)).build().inject(this);//@Component连接起@Inject和@Module的关键

DaggerTestComponent,这是什么鬼,我们好像没有定义过这个类啊,别急,这个dagger2根据@Component注解帮我们生成的类,我们进去看看它具体做了什么工作。

DaggerTestComponent源码:

/**
 1. dagger2根据@Component注解帮我们生成的类,实现我们定义的TestComponent接口
*/
public final class DaggerTestComponent implements TestComponent {

  private MembersInjector<TestPresent> testPresentMembersInjector;//负责注入TestPresent中被@Inject注解修饰的的成员变量的依赖。dagger2会为TestPresent生成一个TestPresent_MembersInjector类,专门负责TestPresent中@Inject注解的依赖注入

  private Provider<IView> provideIViewProvider;//负责注入TestPresent中没有构造函数的成员变量的依赖。dagger2会依据@Component中传入的modules值(示例中为TestModule),为Module类中@Provides修饰的方法(示例中为provideIView)提供一个TestModule_ProvideIViewFactory类

  private Provider<TestPresent> testPresentProvider;//提供TestActivity中TestPresent类型的依赖

  private MembersInjector<TestActivity> testActivityMembersInjector;//负责注入TestActivity中@Inject修饰的成员变量

  private DaggerTestComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.testPresentMembersInjector =
        TestPresent_MembersInjector.create(TestModel_Factory.create());//创建TestModel_Factory实例,再根据生成的TestModel_Factory实例创建TestPresent_MembersInjector的实例

    this.provideIViewProvider = TestModule_ProvideIViewFactory.create(builder.testModule);//通过传入的TestModule实例创建TestModule_ProvideIViewFactory的实例

    this.testPresentProvider =
        TestPresent_Factory.create(testPresentMembersInjector, provideIViewProvider);//根据testPresentMembersInjector和provideIViewProvider创建TestPresent_Factory的实例

    this.testActivityMembersInjector = TestActivity_MembersInjector.create(testPresentProvider);//根据testPresentProvider创建TestActivity_MembersInjector的实例
  }

  @Override
  public void inject(TestActivity testActivity) {
    testActivityMembersInjector.injectMembers(testActivity);//调用testActivityMembersInjector的injectMembers方法为TestActivity提供依赖注入
  }

  public static final class Builder {
    private TestModule testModule;

    private Builder() {}

    public TestComponent build() {
      if (testModule == null) {
        throw new IllegalStateException(TestModule.class.getCanonicalName() + " must be set");
      }
      return new DaggerTestComponent(this);
    }

    //传入TestModule的实例
    public Builder testModule(TestModule testModule) {
      this.testModule = Preconditions.checkNotNull(testModule);
      return this;
    }
  }
}

可以看到,dagger2为TestActivity实现依赖注入的步骤大概如下:

  1. 根据@Component注解生成Dagger开头的DaggerXxxComponent类(示例中为DaggerTestComponent类)
  2. 根据@Component注解中传入的modules值(示例中为TestModule.class),在DaggerXxxComponent的静态内部类Builder中生成相应的xxxModule方法,传入Module的实例。
  3. 实现@Component注解接口XxxComponent(示例中为TestComponent)中的inject方法,传入TestActivity的示例。

此时,dagger2已经通过@Component关联起了@Inject和@Module注解。但是具体是怎么注入的我们还不是很清楚,我们看到inject方法中调用testActivityMembersInjector的injectMembers()方法完成了对TestActivity的依赖注入,我们再看TestActivity_MembersInjector的源码:

/**
 * dagger2根据TestActivity中的@Inject注解生成的成员注入类
 */
public final class TestActivity_MembersInjector implements MembersInjector<TestActivity> {

  private final Provider<TestPresent> mPresentProvider;

  public TestActivity_MembersInjector(Provider<TestPresent> mPresentProvider) {
    assert mPresentProvider != null;
    this.mPresentProvider = mPresentProvider;//将create方法中传入的mPresentProvider赋值mPresentProvider
  }

  //mPresentProvider即为DaggerTestComponent中创建的TestPresent_Factory的实例testPresentProvider
  public static MembersInjector<TestActivity> create(Provider<TestPresent> mPresentProvider) {
    return new TestActivity_MembersInjector(mPresentProvider);
  }

  //依赖注入方法,在DaggerTestComponent的inject方法中被调用
  @Override
  public void injectMembers(TestActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.mPresent = mPresentProvider.get();//通过mPresentProvider的get方法获取到TestPresent的实例并赋值给TestActivity中的mPresent
  }

  public static void injectMPresent(TestActivity instance, Provider<TestPresent> mPresentProvider) {
    instance.mPresent = mPresentProvider.get();
  }
}

可以看到,上面代码中是通过mPresentProvider(TestPresent_Factory的实例)的get方法获取到TestPresent的实例并复制给TestActivity中的mPresent,TestPresent_Factory的源码:

/**
 * dagger2生成的TestPresent工厂类
 */
public final class TestPresent_Factory implements Factory<TestPresent> {
  private final MembersInjector<TestPresent> testPresentMembersInjector;//提供TestPresent中@Inject注解修饰的成员变量的依赖

  private final Provider<IView> viewProvider;//提供TestPresent中不能被@Inject修饰的成员变量(变量类型没有构造函数或构造函数上无法加@Inject注解)的依赖

  public TestPresent_Factory(
      MembersInjector<TestPresent> testPresentMembersInjector, Provider<IView> viewProvider) {
    assert testPresentMembersInjector != null;
    this.testPresentMembersInjector = testPresentMembersInjector;
    assert viewProvider != null;
    this.viewProvider = viewProvider;
  }

  @Override
  public TestPresent get() {
    //首先根据传入的viewProvider中的get方法获取到IView实例,创建TestPresent的实例,
    // 再通过MemberInjectors.injecMembers方法,为创建的TestPreset实例注入@Inject修饰的成员变量(mModule)
    //然后返回创建的TestPresent实例
    return MembersInjectors.injectMembers(
        testPresentMembersInjector, new TestPresent(viewProvider.get()));
  }

  //此处传入的testPresentMemberInjector即为DaggerTestComponent中创建的TestPresent_MembersInjector的实例
  //viewProvider即为DaggerTestComponent中创建的TestModule_ProvideIViewFactory的实例
  public static Factory<TestPresent> create(
      MembersInjector<TestPresent> testPresentMembersInjector, Provider<IView> viewProvider) {
    return new TestPresent_Factory(testPresentMembersInjector, viewProvider);
  }
}

TestPresent_Factory中通过viewProvider的get方法获取到IView的实例,此处的viewProvider即为DaggerTestComponent中传入的TestModule_ProvideIViewFactory实例:

/**
 * dagger2为@Module修饰的类中的每个@Provides修饰的方法生成的类
 */
public final class TestModule_ProvideIViewFactory implements Factory<IView> {
  private final TestModule module;

  public TestModule_ProvideIViewFactory(TestModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public IView get() {
    return Preconditions.checkNotNull(
        module.provideIView(), "Cannot return null from a non-@Nullable @Provides method");//通过传入的TestModule实例的provideIView方法获取IView的实例并返回
  }

  //此处的module即为TestActivity中通过DaggerTestComponet.Builder().testModule()方法传入的TestModule实例
  public static Factory<IView> create(TestModule module) {
    return new TestModule_ProvideIViewFactory(module);
  }
}

TestPresent_Factory中通过MembersInjectors中的injectMembers方法,为创建的TestPresent注入@Inject修饰的成员变量(mModel)的依赖。MembersInjectors的源码:

public final class MembersInjectors {

  public static <T> T injectMembers(MembersInjector<T> membersInjector, T instance) {

    membersInjector.injectMembers(instance);//membersInjector为DaggerTestComponent中传入的TestPresent_MembersInjector的实例testPresentMembersInjector,实际上调用的是TestPresent_MembersInjector的injectMembers()方法
    return instance;
  }

}

TestPresent_MembersInjector的源码:

public final class TestPresent_MembersInjector implements MembersInjector<TestPresent> {
  private final Provider<TestModel> mModelProvider;

  public TestPresent_MembersInjector(Provider<TestModel> mModelProvider) {
    assert mModelProvider != null;
    this.mModelProvider = mModelProvider;
  }

  public static MembersInjector<TestPresent> create(Provider<TestModel> mModelProvider) {
  //mModelProvider为DaggerTestComponent中传入的TestModel_Factory的实例
    return new TestPresent_MembersInjector(mModelProvider);
  }

  @Override
  public void injectMembers(TestPresent instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.mModel = mModelProvider.get();//通过mModelProvider的get方法获取到TestModel的实例并赋值给TestPresent实例中的mModel。
  }

  public static void injectMModel(TestPresent instance, Provider<TestModel> mModelProvider) {
    instance.mModel = mModelProvider.get();
  }
}

TestModel_Factory源码:

public enum TestModel_Factory implements Factory<TestModel> {
  INSTANCE;

  @Override
  public TestModel get() {
    return new TestModel();//返回TestModel的实例
  }

  public static Factory<TestModel> create() {
    return INSTANCE;//返回TestModel_Factory的实例
  }
}

至此,我们完整分析了Dagger2为我们实现依赖注入的全过程,相信大家对Dagger2为我们实现依赖注入的原理有了一定的了解。有什么不解的欢迎大家在评论区给我留言,我会尽量作答。

收藏 赞 (0) 踩 (0)