热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

依赖注入框架Dagger2

什么的依赖注入在软件工程中,依赖注入是实现控制反转的方式之一。百度百科中对于控制反转的解释如下:控制反转(InversionofControl,英文缩写为IoC)把创建对象的权利交给框架

什么的依赖注入

在软件工程中,依赖注入是实现控制反转的方式之一。百度百科中对于控制反转的解释如下:控制反转(Inversion of Control,英文缩写为IoC)把创建对象的权利交给框架。我们可以举例来说明一下,在开发中我们经常有类组合的情况,例如Student类中引用了School类,用于描述学生的学校信息,那么这就是典型的组合,再具体点的例子:假设我们的网络请求类RetrofitManager中需要有一个OkHttpClick对象。

public class RetrofitManager {

OkHttpClient client;

public RetrofitManager() {
client = new OkHttpClient.Builder()
.connectTimeout(1000L, TimeUnit.SECONDS).build();
}
}

我们可以看到上面的类中直接在构造器中将client对象进行初始化,那么此时就产生了较高的耦合现象。这种在一个类中直接创建另一个类的对象的代码,和硬编码(hard-coded strings)以及硬编码的数字(magic numbers)一样,是一种导致耦合的坏味道,我们可以把这种坏味道称为硬初始化(hard init),违背了单一职责原则与开闭原则,同时也会严重影响到单元测试与代码的可读性。

试想一下,领导现在要求你修改网络请求的相关参数或者让你添加固定的请求头又或者要添加相关的网络拦截器等等。这时候我们就必须对RetrofitManager中的client初始化进行修改。当然,这个例子还好,可能就只有这一个地方对吧。但是如果是其他情况,修改的地方有可能是成千上万。因此,我们需要使用依赖注入来解决这个问题。

实现依赖注入的三种方式

这里会介绍几种实现依赖注入的简单方式,而复杂的依赖注入框架当然思想都是一样的。就是避免在调用者中直接实例化(new)被调用者对象,而是通过其他方式进行引入。

1.构造器注入

直接通过构造器参数的形式将被调用者的对象传入

public class RetrofitManager {

OkHttpClient client;

public RetrofitManager(OkHttpClient client) {
this.client = client;
}
}

2.setter注入

通过提供setter方式让外界将对象注入

public class RetrofitManager {

OkHttpClient client;

public void setClient(OkHttpClient client) {
this.client = client;
}
}

2.接口注入

定义注入接口,让调用者都实现改接口

public interface Inject {

void inject(OkHttpClient client);
}

public class RetrofitManager implements Inject{

OkHttpClient client;

@Override
public void inject(OkHttpClient client) {
this.client = client;
}
}

OK,简单了解了依赖注入与耦合问题之后,我们再来看Dagger2

Dagger2是什么

Dagger2

Dagger2一个Android和java快速依赖注入框架,Dagger2是Dagger的升级版,目前由Google进行维护。Dagger2中杜绝了反射的使用,而是采用编译时注解的方式在编译时候实现代码的自动生成。

注解的分类

Annotation(注解)是Java提供的一种对元程序中元素关联信息和元数据(metadata)的途径和方法。Annatation(注解)是一个接口,程序可以通过反射来获取指定程序中元素的Annotation对象,然后通过该Annotation对象来获取注解中的元数据信息。

通常自定义注解时我们会通过@Retention元注解(元注解是专门用来定义注解的注解)来标记该注解的保留周期:.SOURCE:在源文件中有效(即源文件保留)、CLASS:在class文件中有效(即class保留)、RUNTIME:在运行时有效(即运行时保留)。SOURCE,在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。CLASS,在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。RUNTIME,始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。

扯一些题外话,早期的一些控件注入框架普遍采用RUNTIME(运行时)形式的注解通过反射机制在运行时动态完成控件的注入工作。甚至是一些依赖注入框架也采用这种实现形式。大家都知道,反射对性能是有影响的,所有现在流行的框架一般都是采用CLASS(编译时)形式的注解配合javapoet或者javassist等字节码操作工具实现编译时修改代码或者生成类文件的方式去实现相关功能。

