什么是Dagger2
Dagger2是一个依赖注入的IOC框架,通俗的话解释就是能够帮助我们new对象的框架。Dagger2框架通过APT在编译时候生成代码实现帮助new对象功能,里面主要涉及Component
、Subcomponent
、Module
、Provides
、Scope
、Qualifier
。
Dagger2是帮助我们注入对象的框架,那使用该框架有何优点呢?
- 模块间解耦
- 提高代码的可维护性
- 代码的可测试性
Dagger2使用
Inject
@Inject
该注解可以使用在类的构造方法或类的成员变量上。
- 在构造方法使用该注解,是告诉Dagger2需要注入该类的对象时,调用当前构造方法创建对象。如下示例在无参构造方法上使用该注解,在需要注入该类对象时,Dagger2就会调用该构造方法创建对象实例。
java
public class User {
@Inject
public User() {
}
}
- 在成员变量使用该注解,表示该对象需要被注入。如下示例代码就是告诉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
注解,表示该类是用来提供依赖的。该类有一个返回值为Context
的provideContext()
方法,并且使用@Singleton
和Provides
,表示该类能提供一个单例的Context
对象。
如果有一些第三方库的对象,一些接口的实现类或抽象方法的子类需要注入都可以在Module中提供依赖。
Provides
我们在Module类中自己定义的方法上使用该注解,是告诉Dagger2调用该方法可以生成依赖对象。上面的类中provideContext
这个方法就是告诉Dagger2我可以提供Context
对象。
Component
@Component
被用在Component类上,他是Inject
和Module
的桥梁,当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。
这里需要注意两点:
ApplicationComponent
暴露的依赖,ActivityComponent
才能拿到。ActivityComponent
可以有多个dependencies
依赖,并且其它的Component
也可以依赖ActivityComponent
。- 父
Component
与子Component
的Scope
不能相同。
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
指明ActivityComponent
为ApplicationComponent
的子Component。注意这里的方法名没有要求,可以任意。
这里需要注意两点:
- 子Componet需要使用
@Subcomponent
注解,指明为子Component,且子Component不能再使用dependencies
属性。 - 父
Component
与子Component
的Scope
不能相同。
下面我们看看怎么使用?代码如下:
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类型代码的有两种形式:
- 在构造方法使用
@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_Factory
,User_Factory
实现了Factory
接口,Factory
继承了Provider
。可以看出User_Factory
其实是一个Provider
,其get()
方法调用了newInstance()
方法,newInstance()
方法调用了new User()
创建出一个User
对象。可以得出结论,该Provider
主要是给我们提供User
对象依赖。
- 使用
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
,其有ApplicationComponentImpl
和ApplicationComponentImpl
两个内部类,代码如下:
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中通过DaggerApplicationComponent
的build()
方法创建的也是唯一的,这样就把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)
方法,会调用injectUser
和injectUser2
方法,我们看一下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,需要建立ApplicationComponent
、ApplicatonModule
、ActivityComponent
、ActivityModule
、 ApplicationScope
、ActivityScope
等内容,甚至还需要更多的内容需要去创建。这无疑增加了我们使用的复杂度和难度,我们能否简化呢?答案是肯定的,那就是Hilt,(Dagger)匕首+(Hilt)刀柄=好用。我们下篇文章再去分析Hilt
的详细使用与源码分析。