揭开Dagger2的神秘面纱

什么是Dagger2

Dagger2是一个依赖注入的IOC框架,通俗的话解释就是能够帮助我们new对象的框架。Dagger2框架通过APT在编译时候生成代码实现帮助new对象功能,里面主要涉及ComponentSubcomponentModuleProvidesScopeQualifier

Dagger2是帮助我们注入对象的框架,那使用该框架有何优点呢?

  1. 模块间解耦
  2. 提高代码的可维护性
  3. 代码的可测试性

Dagger2使用

Inject

@Inject该注解可以使用在类的构造方法或类的成员变量上。

  1. 在构造方法使用该注解,是告诉Dagger2需要注入该类的对象时,调用当前构造方法创建对象。如下示例在无参构造方法上使用该注解,在需要注入该类对象时,Dagger2就会调用该构造方法创建对象实例。
java 复制代码
public class User {
    @Inject
    public User() {
    }
}
  1. 在成员变量使用该注解,表示该对象需要被注入。如下示例代码就是告诉Dagger2这里的User对象需要你帮我注入进来。这里只是告诉Dagger2需要被注入,但还没有执行注入动作。
java 复制代码
public class TestActivity extends AppCompatActivity {

    @Inject
    User user;
}

我们介绍了Inject可以用在类的构造方法上,用来提供依赖。但如果这个需要注入的类是不是我们自己创建的而是第三方库的呢?我们如何提供依赖?这里我们就引入了Module

Module

@Module用来修饰类,表示该类可以提供依赖,告诉Dagger2所需要的依赖可以来这里找。

java 复制代码
@Module
public class ApplicationModule {
    private final Context context;

    ApplicationModule(Context context) {
        this.context = context;
    }

    @Singleton
    @Provides
    public Context provideContext(){
        return context;
    }
}

我们创建了一个ApplicationModule类,该类使用@Module注解,表示该类是用来提供依赖的。该类有一个返回值为ContextprovideContext()方法,并且使用@SingletonProvides,表示该类能提供一个单例的Context对象。

如果有一些第三方库的对象,一些接口的实现类或抽象方法的子类需要注入都可以在Module中提供依赖。

Provides

我们在Module类中自己定义的方法上使用该注解,是告诉Dagger2调用该方法可以生成依赖对象。上面的类中provideContext这个方法就是告诉Dagger2我可以提供Context对象。

Component

@Component被用在Component类上,他是InjectModule的桥梁,当Component装载上了Module,该Component就具备了注入该Module所提供依赖的能力。如下示例中ApplicationComponent装载了ApplicationModule,那该ApplicationComponent就具备了注入ApplicationModule所提供依赖的能力,也就是有了注入单例Context对象的能力。

java 复制代码
@Singleton
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {

}

Component是一个注入器,主要是帮助我们注入对象的。Module是一个依赖提供器,主要是用来提供依赖的。那我们接下来看一下怎么才能完成真正的注入。

第一步我们需要给ApplicationComponent装载ApplicationModule对象。这里我们创建了一个ApplicationModule对象,并且传入了一个全局的Application Context。我们通过建造者模式把我们创建的ApplicationModule对象传递给DaggerApplicationComponent。关于DaggerApplicationComponent就是APT帮我们生成的AoolicationComponent对应的代码,我们后面在做详细分析。示例代码如下:

java 复制代码
public class MyApplication extends Application {

    private ApplicationComponent applicationComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        ApplicationModule module = new ApplicationModule(this);
        applicationComponent = DaggerApplicationComponent.builder().applicationModule(module).build();
    }

    public ApplicationComponent getApplicationComponent(){
        return applicationComponent;
    }
}

第二步要在component类中写明我们要给哪个类提供注入。下面的示例代码中inject方法表明,我们要给TestActivity类提供注入。这里的inject方法名可以可以随意定义,没有固定要求。

java 复制代码
@Singleton
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
    void inject(TestActivity activity);
}

第三步注入,下面的代码我们就完成了User对象的注入,这里的User成员变量必须使用public修饰,至于为什么是public我们后面讲源码时再作分析。

java 复制代码
public class TestActivity extends AppCompatActivity {

    @Inject
    User user;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
        applicationComponent.inject(this);
    }
}

Qualifier