Dagger2的引入

有两种方式可以完成Dagger2的依赖,第一种方式是使用android-apt,第二种方式是使用annotationProcessor。前者是开发者开发的apt框架,后者是谷歌Android Gradle插件支持的apt方式。Dagger2最新文档上可以看到Android端推荐使用的是第二种方式。具体细节可查看博客:http://blog.csdn.net/xx326664162/article/details/68490059

我们采用第二种方式对Dagger2进行依赖,直接在项目gradle中添加以下两行

annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
compile 'com.squareup.okhttp3:okhttp:3.10.0'

Inject + Component

1.定义类ClassA,为它的无参构造器加上注解@Inject,表明该类支持快速依赖注入

public class ClassA {

@Inject
public ClassA() {
}
}

2.定义接口MainActivityComponent,为该接口添加注解@Component,定义抽象方法,其他返回值为void,参数为需要注入对象的类(必须的需要注入对象的真实所在类,比如:MainActivity,而不能是父类Activity),方法名无限制,但是一般规范为inject或者injectXXX。Component可以理解为classA对象(被调用者)与MainActivity(调用者)之间的桥梁。

@Component
public interface MainActivityComponent {

void inject(MainActivity activity);
}

3.对项目重新Build,框架会根据MainActivityComponent自动生成DaggeMainActivityComponent类(命名规则为Dagger+Component名称),这也验证了在上面说到的自动生成代码的这茬。在MainActivity中声明ClassA成员对象,并用@Inject注解标记,然后使用DaggeMainActivityComponent完成对象注入。

public class MainActivity extends AppCompatActivity {

@Inject
ClassA classA; // 不能是private,后面会提到

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerMainActivityComponent.builder().build().inject(this);
System.out.println("MainActivity.onCreate" + classA.toString());
}
}

com.exp.dagger2 I/System.out: MainActivity.onCreatecom.exp.dagger2.ClassA@2711f49

这个例子很简单,那么我们现在来看一下Dagger2生成的类在哪?类文件的内容是什么?是怎么完成对象注入的。我们将目录结构切换到project模式下进入到 \app\build\generated\source\apt\debug 下可以发现自动生成的三个类:ClassA_Factory、DaggerMainActivityComponent、MainActivity_MembersInjector。

ClassA_Factory是对应ClassA类中构造器上的@Inject注解,只要类的构造器标记@Inject,那么Dagger2框架会自动为其生成对应的工厂类,用于提供实例化对象,那肯定是通过某种途径提供给需要的地方(暂时我们只能认为是MainActivity)

// Generated by dagger.internal.codegen.ComponentProcessor (https://google.github.io/dagger).
package com.exp.dagger2;

import dagger.internal.Factory;

public final class ClassA_Factory implements Factory<ClassA> {
private static final ClassA_Factory INSTANCE = new ClassA_Factory();

@Override
public ClassA get() {
return new ClassA();
}

public static Factory create() {
return INSTANCE;
}

DaggerMainActivityComponent很显然对应的是MainActivityComponent,DaggerMainActivityComponent是由Dagger2生成的一个MainActivityComponent接口的实现类。

public final class DaggerMainActivityComponent implements MainActivityComponent {
private MembersInjector mainActivityMembersInjector;

private DaggerMainActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}

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

public static MainActivityComponent create() {
return new Builder().build();
}

@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {

this.mainActivityMembersInjector = MainActivity_MembersInjector.create(ClassA_Factory.create());
}

@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}

public static final class Builder {
private Builder() {}

public MainActivityComponent build() {
return new DaggerMainActivityComponent(this);
}
}
}

从代码中可以看出DaggerMainActivityComponent构造器的唯一调用是在Builder中,通过builder设计模式来完成Component的实例化。Builder类中的build生成了Component对象,而我们是通过inject方法完成对象注入,而我们可以看到mainActivityMembersInjector对象中实际调用的是MainActivity_MembersInjector的injectMembers方法

