揭开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的详细使用与源码分析。

相关推荐
yangfeipancc23 分钟前
数据库-用户管理
android·数据库
字节流动1 小时前
Android Java 版本的 MSAA OpenGL ES 多重采样
android·java·opengles
xuanfengwuxiang1 小时前
安卓帧率获取
android·python·测试工具·adb·性能优化·pycharm
柯南二号5 小时前
Task ‘wrapper‘ not found in project ‘:example‘. 报错解决
android·gradle·hippy
我又来搬代码了5 小时前
【Android】项目升级时报错 android:style/Holo.Widget
android·gitee
洞见不一样的自己7 小时前
android 常用方法
android
暗碳7 小时前
华为麦芒5(安卓6)termux记录 使用ddns-go,alist
android·linux
seven27298 小时前
Android MQTT关于断开连接disconnect报错原因
android·mqtt·disconnect报错
Maplee11 小时前
Compose 转场动画之 Transition
android·前端
weixin_4825655313 小时前
Android IC读写器安卓小程序 3
android·小程序