@Qualifier注解是用在Annotation上的用来创建别名。那创建别名有什么用呢?别名主要是用来处理同一个类有多个对象,表明具体需要注入那个对象。如果不自己定义Qualifier可以看Dagger2给我们提供好的一个Annoation @Name

java 复制代码
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface UserQualifier {
}

那我们定义了这个UserQualifier如何使用呢,我们修改一下我们的ApplicationModule类,添加一个可以提供带有@UserQualifier注解的方法,该方法返回一个User对象。在TestActivity类中的成员变量中添加一个带有@UserQualifier注解的成员变量user2.这样user2对象就会使用有同样注解的provideUser()方法提供注入对象,而没有@UserQualifier注解的成员变量user,仍然使用User构造方法提供注解。代码如下:

java 复制代码
@Module
public class ApplicationModule {
    ...

    @UserQualifier
    @Provides
    public User provideUser(){
        return new User();
    }
}

public class TestActivity extends AppCompatActivity {

    @Inject
    User user;

    @UserQualifier
    @Inject
    User user2;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
        applicationComponent.inject(this);
    }
}

Scope

@Scope注解是用在Annotation上,主要是用来提供注入对象的作用域(生命周期),如我们上面用到的@Singleton就是一个scope,我们可以自己定义一些scope。如下代码我们定义了一个Application级别的Scope和一个Activity级别的scope。

java 复制代码
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

上面我们定义了Scope,那怎么使用呢?我们修改一下我们定义的ApplicationModule,在provideContext()方法修改成使用我们自己定义的scope ApplicationScope。再定义一个ActivityModule用来提供Activity级别Context的Module,并把ApplicationScope装载到ActivityComponent上。在给ApplicationPresenter提供application级别的Context和Activity级别的Context就用到了这两个注解。具体代码如下:

java 复制代码
@Module
public class ApplicationModule {
    private final Context context;

    public ApplicationModule(Context context) {
        this.context = context;
    }

    @ApplicationScope
    @Provides
    public Context provideContext(){
        return context;
    }

     ...
}


@Module
public class ActivityModule {

    private final Context context;

    public ActivityModule(Context context) {
        this.context = context;
    }

    @Provides
    @ActivityScope
    public Context provideContext(){
        return context;
    }
}


@Component(modules = {ActivityModule.class})
public interface ActivityComponent {
}



public class ApplicationPresenter {

    private final Context appContext;
    private final Context activityContext;

    public ApplicationPresenter(@ApplicationScope Context appContext,@ActivityScope Context activityContext) {
        this.appContext = appContext;
        this.activityContext = activityContext;
    }
}

Component的dependencies属性

Component的属性可以使用dependencies拿到其它Component暴露出来的依赖。示例代码如下:

java 复制代码
@ApplicationScope
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {

    /**
     * 暴露Context依赖给其它的Component使用。
     */
    Context context();
}


@ActivityScope
@Component(dependencies = {ApplicationComponent.class}, modules = {ActivityModule.class})
public interface ActivityComponent {
    void inject(TestActivity activity);
}

这里我们的ApplicationComponent暴露的Context依赖,ActivityComponent依赖了ApplicationComponent,这样ActivityComponent也同样具有了Application级别的Context。

这里需要注意两点:

  1. ApplicationComponent暴露的依赖,ActivityComponent才能拿到。ActivityComponent可以有多个dependencies依赖,并且其它的Component也可以依赖ActivityComponent
  2. Component与子ComponentScope不能相同。

Subcomponent

@Subcomponent从属方式,我们上面的实现方式是选择Component的dependencies属性,此时需要在被依赖的Component中有显示的声明。这里介绍另一种方法,Subcomponent(包含方式)。相比于依赖方式,包含方式不需要在父Component中显示的提供方法,就可以拿到想要的东西。示例代码:

java 复制代码
@ApplicationScope
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {

    ActivityComponent plus(ActivityModule module);
}


@ActivityScope
@Subcomponent(modules = {ActivityModule.class})
public interface ActivityComponent {
    
    void inject(TestActivity activity);
}

ApplicationComponent中不需要暴露依赖,但需要指明子Component,这里使用方法plus指明ActivityComponentApplicationComponent的子Component。注意这里的方法名没有要求,可以任意。

这里需要注意两点:

  1. 子Componet需要使用@Subcomponent注解,指明为子Component,且子Component不能再使用dependencies属性。
  2. Component与子ComponentScope不能相同。

