从零开始学dagger2

时间:2021-12-28 21:01:24

前言

dagger2这个框架做安卓的同学应该都听说过,现在公司一般的项目架构都是Retrofit + rxjava + mvp,然后dagger2框架又很适用于mvp模式,所以它也十分主流,虽然十分主流,但是很多公司的项目依然没用用起来,为什么呢?因为它和rxjava一样,学习曲线很陡,刚学起来是比较吃力的,作者也是如此。

这篇文章题是从零开始学dagger2,对于作者而言是第三次从零开始,因为在去年的时候我也是花过时间和心思研究过这个框架,奈何水平有限还是比较晕,但是没关系,一遍不行就第二遍,再不行就加一遍,希望通过这篇文章同大家一起学习。

正文

dagger2是一个依赖注入的框架,什么叫依赖注入呢?假如我是一家卖手机的公司,我虽然卖手机,但是不可能所有的零件都是我自己造吧?CPU、内存这些要自己造可不简单,所以我就要找别的公司帮我造这些东西,依赖关系就产生了,手机公司就依赖生产CPU的公司了。

/**
* 手机公司
*/

public class CellPhoneCompany {
CpuCompany cpuCompany;

public CellPhoneCompany() {
cpuCompany = new CpuCompany();
}

public CellPhoneCompany(CpuCompany cpuCompany) {
this.cpuCompany = cpuCompany;
}

// 生产手机
public void makePhone() {
cpuCompany.makeCpu();
}
}

/**
* Cpu生产公司
*/

public class CpuCompany {

public CpuCompany() {

}

public void makeCpu() {

}
}

如果使用dagger2会变成什么样呢?

/**
* 手机公司
*/

public class CellPhoneCompany {
@Inject
CpuCompany cpuCompany;

// 生产手机
public void makePhone() {
cpuCompany.makeCpu();
}
}

/**
* Cpu生产公司
*/

public class CpuCompany {
@Inject
public CpuCompany() {

}

public void makeCpu() {

}
}

在依赖类的构造方法和被使用的地方都添加了一个@Inject注解,当然光靠这个注解是无法使用的,还需要其他的注解配合起来才能使用。因为使用dagger2,CellPhoneCompany中减少了2个用来添加依赖的方法,不仅如此,假如我们在开发过程中CpuCompany的构造方法需要添加参数,也就是说所有依赖于CpuCompany的地方都需要做修改,那是很麻烦的。dagger2的作用就是降低这种依赖的耦合度。

在使用dagger2之前我们要把它引入项目。添加依赖:

// dagger2
compile 'com.google.dagger:dagger:2.7'
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'

@Inject、@Component、@Module、@Provides

看到这4个注解不要慌,我也不会向你用语言去解释他们是干吗的,而是会带你从源码中反过来去理解它们是干吗的。

搭好dagger2之后就来使用吧,还是继续我们上面的例子

/**
* Cpu生产公司
*/

public class CpuCompany {

@Inject
public CpuCompany() {

}

// 生产CPU
public void makeCpu() {

}
}

这里在构造方法上加了一个@Inject的注解,被这个注解注释的类的构造方法,dagger2会通过内部机制找到它,然后初始化

然后还需要有一个对应的Component,它的作用就是桥接CpuCompany和CellPhoneCompany的

@Component
public interface CpuCompanyComponent {
CpuCompany inject();
}

这里使用的是接口,作者试了,使用class会报错,找不到这个CpuCompanyComponent。然后这里的inject()方法和前面的@Inject注解没有关系,你可以把它改成你想要的名字。

接下来是使用的方法:

public class CellPhoneActivity extends AppCompatActivity {
private static final String TAG = "Dagger2Log";

@Inject
CpuCompany cpuCompany;

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

// 重点是这句
cpuCompany = DaggerCpuCompanyComponent.builder().build().inject();

Log.i(TAG, "onCreate: " + cpuCompany);
}
}

这里要注意的是,如果你直接使用DaggerCpuCompanyComponent是找不到这个类的,因为dagger2通过apt插件生成代码,需要你build/make project后才能使用。android studio的同学可以使用这个:

从零开始学dagger2

