Mark Xu 的博客

记录精彩的程序人生

Dagger2 学习总结及源码浅析

Dagger2 是一个解决 Android 或 Java 的依赖注入类库,关于 Dagger2 的教程网上已经很多了,此处推荐《Dagger2 入门, 以初学者角度》《Android:dagger2 让你爱不释手 - 基础依赖注入框架篇》这两篇,对照教程可以基本学会使用方法。本文算是我自己在学习 Dagger2 过程中的一个精华汇总吧,同时外加实现源码分析。

学习总结

总览

1、Java 中万物皆对象,每一个对象可以拥有很多成员变量,这些成员变量可以是各种对象

2、依赖注入,顾名思义指一个对象所依赖的对象的注入过程,省去了手动去为一个对象 new 一堆依赖的过程

3、Module 是依赖提供方,Activity、Fragment 等是依赖需求方,Component 是依赖注入器

4、Component 是一个接口,编译代码后,会自动生成一个实现类

工作流程

1、调用 Component 实现类的 inject 方法,把依赖需求方对象(比如 Activity 自身),传递给 Component

2、Component 根据依赖需求方的需求,从 Module 中寻找现成的实例对象,并注入给依赖需求方(Activity)

关键注解说明

@Component

一个标注的 @Component 注解形式如下:

@Component(dependencies = AppComponent.class,modules = {MainModule.class, ActivityModule.class})
public interface MainComponent extends ActivityComponent{
    ...
}

@Component 注解有两个属性:dependencies 和 modules。dependencies 属性声明 @Component 的依赖关系,modules 属性声明 @Component 后期会从哪些 @Module 中去寻找依赖实例。

我们在 创建 @Component 的实现类对象时,需要提供父 Component 实例对象和 Module 实例对象,如下:

mMainComponent = DaggerMainComponent.builder().appComponent(getAppComponent())
        .mainModule(new MainModule())
        .activityModule(new ActivityModule(this)).build();
mMainComponent.inject(this);

@Scope

Scope 的意思是 “范围”,即用来声明依赖对象作用范围的

Dagger2 有一个机制:在同一个作用范围内,@Provides 方法提供的依赖对象会变成单例。即依赖需求方不管依赖几次 @Provides 提供的对象,Dagger2 都会只调用一次该 @Provides 方法来获取依赖实例,后期再需要该依赖实例时直接进行提供。

@Scope 注解需要自定义注解才可以使用,@Singleton 就是一个自定义注解:

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

依赖提供方 Module 的 @Provides 方法使用 @Singleton 注解声明,依赖注入器 Component 也使用 @Singleton 注解声明即可。这样 Module 提供的依赖对象就是单例的了。

@Qulifier

Dagger2 是通过返回值类型来从 Module 中获取依赖对象的。也就是说像如下的 Module 代码中,Component 并不知道该调用哪一个方法来生成对象:

@Provides
public Cloth getRedCloth() {
    Cloth cloth = new Cloth();
    cloth.setColor("红色");
    return cloth;
}
@Provides
public Cloth getBlueCloth() {
    Cloth cloth = new Cloth();
    cloth.setColor("蓝色");
    return cloth;
}

所以需要一个 Qulifier 注解来进行区分:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface RedCloth {
}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface BlueCloth {
}

使用 @RedCloth 和 @BlueCloth 对依赖需求方和依赖提供方进行注解,便可进行区分

源码浅析

注解

Dagger2 的工作主要是通过注解来实现的,所以理解注解至关重要,这里推荐一篇讲解注解很清晰的文章:秒懂,Java 注解 (Annotation)你可以这样学

注解处理器

和 EventBus、ButterKnife 一样,Dagger2 也是通过注解处理器来在编译器进行代码生成,关于注解处理器可以参考我的另一篇博文:EventBus 3.0 源码分析(二)注解处理器的使用

源码分析

我们先来定义一个最简单的 Dagger2 的使用实例:

MainActivity 类如下:

public class MainActivity extends AppCompatActivity {
    @Inject
    public Computer mComputer;

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

        MainComponent mainComponent = DaggerMainComponent.builder()
                .mainModule(new MainModule())
                .build();
        mainComponent.inject(this);
    }
}

@Module 类如下:

@Module
public class MainModule {
    @Provides
    public Computer providesAppleComputer() {
        return new Computer("Apple");
    }
}

@Component 类如下:

@Component(modules = MainModule.class)
public interface MainComponent {
    void inject(MainActivity activity);
}

然后点击 rebuild project 进行编译。我这边在第一次进行代码编译时,并没有如预期生成 @Component 的实现类,发现由于 Dagger2 的引入是在 子 Module 中引入的,因此在 app 模块中注解处理器无法为我们生成代码。只要在主模块引入注解处理器即可:

annotationProcessor 'com.google.dagger:dagger-compiler:2.15'

在 MainActivity 代码中可以看到,我们先通过 Component 的实现类的 builder 模式获取到实例对象,再调用 inject 方法进行依赖注入。注入成功后此时 MainActivity 的成员变量就已经赋值了。

我们看看 Component 的实现类 DaggerMainComponent 的源码:

public final class DaggerMainComponent implements MainComponent {
    private MainModule mainModule;

    private DaggerMainComponent(Builder builder) {
        initialize(builder);
    }

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

    public static MainComponent create() {
        return new Builder().build();
    }

    @SuppressWarnings("unchecked")
    private void initialize(final Builder builder) {
        this.mainModule = builder.mainModule;
    }

    @Override
    public void inject(MainActivity activity) {
        injectMainActivity(activity);
    }

    private MainActivity injectMainActivity(MainActivity instance) {
        MainActivity_MembersInjector.injectMComputer(
                instance, MainModule_ProvidesAppleComputerFactory.proxyProvidesAppleComputer(mainModule));
        return instance;
    }

    public static final class Builder {
        private MainModule mainModule;

        private Builder() {}

        public MainComponent build() {
            if (mainModule == null) {
                this.mainModule = new MainModule();
            }
            return new DaggerMainComponent(this);
        }

        public Builder mainModule(MainModule mainModule) {
            this.mainModule = Preconditions.checkNotNull(mainModule);
            return this;
        }
    }
}

代码精简一下,其实也很简单,仅仅是一个标准的 builder 模式,外加实现了 Component 接口的 inject 方法:

public final class DaggerMainComponent implements MainComponent {
    private MainModule mainModule;

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

    @Override
    public void inject(MainActivity activity) {
        injectMainActivity(activity);
    }

    public static final class Builder {
        // ...
    }
}

接下来我们一一看:

首先是 builder 模式的 build 方法:

public MainComponent build() {
    if (mainModule == null) {
        this.mainModule = new MainModule();
    }
    return new DaggerMainComponent(this);
}
private DaggerMainComponent(Builder builder) {
    initialize(builder);
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
    this.mainModule = builder.mainModule;
}

很好理解,其实就是给 DaggerMainComponent 的成员变量 mainModule 对象进行赋值操作。接下来看看比较关键的注入操作:

@Override
public void inject(MainActivity activity) {
    injectMainActivity(activity);
}
private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectMComputer(
            instance, MainModule_ProvidesAppleComputerFactory.proxyProvidesAppleComputer(mainModule));
    return instance;
}

可以看到,注入操作是调用了 MainActivity_MembersInjector 这个类的 injectMComputer 方法,看看这个方法:

public static void injectMComputer(MainActivity instance, Computer mComputer) {
	instance.mComputer = mComputer;
}

在 injectMComputer 方法中,直接把第二个参数 Computer 对象赋值给依赖需求方 MainActivity。MainActivity 是我们直接手动传递过来的,那么第二个参数的依赖对象是怎么来的呢,也就是 MainModule_ProvidesAppleComputerFactory.proxyProvidesAppleComputer(mainModule) 这一行代码是如何拿到一个目标依赖对象的呢?

编译时 Dagger2 自动为我们生成了 MainModule_ProvidesAppleComputerFactory 这个类,看名称就知道是提供 AppleComputer 的工厂类,对应着我们 MainModule 中的 providesAppleComputer。看看 proxyProvidesAppleComputer 方法:

public static Computer proxyProvidesAppleComputer(MainModule instance) {
	// 此处做一下非空判断
	return Preconditions.checkNotNull(
		instance.providesAppleComputer(),
		"Cannot return null from a non-@Nullable @Provides method");
}

参数 MainModule 就是刚才在 builder 方法中初始化的那个 MainModule。最终返回的是 MainModule 的 providesAppleComputer 方法所提供的实例对象。至此,Component 就完成了从 Module 中获取依赖实例对象,并注入到依赖需求方的过程。

留下你的脚步