下面我们看看怎么使用?代码如下:

java 复制代码
public class TestActivity extends AppCompatActivity {

    @Inject
    User user;

    @UserQualifier
    @Inject
    User user2;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
        applicationComponent.plus(new ActivityModule(this)).inject(this);
    }
}

源码解析

上面我们知道了Dagger2能帮助我们完成依赖注入,并且知道了怎么去使用它,下面我们去了解一下为什么Dagger2能帮助我们完成依赖注入。

我们观察Dagger2通过APT帮我们生成的代码,会发现有3种代码:

  • DaggerxxxComponent
  • xxx_Factory
  • xxx_MembersInjector

XXX_Factory

能产生Factory类型代码的有两种形式:

  1. 在构造方法使用@Inject注解来提供依赖的类生成,如下所示:
java 复制代码
public final class User_Factory implements Factory<User> {
  @Override
  public User get() {
    return newInstance();
  }

  public static User_Factory create() {
    return InstanceHolder.INSTANCE;
  }

  public static User newInstance() {
    return new User();
  }

  private static final class InstanceHolder {
    private static final User_Factory INSTANCE = new User_Factory();
  }
}

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

我们创建的User类,生成了User_FactoryUser_Factory实现了Factory接口,Factory继承了Provider。可以看出User_Factory其实是一个Provider,其get()方法调用了newInstance()方法,newInstance()方法调用了new User()创建出一个User对象。可以得出结论,该Provider主要是给我们提供User对象依赖。

  1. 使用Module提供的依赖,如我们的ApplicationModule提供的User依赖也会生成Factory,代码如下:
java 复制代码
public final class ApplicationModule_ProvideUserFactory implements Factory<User> {
  private final ApplicationModule module;

  public ApplicationModule_ProvideUserFactory(ApplicationModule module) {
    this.module = module;
  }

  @Override
  public User get() {
    return provideUser(module);
  }

  public static ApplicationModule_ProvideUserFactory create(ApplicationModule module) {
    return new ApplicationModule_ProvideUserFactory(module);
  }

  public static User provideUser(ApplicationModule instance) {
    return Preconditions.checkNotNullFromProvides(instance.provideUser());
  }
}

get()方法调用的我们在ApplicationModule类中定义的provideUser()方法生成User对象。

DaggerxxxComponent

我们创建的ApplicationComponent生成了DaggerApplicationComponent,其有ApplicationComponentImplApplicationComponentImpl两个内部类,代码如下:

java 复制代码
public final class DaggerApplicationComponent {
  private DaggerApplicationComponent() {
  }

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

  public static final class Builder {
    private ApplicationModule applicationModule;

    private Builder() {
    }

    public Builder applicationModule(ApplicationModule applicationModule) {
      this.applicationModule = Preconditions.checkNotNull(applicationModule);
      return this;
    }

    public ApplicationComponent build() {
      return new ApplicationComponentImpl(applicationModule);
    }
  }

  private static final class ActivityComponentImpl implements ActivityComponent {
    ...
  }

  private static final class ApplicationComponentImpl implements ApplicationComponent {
    ...
  }
}

ApplicationComponentImpl实现了我们定义的ApplicationComponent。我们先看一下该类做了什么?代码如下:

java 复制代码
private static final class ApplicationComponentImpl implements ApplicationComponent {
  private final ApplicationComponentImpl applicationComponentImpl = this;

  private Provider<User> provideUserProvider;

  private ApplicationComponentImpl(ApplicationModule applicationModuleParam) {

    initialize(applicationModuleParam);

  }

  @SuppressWarnings("unchecked")
  private void initialize(final ApplicationModule applicationModuleParam) {
    this.provideUserProvider = DoubleCheck.provider(ApplicationModule_ProvideUserFactory.create(applicationModuleParam));
  }

  @Override
  public ActivityComponent plus(ActivityModule module) {
    Preconditions.checkNotNull(module);
    return new ActivityComponentImpl(applicationComponentImpl, module);
  }
}

该类在构造方法中调用initialize方法初始化了一个Provider<User>成员变量,该成员变量用来提供User对象依赖。initialize中又调用了DoubleCheck.provider方法,其参数是一个Factory,上面我们已经说过了,这里不再详细说明。我们看一下DoubleCheck做了什么?代码如下:

java 复制代码
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();
          instance = reentrantCheck(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;
  }

  public static <P extends dagger.internal.Provider<T>, T> dagger.internal.Provider<T> provider(
      P 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);
  }

}

我们可以看出其实DoubleCheck也是一个Provider,我们调用DoubleCheck.provider方法传入一个User_Factory并且创建了一个DoubleCheck对象。既然DoubleCheck也是一个Provider,我们看一下它的get()方法,其get()方法使用双重检查锁返回了User对象。

ApplicationModule_ProvideUserFactory也是一个能提供User对象的Provider,DoubleCheck是一个提供User对象的Provider,那把ApplicationModule_ProvideUserFactory包装成一个DoubleCheck对象有什么用呢?

其作用是同一个DoubleCheck对象调用其get()方法获得的User对象是唯一的,而提供User对象的DoubleCheck对象在ApplicationComponentImpl类中也是唯一的,而ApplicationComponentImpl是在我们自己定义的Application中通过DaggerApplicationComponentbuild()方法创建的也是唯一的,这样就把User的作用域(生命周期)与ApplicationComponent保持一致,这也是Dagger2作用域的一种体现。

xxx_MembersInjector

xxx_MembersInjector是真正注入的地方,我们要给TestActivity提供注入,Dagger2就生成了TestActivity_Membersinjector,代码如下:

java 复制代码
public final class TestActivity_MembersInjector implements MembersInjector<TestActivity> {
  private final Provider<User> userProvider;

  private final Provider<User> user2Provider;

  public TestActivity_MembersInjector(Provider<User> userProvider, Provider<User> user2Provider) {
    this.userProvider = userProvider;
    this.user2Provider = user2Provider;
  }

  public static MembersInjector<TestActivity> create(Provider<User> userProvider,
      Provider<User> user2Provider) {
    return new TestActivity_MembersInjector(userProvider, user2Provider);
  }

  @Override
  public void injectMembers(TestActivity instance) {
    injectUser(instance, userProvider.get());
    injectUser2(instance, user2Provider.get());
  }

  @InjectedFieldSignature("com.example.lz.TestActivity.user")
  public static void injectUser(TestActivity instance, User user) {
    instance.user = user;
  }

  @InjectedFieldSignature("com.example.lz.TestActivity.user2")
  @UserQualifier
  public static void injectUser2(TestActivity instance, User user2) {
    instance.user2 = user2;
  }
}

该类中的injectMembers(TestActivity instance)方法,会调用injectUserinjectUser2方法,我们看一下injectUser方法,会给TestActivity.user赋值,这也是我们刚开始说到的需要被注入的对象必须定义成publish原因。

injectMembers(TestActivity instance)方法在ActivityComponentImpl中被inject方法调用。代码如下:

java 复制代码
private static final class ActivityComponentImpl implements ActivityComponent {
  private final ApplicationComponentImpl applicationComponentImpl;

  private final ActivityComponentImpl activityComponentImpl = this;

  private ActivityComponentImpl(ApplicationComponentImpl applicationComponentImpl,
      ActivityModule activityModuleParam) {
    this.applicationComponentImpl = applicationComponentImpl;
  }

  @Override
  public void inject(TestActivity activity) {
    injectTestActivity(activity);
  }

  private TestActivity injectTestActivity(TestActivity instance) {
    TestActivity_MembersInjector.injectUser(instance, new User());
    TestActivity_MembersInjector.injectUser2(instance, applicationComponentImpl.provideUserProvider.get());
    return instance;
  }
}

从上面的使用到源码的分析,我们发现使用dagger2,需要建立ApplicationComponentApplicatonModuleActivityComponentActivityModuleApplicationScopeActivityScope等内容,甚至还需要更多的内容需要去创建。这无疑增加了我们使用的复杂度和难度,我们能否简化呢?答案是肯定的,那就是Hilt,(Dagger)匕首+(Hilt)刀柄=好用。我们下篇文章再去分析Hilt的详细使用与源码分析。

相关推荐
Kapaseker7 分钟前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 小时前
Andorid Google 登录接入文档
android
黄林晴2 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab15 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿18 小时前
Android MediaPlayer 笔记
android
Jony_18 小时前
Android 启动优化方案
android
阿巴斯甜18 小时前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇18 小时前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android
_小马快跑_1 天前
Kotlin | 从SparseArray、ArrayMap的set操作符看类型检查的不同
android