让我们来分析一下,首先DaggerCpuCompanyComponent这个类的名字是CpuCompanyComponent的前面加了一个“Dagger”,而我们的CpuCompanyComponent类确实是被@Component所注释的,这就是他的命名规则,然后我们看一下源码:

public final class DaggerCpuCompanyComponent implements CpuCompanyComponent {
private DaggerCpuCompanyComponent(Builder builder) {
assert builder != null;
}

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

public static CpuCompanyComponent create() {
return builder().build();
}

@Override
public CpuCompany inject() {
return CpuCompany_Factory.create().get();
}

public static final class Builder {
private Builder() {}

public CpuCompanyComponent build() {
return new DaggerCpuCompanyComponent(this);
}
}
}

这里做了几件事情:
1. builder()方法返回一个Builder实例,这个Builder是一个静态内部类
2. build()方法返回一个DaggerCpuCompanyComponent实例,这里会把Builder自身给传进去
3. inject()方法返回我们所需要的CpuCompany实例,这个inject()方法就是通过实现CpuCompanyComponent接口而来的
4. 这里有一个CpuCompany_Factory,看名字应该是用来创建CpuCompany的工厂,我们看一下:

public enum CpuCompany_Factory implements Factory<CpuCompany> {
INSTANCE;

@Override
public CpuCompany get() {
return new CpuCompany();
}

public static Factory<CpuCompany> create() {
return INSTANCE;
}
}

public interface Factory<T> extends Provider<T> {
}

public interface Provider<T> {
T get();
}

发现了吗,这个类是个enum,我第一次见枚举还可以这样使用,枚举有线程安全、唯一的特点,这个枚举实现了Factory接口然后通过create()方法返回自身实例,再通过get()方法返回我们真正所需要的CpuCompany实例,这个get方法是从Provider接口所继承下来实现的,返回的类型就是我们注解的类型,这里记一下这个Provider接口,我们待会还会联系到这个接口。

关系是这样的:
要使用CpuCompany的地方–>DaggerCpuCompanyComponent–>CpuCompany_Factory–>CpuCompany

dagger2就是以这样一种方式,通过Component组件把调用者和被调用者联系起来。就做到了用
DaggerCpuCompanyComponent.builder().build().inject()代替new CpuCompany(),你可能会问,就这样我不但没减少代码,还增加了好多东西有个啥用呢?别急,这只是一个最简单例子来探索dagger2基本的实现,接下来让我们继续深入。

这里我们必须使用cpuCompany = DaggerCpuCompanyComponent.builder().build().inject()才能实例化,因为我们的inject()方法的返回类型是CpuCompany,那能不能更加简单一点呢?答案是肯定的

修改一下CpuCompanyComponent

@Component
public interface CpuCompanyComponent {
void inject(CellPhoneActivity activity);
}

重新make project一下,我们就可以把cpuCompany = DaggerCpuCompanyComponent.builder().build().inject()改成==> DaggerCpuCompanyComponent.builder().build().inject(this)了,给我的感觉是原先的方式支持在任何地方实例化,修改过后的方式变成了只能在CellPhoneActivity中实例化,有利有弊,就我们现在而言使用后者要更加方便

看一下源码做了什么改变:

public final class DaggerCpuCompanyComponent implements CpuCompanyComponent {
private MembersInjector<CellPhoneActivity> cellPhoneActivityMembersInjector;

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

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

public static CpuCompanyComponent create() {
return builder().build();
}

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

this.cellPhoneActivityMembersInjector =
CellPhoneActivity_MembersInjector.create(CpuCompany_Factory.create());
}

@Override
public void inject(CellPhoneActivity activity) {
cellPhoneActivityMembersInjector.injectMembers(activity);
}

public static final class Builder {
private Builder() {}

public CpuCompanyComponent build() {
return new DaggerCpuCompanyComponent(this);
}
}
}

修改的地方有:
1. 在DaggerCpuCompanyComponent的构造方法中添加了一个initialize方法
2. 新增了一个成员变量MembersInjector,看这个接口的名字和内容就知道是注册成员列表,也就是我们的CellPhoneActivity。

public interface MembersInjector<T>{
void injectMembers(T instance);
}
  1. 这个MembersInjector是在initialize方法中被创建的,这里又有一个CellPhoneActivity_MembersInjector对象,让我们继续跟进去