public final class MainActivity_MembersInjector implements MembersInjector {
private final Provider classAProvider;

public MainActivity_MembersInjector(Provider classAProvider) {
assert classAProvider != null;
this.classAProvider = classAProvider;
}

public static MembersInjector create(Provider classAProvider) {
return new MainActivity_MembersInjector(classAProvider);
}

@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.classA = classAProvider.get();
}

public static void injectClassA(MainActivity instance, Provider classAProvider) {
instance.classA = classAProvider.get();
}
}

在实例化MainActivity_MembersInjector传入了ClassA_Factory对象,在injectMembers方法中直接通过instance.classA进行对象赋值(因此注入目标成员的修饰符不能是private,否则此处无法直接引用),而classAProvider.get()就是ClassA_Factory中返回的new ClassA()。

整个简单的流程就分析完了,现在再来总结下。首先我们的ClassA类构造器添加了@Inject,Dagger2为其生成工厂类。接着我们定义了Component接口,声明抽象注入方法,Dagger2为其生成了对应的实现了并在方法实现中调用MainActivity_MembersInjector的inject方法,而MainActivity_MembersInjector中封装了通过ClassA_Factory实现classA对象的注入。

简单流程浅析

Inject + Module + Component

上面的ClassA是我们自己定义的类文件,现在有个问题。假设我们需要为某个类注入一个OkHttpClient对象。好的,那么咱们去OkHttpClient类中找个地方加@Inject注解,呃呃呃…不对,通过外部gradle命令依赖的它很高冷,咱们不能修改它。这个时候我们需要用到Module,就是说这个时候可以用Module去代替上面的@Inject(这么说不恰当)

怎么理解Module呢,我们可以想一下上个例子,构造器是干什么的?完成对象实例化的,提供对象的。没错,Module我们可以这么理解,它是用来向Component提供依赖对象的。实际操作一下~

1.创建一个类MainModule,并用注解@Module标记。类中定义提供OkHttpClient对象的方法并用@Provides标记

@Module
public class MainModule {

@Provides
public OkHttpClient provideOkHttpClient(){
return new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).build();
}
}

2.修改MainActivityComponent,为其使用@Component注解添加module引用。表明这个Component可能需要用到MainModule中提供的对象

@Component(modules = MainModule.class)
public interface MainActivityComponent {

void inject(MainActivity activity);
}

3.MainActivity增加OkHttpClient对象,并标记@Inject

    @Inject
OkHttpClient client;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerMainActivityComponent.builder().build().inject(this);
System.out.println("MainActivity.onCreate" + client);
}

com.exp.dagger2 I/System.out: MainActivity.onCreateokhttp3.OkHttpClient@d28c04e

这个过程与上个例子的区别主要在于MainModule,咱们这次再看生成的类是什么样的。我们主要讲解差异点

自动生成的类文件

多了MainModule_ProvideOkHttpClientFactory类,该类其实与ClassA_Factory本质是一样的。只不过ClassA_Factory只是单纯的提供ClassA对象,而MainModule_ProvideOkHttpClientFactory中是用于提供OkHttpClient对象的,假设MainModule还有其他Provides,那么会生成对应数量的MainModule_ProvideXXXFactory。MainModule_ProvideOkHttpClientFactory会提供n个get方法(方法的重载),不同的get方法会返回不同的对象,这些方法的生成规则是根据MainModule中那么使用@Provides注解标记的方法,此例只有一个。

public final class MainModule_ProvideOkHttpClientFactory implements Factory<OkHttpClient> {
private final MainModule module;

public MainModule_ProvideOkHttpClientFactory(MainModule module) {
assert module != null;
this.module = module;
}

@Override
public OkHttpClient get() {
return Preconditions.checkNotNull(
module.provideOkHttpClient(), "Cannot return null from a non-@Nullable @Provides method");
}

public static Factory create(MainModule module) {
return new MainModule_ProvideOkHttpClientFactory(module);
}
}

DaggerMainActivityComponent在创建mainActivityMembersInjector时候增加了一个provideOkHttpClientProvider参数,因为MainActivity_MembersInjector的作用是要完成MainActivity的对象注入,而MainActivity此时需要注入的有两个对象,第一个是ClassA,第二个是OkHttpClient。他们需要分别从ClassA_Factory与MainModule_ProvideOkHttpClientFactory获取

  // DaggerMainActivityComponent#initialize
