出来混迟早要还的,技术债Dagger2:Android篇(下)进一步理解Dagger

1,415 阅读5分钟

前言

警告!这不是一个干货的文章!

个人认为,学技术不宜太浮躁。对于一项技术一味地追求干货其实并不一定有用,货太干容易噎着,哈哈~不如循序渐进慢下来,一点点去体会技术的前因后果。(这个系列适合于:了解但没有在项目里大规模应用Dagger2的读者)

出来混迟早要还的,技术债Dagger2:基础篇

出来混迟早要还的,技术债Dagger2:Android篇(上)

出来混迟早要还的,技术债Dagger2:Android篇(中)@Scope、@Singleton

本以为阅读一些文档,写一些Demo就能驾驭工作中的项目...我错了,我再也不会有这么愚蠢的想法了... 这么多依赖关系,谁扛得住啊!所以还是一点点来吧。

正文

前俩篇文章过后,我猜大家对下面的代码已经很熟悉了:

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
  void inject(MainActivity mainActivity);
  SharedPreferences getSharedPrefs();
}

@Module
public class AppModule {
    Application application;
 
    public AppModule(Application application) {
       this.application = application;
    }
 
    @Provides
    Application providesApplication() {
       return application;
    }
    
    @Provides
    @Singleton
    public SharedPreferences providePreferences() {
        return application.getSharedPreferences(DATA_STORE,Context.MODE_PRIVATE);
    }
}


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

很基本,很简单的Dagger应用。不过大家有没有感觉到这个appModule(new AppModule(this))特别烦?安利我用的时候,说依赖注入,看不见new。我哼哧哼哧写这么多,这不还是new出来的?

那么问题来了,是不是可以不需要appModule(new AppModule(this))呢?当然可以。用过Dagger-Android的朋友,肯定很清楚,的确看不到任何new。那么这篇文章,咱们就来看看如何彻底不用new。

当然这也是Dagger-Android的原理

@Component.Builder

做这一切的前提是这个注解。这个注解是干啥的呢?说白了,给Component提供Module的依赖。

咱们先来想一个问题,下面代码存在的意义:

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

Dagger为我们所需的依赖生成实例,那么必然需要构建整个依赖关系网。Dagger就算是再神奇,也不可能凭空把整个我们所需要依赖关系构建出来,所以需要我们适时的“提供和引导”。那么new AppModule(this)就是给Dagger进行提供,因为从我们上述的代码中,Dagger是不知道该怎么去实例化这个AppModule,因此需要我们对其进行提供。

那么话又说回来,我们在上述的代码中,告诉它如何去实例化AppModule,不就可以避免我们手动去new AppModule(this)了么?

没错,@Component.Builder就是做这个的。

累死了,绕了一圈不知道大家理没理解@Component.Builder存在的含义了。

AppModule(Application application)

对于我们的AppModule来说,实例化它的关键是如何提供一个Application

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

对于Dagger也是如此,它不能实例化AppModule的原因是它不知道或者说没办法去获取一个Application实例。因此,对于Dagger来说,我们应该给它提供Application的实例,而非AppModule

开始改造

改造需要一步步来:第一步,我们使用@Component.Builder去改造AppComponent

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
   void inject(MainActivity mainActivity);
   SharedPreferences getSharedPrefs();
   // 改造内容
   @Component.Builder
   interface Builder {
        AppComponent build();
        // 此时还未改造这行代码
        Builder appModule(AppModule appModule);
    }
}

现在我们告诉了Dagger,你要以@Component.Builder注解的接口那样去实例化我们所需的AppComponent

是不是发现这里添加的方法和DaggerAppComponent生成的代码很像?没错Dagger默认实例化AppComponent就是以这种代码进行的。

不过,对于我们这个AppModule来说,我们不需要关系它是怎么初始化(因为我们只需要它所提供给我们的依赖)。对于Dagger来说也是如此: Dagger想要为我们提供被@Provides标注的依赖,只需要拥有Application实例即可。因为只要拥有Application实例Dagger就有办法实例化AppModule,直接new即可。

所以这里我们需要一种方式来告诉Dagger:我要提供给你,在@Module中需要的内容。对于咱们的这个demo来说,咱们需要用一种方式把Dagger所需要的Application给他。

而做到这一点的就是@BindsInstance

@BindsInstance

按照官网的介绍,此注解用于标识Component Builder/SubComponent Builder中的某个方法,该方法允许将实例绑定到Component中。

所以对于我们的AppModule来说,它只需要提供@Provides的内容就可以了!,它所需要的,Dagger会按照我们的“指示”,注入进来。也就是这个样子:

@Module
public class AppModule {
     @Provides
     @Singleton
     // 外部传入Application实例(Dagger会按照我们的“指示”,注入进来)
     public SharedPreferences providePreferences(Application application) {
         return application.getSharedPreferences("store", Context.MODE_PRIVATE);
     }
 }

而我们的AppConponent这样就可以了:

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
   void inject(MainActivity mainActivity);
   SharedPreferences getSharedPrefs();
   @Component.Builder
   interface Builder {
        AppComponent build();
        @BindsInstance 
        // 此时的application就会被,注入到AppModule的providePreferences方法中
        Builder application(Application application);      
  }
}

build过后,我们初始化DaggerAppComponent,只需如此写:

DaggerAppComponent appComponent = DaggerAppComponent.builder()
   .application(this)
   .build();

那么Dagger是如何为我们生成DaggerAppComponent的呢?

public final class DaggerAppComponent implements AppComponent {
  private Provider<Application> applicationProvider;

  private Provider<SharedPreferences> providePreferencesProvider;

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

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

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.applicationProvider = InstanceFactory.create(builder.application);
    this.providePreferencesProvider =
        DoubleCheck.provider(
            AppModule_ProvidePreferencesFactory.create(builder.appModule, applicationProvider));
  }

  @Override
  public void inject(MainActivity mainActivity) {}

  @Override
  public SharedPreferences getSharedPrefs() {
    return providePreferencesProvider.get();
  }

  private static final class Builder implements AppComponent.Builder {
    private AppModule appModule;

    private Application application;

    @Override
    public AppComponent build() {
      if (appModule == null) {
        this.appModule = new AppModule();
      }
      if (application == null) {
        throw new IllegalStateException(Application.class.getCanonicalName() + " must be set");
      }
      return new DaggerAppComponent(this);
    }

    @Override
    public Builder application(Application application) {
      this.application = Preconditions.checkNotNull(application);
      return this;
    }
  }
}

对于AppModule来说,只是简单的new出来,当我们需要@Provides时,只是将所需的application传进去。而我们的application以成员变量的身份呆在了DaggerAppComponent“体内”。

尾声

这篇内容,其实并不是为了去讲@Component.Builder@BindsInstance。而是尽可能的通过它们,来加深大家去Component和Module的理解。去感受Dagger的实现,更好的去理解依赖注入。对与我们而言,需要用其形,学其神。

这样我们才不会受制于框架,而是驾驭框架。

我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,以及我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~

个人公众号:咸鱼正翻身