public final class CellPhoneActivity_MembersInjector implements MembersInjector<CellPhoneActivity> {
private final Provider<CpuCompany> cpuCompanyProvider;

public CellPhoneActivity_MembersInjector(Provider<CpuCompany> cpuCompanyProvider) {
assert cpuCompanyProvider != null;
this.cpuCompanyProvider = cpuCompanyProvider;
}

public static MembersInjector<CellPhoneActivity> create(Provider<CpuCompany> cpuCompanyProvider) {
return new CellPhoneActivity_MembersInjector(cpuCompanyProvider);
}

@Override
public void injectMembers(CellPhoneActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.cpuCompany = cpuCompanyProvider.get();
}

public static void injectCpuCompany(
CellPhoneActivity instance, Provider<CpuCompany> cpuCompanyProvider) {
instance.cpuCompany = cpuCompanyProvider.get();
}
}

这里我们又看见Provider了,感情他就是个提供者的角色,提供一些参数。这段代码就做了2件事情:(一)create()方法返回一个带有CpuCompany实例的CellPhoneActivity_MembersInjector实例;(二)injectMembers()方法把CellPhoneActivity和CpuCompany给关联起来了,这样一来经过我们修改后的代码整个逻辑就通顺了。

你可能会问,假如CpuCompany的构造方法中从无参变成有参了呢?

/**
* Cpu生产公司
*/

public class CpuCompany {
String color;

@Inject
public CpuCompany(String color) {
this.color = color;
}

// 生产CPU
public void makeCpu() {

}
}

这里我们就要用到@Module和@Provides了

@Module
public class CpuCompanyModule {
@Provides
public String providesColor() {
return "五颜六色";
}
}

Component类中也要记得关联起来:

@Component(modules = CpuCompanyModule.class)
public interface CpuCompanyComponent {
void inject(CellPhoneActivity activity);
}

让我们把程序跑起来,日志:

onCreate: 五颜六色

很棒,成功了。让我们再次一头扎进源码中。。

public final class DaggerCpuCompanyComponent implements CpuCompanyComponent {
private Provider<String> providesColorProvider;

private Provider<CpuCompany> cpuCompanyProvider;

private MembersInjector<CellPhoneActivity> cellPhoneActivityMembersInjector;

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

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

public static CpuCompanyComponent create() {
return builder().build();
}

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

this.providesColorProvider =
CpuCompanyModule_ProvidesColorFactory.create(builder.cpuCompanyModule);

this.cpuCompanyProvider = CpuCompany_Factory.create(providesColorProvider);

this.cellPhoneActivityMembersInjector =
CellPhoneActivity_MembersInjector.create(cpuCompanyProvider);
}

@Override
public void inject(CellPhoneActivity activity) {
cellPhoneActivityMembersInjector.injectMembers(activity);
}

public static final class Builder {
private CpuCompanyModule cpuCompanyModule;

private Builder() {}

public CpuCompanyComponent build() {
if (cpuCompanyModule == null) {
this.cpuCompanyModule = new CpuCompanyModule();
}
return new DaggerCpuCompanyComponent(this);
}

public Builder cpuCompanyModule(CpuCompanyModule cpuCompanyModule) {
this.cpuCompanyModule = Preconditions.checkNotNull(cpuCompanyModule);
return this;
}
}
}

比起我们第二次修改,又多了代码,不要慌,每次增加代码的量不多,我们细细看。

修改的地方:
1. 新增了Provider和Provider两个成员变量,其中Provider不就是我们在CpuCompanyModule中proveidesColor方法提供的数据吗,这里就说明添加了@Provides注解的方法会在这里生成一个Provider对象。
2. 还记得我们前面的修改中Provider是怎么来的吗?是通过CpuCompany_Factory.create()来的吧,而且它就是new CpuCompany_Factory()就返回了,但是现在不行了,为什么呀?因为他构造方法要传参数了啊。这里我们先要提供一个Provider才能创建这个Provider。这个Provider通过CpuCompanyModule_ProvidesColorFactory.create(builder.cpuCompanyModule)创建,很明显,这里也是一个工厂类,用来创建Provider,还记得我们的CpuCompanyModule吗,这里就需要用到他了,这个Module是在build()方法里面实例化的,然后和Builder绑一起了,现在要用了,就从Builder里面取出来就好了这里的CpuCompanyModule_ProvidesColorFactory就不细看了,感觉现在所有的Factory里面都是一个套路。

