从零开始学dagger2

来源:互联网 发布:那年夏天宁静的海 知乎 编辑:程序博客网 时间:2024/06/05 19:50

前言

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之前我们要把它引入项目。添加依赖:

// dagger2compile '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的

@Componentpublic 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的同学可以使用这个:

这里写图片描述

让我们来分析一下,首先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

@Componentpublic 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了

@Modulepublic 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就会跑我们提供的方法

@Modulepublic 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@3a2fbbc9onCreate: 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修饰表示单例

@Modulepublic 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

@Modulepublic 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@cdc8c8espUtils2=com.example.testmodule.SpUtils@cdc8c8e

说明成功了。

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


总结

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

1 0
原创粉丝点击