private void initialize(final Builder builder) {

this.provideOkHttpClientProvider =
MainModule_ProvideOkHttpClientFactory.create(builder.mainModule);

this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(ClassA_Factory.create(), provideOkHttpClientProvider);
}
// MainActivity_MembersInjector#injectMembers
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.classA = classAProvider.get();
instance.client = clientProvider.get();
}

如果我们把ClassA的@Inject去掉,在MainModule中添加ClassA对象的提供方法,可以?答案:是的;可以自己试试

Module带参数

通常情况下大多数类的实例化可能都需要传入参数,那么我们来看看带参数情况下应该怎么写

我们将OkHttpClient网络请求的超时时限作为参数传入

@Module
public class MainModule {

int timeOut;

public MainModule(int timeOut) {
this.timeOut = timeOut;
}

@Provides
public OkHttpClient provideOkHttpClient(){
return new OkHttpClient.Builder().connectTimeout(timeOut, TimeUnit.SECONDS).build();
}

@Provides
public ClassA providesClassA(){
return new ClassA();
}
}

然后在MainActivity中我们通过设置MainModule对象完成参数设置

DaggerMainActivityComponent.builder().mainModule(new MainModule(10)).build().inject(this);

这里有个需要注意的地方,之前的例子中都没有参数,那么Component生成的类中会提供create方法直接创建Component

DaggerMainActivityComponent.create().build().inject(this);

而在有参数的情况下,不再提供create方法,而是只有build方法,因为此时需要传入MainModule对象

当然我们更建议以下写法:通过provide方法提供参数,一方面是解耦,一方面是代码可读性
Module中,不能出现参数和返回参数一致的情况,否则会导致死循环

@Module
public class MainModule {

int timeOut;

public MainModule(int timeOut) {
this.timeOut = timeOut;
}

@Provides
public int provideTimeOut(){
return timeOut;
}

@Singleton
@Provides
public OkHttpClient provideOkHttpClient(int timeOut){
return new OkHttpClient.Builder().connectTimeout(timeOut,TimeUnit.SECONDS).build();
}

@Provides
public ClassA providesClassA(){
System.out.println("MainModule.providesClassA");
return new ClassA();
}
}

Scope作用域

通过Singleton注解来看Scope,我们在页面上注入了两个OkHttpClient对象,我们来看看结果

    @Inject
OkHttpClient client1;
@Inject
OkHttpClient client2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerMainActivityComponent.builder().mainModule(new MainModule(10)).build().inject(this);
System.out.println("MainActivity.onCreate client1 --- " + client1);
System.out.println("MainActivity.onCreate client2 --- " + client2);
}

System.out: MainActivity.onCreate client1 — okhttp3.OkHttpClient@d28c04e
System.out: MainActivity.onCreate client2 — okhttp3.OkHttpClient@f45d56f

可以看到两个是不同的对象,那假设我们需要让他保持单例呢?可以使用@Single注解。我们需要关注的是如果我们希望把这个Person变成单例,这个注解需要在哪些地方使用。主要两个地方,如下:

一是:提供Person对象的地方,有可能是类构造器,有可能是Module中某个标记了@Provids注解的方法。但是由于@Inject与@Singleton不能同时使用,因此只能是在Module中某个标记了@Provids注解的方法。

@Module
public class MainModule {

int timeOut;

public MainModule(int timeOut) {
this.timeOut = timeOut;
}

@Singleton
@Provides
public OkHttpClient provideOkHttpClient(){
return new OkHttpClient.Builder().connectTimeout(timeOut, TimeUnit.SECONDS).build();
}

@Provides
public ClassA providesClassA(){
System.out.println("MainModule.providesClassA");
return new ClassA();
}
}

二是:作为提供对象与对象调用者之间的桥梁-Component

@Singleton
@Component(modules = MainModule.class)
public interface MainActivityComponent {

void inject(MainActivity activity);
}