文章上述内容主要提到了几个注解:
- @Inject 使用此注解的属性或构造方法Dagger2会生成对应实例
- @Module 带有此注解的类,用来提供依赖,里面用@Provides修饰的以provide开头的方法用来提供参数
- @Component 用来将@Inject和@Module联系起来的桥梁


@Scope

这个注解限定了实例的使用范围,就像我们的Activity有生命周期从onCreate到onDestroy,我们就可以定义一个Scope限定它的使用范围是从Activity的onCreate到onDestroy。

Scope我们可以自己定义,dagger2也提供了一个自带的@Singleton注解,看这个名字应该就知道这个注解代表的是单例吧?让我们一起验证一下,首先修改一下我们的例子

CpuCompanyModule中提供一个providesCpuCompany来自己提供实例创建的方法,这里不提供这个方法dagger2就会在内部帮我们创建实例,但是我们提供之后,dagger2就会跑我们提供的方法

@Module
public class CpuCompanyModule {
@Provides
public String providesColor() {
return "五颜六色";
}

@Provides
public int providesSize() {
return 3;
}

@Provides
@Singleton
public CpuCompany providesCpuCompany(String color, int size) {
return new CpuCompany(color, size);
}
}

然后给providesCpuCompany加上@Singleton注解,再修改一下CpuCompanyComponent

@Singleton
@Component(modules = CpuCompanyModule.class)
public interface CpuCompanyComponent {
void inject(CellPhoneActivity activity);
}

再修改一下Activity

public class CellPhoneActivity extends AppCompatActivity {
private static final String TAG = "Dagger2Log";

@Inject
CpuCompany cpuCompany;

@Inject
CpuCompany cpuCompany1;

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

DaggerCpuCompanyComponent.create().inject(this);

Log.i(TAG, "onCreate: " + cpuCompany);
Log.i(TAG, "onCreate: " + cpuCompany1);
}
}

这里我们创建了2个CpuCompany实例,然后通过Log打印内存地址查看是否相同。日志:

onCreate: com.example.dagger2testmodule.test.CpuCompany@3a2fbbc9
onCreate: com.example.dagger2testmodule.test.CpuCompany@3a2fbbc9

内存地址是相同的,说明是同一个对象,来继续看源码。被@Singleton修饰的方法会生成一个DoubleCheck的类,正是这个类控制了单例的生成

public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
private static final Object UNINITIALIZED = new Object();

private volatile Provider<T> provider;
private volatile Object instance = UNINITIALIZED;

private DoubleCheck(Provider<T> provider) {
assert provider != null;
this.provider = provider;
}

@SuppressWarnings("unchecked") // cast only happens when result comes from the provider
@Override
public T get() {
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get();
/* Get the current instance and test to see if the call to provider.get() has resulted
* in a recursive call. If it returns the same instance, we'll allow it, but if the
* instances differ, throw. */

Object currentInstance = instance;
if (currentInstance != UNINITIALIZED && currentInstance != result) {
throw new IllegalStateException("Scoped provider was invoked recursively returning "
+ "different results: " + currentInstance + " & " + result);
}
instance = result;
/* Null out the reference to the provider. We are never going to need it again, so we
* can make it eligible for GC. */

provider = null;
}
}
}
return (T) result;
}

/** Returns a {@link Provider} that caches the value from the given delegate provider. */
public static <T> Provider<T> provider(Provider<T> delegate) {
checkNotNull(delegate);
if (delegate instanceof DoubleCheck) {
/* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped
* binding, we shouldn't cache the value again. */

return delegate;
}
return new DoubleCheck<T>(delegate);
}

/** Returns a {@link Lazy} that caches the value from the given provider. */
public static <T> Lazy<T> lazy(Provider<T> provider) {
if (provider instanceof Lazy) {
@SuppressWarnings("unchecked")
final Lazy<T> lazy = (Lazy<T>) provider;
// Avoids memoizing a value that is already memoized.
// NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L
// are different types using covariant return on get(). Right now this is used with
// DoubleCheck<T> exclusively, which is implemented such that P and L are always
// the same, so it will be fine for that case.
return lazy;
}
return new DoubleCheck<T>(checkNotNull(provider));
}
}

这里我们其实只需要看最重要的get方法就好了,因为provide方法所提供的数据最后都是通过这个get方法来返回的。这个get方法一开始就做了一个doubleCheck,就是我们写单例类是判断对象是否为空的方法,然后把通过provider的get方法把对象(就是我们的CpuCompany实例)赋值给result,再返回result,等到下次再这个方法时判断条件不会成立就直接返回原来的CpuCompany实例了。当然我们也可以自定义Scope来限定作用域,比如说可以创建一个PerActivity的Scope

@Scope
@Retention(RUNTIME)
public @interface PerActivity {
}

组件依赖

看了上面例子你有可能想问,假如在正式项目中使用,我要有一个SpUtils的单例不仅能在所有的Activity中使用,还要能在Application中使用,该怎么定义呢?接下来我们来学习一下组件依赖。Component之间是可以相互依赖的,但是有几个点要注意:
1. 依赖的组件之间的域不能相同,比如有一个组件是@Singleton,那么和他依赖的组件就不能使用这个域了
2. 父组件必须要提供子组件需要的元素,比如说我的SpUtils很明显要再Application中创建,但是又要在Activity中使用它,那么就必须在父组件中暴露给子组件它才能使用。

上代码,首先是我们的AppComponent,用@Singleton修饰的,作用域与Application相同:

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
void inject(MyApplication application);

SpUtils sqUtils();// 这里必须提供给子组件
}

然后是AppModule,这里给AppModule添加了一个构造方法把我们的Application传进去,通过provide方法提供出来,还有一个provideSqUtils用来提供SpUtils实例。这里都是用@Singleton修饰表示单例

@Module
public class AppModule {
public final Application application;

public AppModule(Application application) {
this.application = application;
}

@Provides
@Singleton
Application provideApp() {
return application;
}

@Provides
@Singleton
SpUtils provideSqUtils() {
return new SpUtils(application);
}
}

接下来是ActivityComponent,这里就用到了上面我们自定义的@PerActivity,同时还添加了组件依赖,依赖的是AppComponent

@PerActivity
@Component(modules = ActivityModule.class, dependencies = AppComponent.class)
public interface ActivityComponent {
void inject(MainActivity activity);
}

然后是ActivityModule和SpUtils

@Module
public class ActivityModule {}
public class SpUtils {
@Inject
public SpUtils(Application application) {}
}

自定义Application,这里需要把appComponent显示提供出去,因为在Activity中注册时需要用到:

public class MyApplication extends Application {
private AppComponent appComponent;

@Override
public void onCreate() {
super.onCreate();

appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}

public static MyApplication get(Context context) {
return (MyApplication) context.getApplicationContext();
}

public AppComponent getAppComponent() {
return appComponent;
}
}

再看Activity中是怎么使用的:

public class MainActivity extends AppCompatActivity {
private static final String TAG = "mMainActivity";
@Inject
SpUtils spUtils1;
@Inject
SpUtils spUtils2;

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

DaggerActivityComponent.builder()
.appComponent(MyApplication.get(this).getAppComponent())
.build()
.inject(this);

Log.i(TAG, "onCreate: " + spUtils1);
Log.i(TAG, "onCreate: " + spUtils2);
}
}

日志:

spUtils1=com.example.testmodule.SpUtils@cdc8c8e
spUtils2=com.example.testmodule.SpUtils@cdc8c8e

说明成功了。

有了这个组件依赖,加上前面讲的知识,对于dagger2 + mvp模式就能够自己消化了和封装了,祝你也能成功且理解dagger2的使用,它真的是一个很强大的框架。


总结

终于讲完了,如果,你觉得从头到位很懵逼,没关系,自己动手尝试,一个一个代码跟着敲,跑到源码里面自己去理逻辑,帖子里面所谓的总结总归都是从源码里面来的;如果你从头到位思路很清楚,那么恭喜你天赋很高。。。理解能力强,希望这篇帖子能给你带来一点帮助