System.out: MainActivity.onCreate client1 — okhttp3.OkHttpClient@2711f49
System.out: MainActivity.onCreate client2 — okhttp3.OkHttpClient@2711f49

我们来看看生成的文件差异,主要是在DaggerMainActivityComponent#initialize方法中

  private void initialize(final Builder builder) {

this.provideOkHttpClientProvider =
DoubleCheck.provider(MainModule_ProvideOkHttpClientFactory.create(builder.mainModule));

this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(provideOkHttpClientProvider);
}

在获取provideOkHttpClientProvider对象时,额外增加了DoubleCheck.provider(…)

  /** Returns a {@link Provider} that caches the value from the given delegate provider. */
public static Provider provider(Provider 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(delegate);
}

意思就是说如果咱们使用了scoped类型的注解,我们将返回之前缓存的Provider对象。我们再看看Singleton注解的定义

/**
* Identifies a type that the injector only instantiates once. Not inherited.
*
* @see javax.inject.Scope @Scope
*/

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

非常明显,类注释已经告诉我们这个注解的作用就是用来实现单例的。这边要注意到该注解使用了元注解@Scope,通过深入研究可以发现该Singleton注解的作用也仅仅是个标记作用而已。也就是说我们可以自定义一个注解来代替@Singleton`

@Scope
@Documented
@Retention(RUNTIME)
public @interface TestSingleton {}

还有需要注意的一点是Scope的作用周期取决于Component的周期长短,假设我们定义了AppComponent用于全局Application相关成员对象的注入,那么周期就是整个应用程序周期。而我们上面用到的MainActivityComponent对应的周期则是Activity的声明周期。这边有些同学会有疑惑的地方,如果说AppComponent中我们注入了一个单例OkHttpClient,我们后面又在别的地方通过另一个Component注入一个OkHttpClient,那么这两个OkHttpClient对象是否为同一个呢?答案是否定的,因为Scope与Component是对应关系。尽管都设定了Scope,但是由于Component,他们之间是相关独立的。

Qualifier方法区分

当我们的Module中存在两个相同返回值的方法时,我们可以使用@Name进行区分,对应在注入时也需要指明。这里的Qualifier与Scope完全类似,大家可以对比一下。因此,我们也可以自定义注解去代替@Name

// AppModule
@Module
public class AppMudole {

@Named("two")
@Provides
String providesString1(){
return "test_one";
}

@Named("three")
@Provides
String providesString2(){
return "test_two";
}

@Provides
CacheManager providesCacheManager(){
return new CacheManager();
}
}

// MainActivity
@Named("two")
@Inject
String test1;
@Named("three")
@Inject
String test2;

自定义注解的话可以这样子


@Qualifier
@Documented
@Retention(RUNTIME)
public @interface QualifierTwo {

}

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface QualifierThree {

}

@Module
public class AppMudole {

@QualifierTwo
@Provides
String providesString1(){
return "test_one";
}

@QualifierThree
@Provides
String providesString2(){
return "test_two";
}

@Provides
CacheManager providesCacheManager(){
return new CacheManager();
}
}

@QualifierTwo
@Inject
String test1;
@QualifierThree
@Inject
String test2;

Component间的依赖

很多时候可能某个东西是很多地方都需要使用到的,这个时候我们就可以通过Component之间的依赖实现

方式1:dependence


// 修改ClassA构造
public class ClassA {

CacheManager cacheManager;

public ClassA(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
}

@Module
public class AppMudole {

Context context;

public AppMudole(Context context) {
this.cOntext= context;
}

@Provides
Context providesContext(){
return context;
}

@Provides
CacheManager providesCacheManager(){
return new CacheManager();
}
}

@Component(modules = AppMudole.class)
public interface AppComponent {

CacheManager providesCacheManager();
}

@Singleton()
@Component(dependencies = AppComponent.class,modules = MainModule.class)
public interface MainActivityComponent {

void inject(MainActivity activity);
}

定义一个AppModule提供对象,定义AppComponent作为全局基础组件,提供应用中可能常用到的对象。在基础组件中我们一般就不会提供注入方法,因为该Component一般作为其他Component的依赖,而不会具体在某个地方使用。

然后我们在注入时候需要将AppComponent对象传递给MainActivityComponent

        AppComponent appCompOnent= DaggerAppComponent
.builder().appMudole(new AppMudole(this)).build();
DaggerMainActivityComponent.builder().appComponent(appComponent)
.mainModule(new MainModule(10))
.build().inject(this);

这个时候咱们来看看apt生成的代码


// DaggerMainActivityComponent#initialize
private void initialize(final Builder builder) {

this.providesCacheManagerProvider =
new com_exp_dagger2_AppComponent_providesCacheManager(builder.appComponent);

this.providesClassAProvider =
MainModule_ProvidesClassAFactory.create(builder.mainModule, providesCacheManagerProvider);

this.mainActivityMembersInjector = MainActivity_MembersInjector.create(providesClassAProvider);
}

// DaggerMainActivityComponent$com_exp_dagger2_AppComponent_providesCacheManager
private static class com_exp_dagger2_AppComponent_providesCacheManager
implements Provider<CacheManager> {

private final AppComponent appComponent;

com_exp_dagger2_AppComponent_providesCacheManager(AppComponent appComponent) {
this.appCompOnent= appComponent;
}

@Override
public CacheManager get() {
return Preconditions.checkNotNull(
appComponent.providesCacheManager(),
"Cannot return null from a non-@Nullable component method");
}
}

DaggerMainActivityComponent中对于Component依赖中提供的对象获取仍然是通过Provider对象的形式实现,只不过对于Component依赖,会为其创建一个命名规则为”包名依赖组件名提供对象的方法名”静态内部类。然后通过构造模式的Builder获取到组件对象,作为参数传入com_exp_dagger2_AppComponent_providesCacheManager生成Provider。

假设AppMudole中提供了获取另一个类型对象的方法,这里举例如下

    @Provides
String providesString(){
return "test";
}

这个时候我们尝试在MainActivity中进行String对象的注入

    @Inject
String test
;

编译会发现报错,Dagger2找不到从哪能获取到这个String对象

java.lang.String cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.

我们在必须在AppComponent中显式提供String对象的方法即可

@Component(modules = AppMudole.class)
public interface AppComponent {

CacheManager providesCacheManager();
String providesString();
}

那么这样来看的话就很清晰了,其实AppComponent被其他Component依赖,那么它只是作为AppModule对象的转发者

方式2:SubComponent

AppComponent中的定义不再是提供AppModule中的对象转发,而是变成提供子Component

@Component(modules = AppMudole.class)
public interface AppComponent {

MainActivityComponent activityComponent();
}

这种情况下,就可以直接使用AppModule提供的对象完成注入(对此上例中的String对象注入)
然后子Comonent的定义很简单,没有变化,只是去除了dependencies换为SubComponent

@Singleton()
@Subcomponent(modules = MainModule.class)
public interface MainActivityComponent {

void inject(MainActivity activity);
}

需要注意的是这个时候MainModule必须提供无参构造器(这个在后面分析会提到)

注入时通过DaggerAppComponent完成注入,ClassA对象的注入是由SubComponent(即MainActivityComponent)提供的,而String对象的注入是由AppComponent提供的


@Inject
ClassA classA
;
@Inject
String test
;

DaggerAppComponent.builder().build().activityComponent().inject(this);

OK,我们再来看看生成的代码


// DaggerAppComponent部分代码

@Override
public MainActivityComponent activityComponent() {
return new MainActivityComponentImpl();
}

private final class MainActivityComponentImpl implements MainActivityComponent {
private final MainModule mainModule;

private Provider providesClassAProvider;

private MembersInjector mainActivityMembersInjector;

private MainActivityComponentImpl() {
this.mainModule = new MainModule();
initialize();
}

@SuppressWarnings("unchecked")
private void initialize() {

this.providesClassAProvider =
MainModule_ProvidesClassAFactory.create(
mainModule, DaggerAppComponent.this.providesCacheManagerProvider);

this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(
providesClassAProvider, DaggerAppComponent.this.providesStringProvider);
}

@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
}

可以看到DaggerAppComponent中为每个子Component生成了一个命名规则为“ComponentName + Impl”的内部类,并提供了获取方法(其实是根据AppComponent接口的定义),然后在initialize方法中我们可以看到mainActivityMembersInjector由两个部分组成:providesClassAProvider(由MainModule而来,SubComponent所用的Module),providesStringProvider(由AppModule而来,AppComponent所用的Module,对象由外部类DaggerAppComponent创建)。注意,这种情况下时候子Component不会生成任何DaggerXXXComponent类。

两种方式比较

两种方式都是用来实现Component之间关联的,dependencies 方法更像Java中的组合形式,而Subcomponent更像是类之间的继承关系。具体情况根据实际需要进行选择,下面是我以自身的理解进行差异分析

两者与Scope需要注意的:两个拥有依赖关系的 Component 是不能有相同 @Scope 注解的,而使用@SubComponent 则可以使用相同的@Scope注解。

dependencies 方法

特点:

  • 可以清晰的对应Component所依赖的其他Component
  • 两个Component都会生成对应的DaggerXXXComponent
  • 被依赖Component为目标Component提供相关对象获取
  • 注入时使用目标Component传入依赖Component完成

注意:

  • 被依赖Component中没有显式提供的依赖无法完成注入

SubComponent 方法

特点:

  • 只有父Component会生成DaggerXXXComponent
  • 父Component可以方便的管理子Component
  • 默认可获取到父子两Component使用到的Module所提供的对象
  • 注入时使用父Component并选择指定的子Component完成

注意:

  • 子Component所用到的Module必须提供无参构造器

懒加载与重新加载

懒加载(Lazy):调用get方法时候才去创建对象,后续获取都为同一个对象
重新加载(Provides):调用get方法时候会强制重新创建对象,对象是否为同一个取决于Module的实现或Scope注解

public class MainActivity extends AppCompatActivity {

@Inject
Lazy classALazy;
@Inject
Provider provider;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerAppComponent.builder().build().activityComponent().inject(this);
ClassA classA = this.classALazy.get();
String string = this.provider.get();

}
}

老样子,看看生成的代码

  // MainActivity_MembersInjector#injectMembers
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.classALazy = DoubleCheck.lazy(classALazyProvider);
instance.provider = providerProvider;
}

咱们主要就看MainActivity_MembersInjector类中的injectMembers方法,通常情况下injectMembers方法中都是直接通过相关的Provider的get方法提取对象。在使用了Lazy则通过DoubleCheck.check()处理后得到Lazy对象,而Provider则直接赋值


// DoubleCheck 部分代码

/** Returns a {@link Lazy} that caches the value from the given provider. */
public static Lazy lazy(Provider provider) {
if (provider instanceof Lazy) {
@SuppressWarnings("unchecked")
final Lazy lazy = (Lazy) provider;
// Avoids memoizing a value that is already memoized.
// NOTE: There is a pathological case where Provider

may implement Lazy, but P and L
// are different types using covariant return on get(). Right now this is used with
// DoubleCheck 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(checkNotNull(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 + ". This is likely "
+ "due to a circular dependency.");
}
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;
}

由上面代码可以看出,Lazy其实是一个DoubleCheck对象,通过封装Provider使用双重检验机制完成单例实现。

总结

Component:必不可少的东西,本身不提供对象,主要用于链接对象提供者与注入目标,完成对象注入
Inject:可用于标记构造器,表明可通过该构造器获取对象;可用于标记需要注入的目标成员变量;
Module:提供各类对象,方法返回值与参数不可一致,可用@Name或者自定义注解区分相关返回值的方法
Scope:用于作用域标记,可用@Single或者自定义注解实现单例或者对象作用域范围控制
Component关联:dependencies 方式与 SubComponent 方式,类似组合与集成,区分使用
懒加载(Lazy)与重新加载(Provider):Lazy是Provider封装而成的DoubleCheck对象,单例
在相关的方法定义中方法名是没有严格要求的,只不过是为了规范统一,核心在返回值类型


推荐阅读
author-avatar
圈圈
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有