Dagger2进阶学习

// BasicAnnotationProcessor.java

public interface ProcessingStep {

// 处理注解的集合

Set<? extends Class<? extends Annotation>> annotations();

//处理注解对应的元素,返回没有进行处理的元素的集合

Set<? extends Element> process(

SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation);

}

来看下之前注释6的process():

// BasicAnnotationProcessor.java

private void process(ImmutableSetMultimap<Class<? extends Annotation>, Element> validElements) {

for (ProcessingStep step : steps) { // 1

ImmutableSetMultimap<Class<? extends Annotation>, Element> stepElements =

new ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element>()

.putAll(indexByAnnotation(elementsDeferredBySteps.get(step), step.annotations()))

.putAll(filterKeys(validElements, Predicates.in(step.annotations())))

.build(); // 2

if (stepElements.isEmpty()) {

elementsDeferredBySteps.removeAll(step);

} else {

Set<? extends Element> rejectedElements = step.process(stepElements); // 3

elementsDeferredBySteps.replaceValues(

step, transform(rejectedElements, ElementName::forAnnotatedElement));

}

}

}

注释1:遍历所有的 ProcessingStep

注释2:获取一个step需要处理的 Element的集合,主要通过 setp.annotation() 过滤掉不是该step需要处理的注解。

比如说一个注解,他需要处理的Element集合为{方法/ 属性 / 类...}

注释3:如果集合不为空,就调用step的 process,即子类实现的process方法。

BasicAnnotationprocessor所做的事情可以总结成:

  1. 定义最小逻辑处理单元 ProcessingStep

  2. 让子类实现 initSteps(),这个方法里面,让子类自己去分出几个ProcessingStep

  3. 遍历每个 ProcessingStep,调用他们的 process

分成每个逻辑单元分布实现,这样做的好处就是:对于一个processor要处理众多的注解时,显然写在一个process方法里面未免太过混杂, BasicAnnotationprocessor对每个注解的处理进行了管理,清晰且简单。

那我们来看看 ComponentProcessor是如何在 initStep()里面创建 ProcessingSteps的:

// ComponentProcessor.java

protected Iterable<? extends ProcessingStep> initSteps() {

ProcessorComponent.factory().create(processingEnv, testingPlugins).inject(this);

statisticsCollector.processingStarted();

bindingGraphPlugins.initializePlugins();

return Iterables.transform(

processingSteps, // 1

step -> new DaggerStatisticsCollectingProcessingStep(step, statisticsCollector));

}

注释1:传入了 processingSteps,它就是ComponentProcessor定义的ProcessingStep集合,但是发现它没在这个方法中创建。在查看它是怎么来的时候,发现它也是通过了注解的方法实现创建...(套娃了),这里不讲过程,直接看看它的创建:

// ComponentProcessor.java

@Module

interface ProcessingStepsModule {

@Provides

static ImmutableList processingSteps(

MapKeyProcessingStep mapKeyProcessingStep,

InjectProcessingStep injectProcessingStep,

MonitoringModuleProcessingStep monitoringModuleProcessingStep,

MultibindingAnnotationsProcessingStep multibindingAnnotationsProcessingStep,

BindsInstanceProcessingStep bindsInstanceProcessingStep,

ModuleProcessingStep moduleProcessingStep,

ComponentProcessingStep componentProcessingStep,

ComponentHjarProcessingStep componentHjarProcessingStep,

BindingMethodProcessingStep bindingMethodProcessingStep,

CompilerOptions compilerOptions) {

return ImmutableList.of(

mapKeyProcessingStep,

injectProcessingStep,

monitoringModuleProcessingStep,

multibindingAnnotationsProcessingStep,

bindsInstanceProcessingStep,

moduleProcessingStep,

compilerOptions.headerCompilation()
:
componentProcessingStep,

bindingMethodProcessingStep);

}

}

在 ImmutatbleList.of 中 创建了很多个 xxxPrcessingStep,他们都间接的继承了 ProcessingStep,代表着每一种注解,可以看出来ComponentProcessor要处理的注解有 @MapKey、@Inject、@Mutibinding、@Module、@Component等等。

所以真正实现process方法,都是在这些 xxxProcessingStep.process()中,这里以 @Inject为例子,看看他是怎么实现对 @Inject注解的处理。

<>1.2 从InjectProcessingStep到Inject文件的生成


InjectProcessingStep实现了继承了 TypeCheckingProcessingStep

// InjectProcessingStep.java

final class InjectProcessingStep extends TypeCheckingProcessingStep {

private final ElementVisitor<Void, Void> visitor;

@Inject

InjectProcessingStep(InjectBindingRegistry injectBindingRegistry) {

super(e -> e);

this.visitor =

new ElementKindVisitor8<Void, Void>() { // 构造一个访问者,对不同的Element做出反应

@Override

public Void visitExecutableAsConstructor(

ExecutableElement constructorElement, Void aVoid) {

injectBindingRegistry.tryRegisterConstructor(constructorElement);

return null;

}

@Override

public Void visitVariableAsField(VariableElement fieldElement, Void aVoid) {

injectBindingRegistry.tryRegisterMembersInjectedType(

MoreElements.asType(fieldElement.getEnclosingElement()));

return null;

}

@Override

public Void visitExecutableAsMethod(ExecutableElement methodElement, Void aVoid) {

injectBindingRegistry.tryRegisterMembersInjectedType(

MoreElements.asType(methodElement.getEnclosingElement()));

return null;

}

};

}

@Override

public Set<Class<? extends Annotation>> annotations() {

return ImmutableSet.of(Inject.class);

}

@Override

protected void process(

Element injectElement, ImmutableSet<Class<? extends Annotation>> annotations) {

injectElement.accept(visitor, null);

}

}

可以看到 Inject注解器也实现了给自己注解。我们先不用搞懂它是怎么给自己实现注解处理的。

我们先看看 被@Inject修饰的构造方法里面做了什么:创建了一个 ElementKindVisitor8对象并赋值给了 ElementVisitor类型的 vistor。然后又在 process()中调用了 Element.accept(visitor)

这个环节的操作,使用了面向对象编程的设计模式中 "访问者模式" 。当Android中提到访问者模式的例子时,Dagger2中这块地方,就是被拿出来讲解的最经典的例子= = 。所以要看懂这块代码的结构,先要搞清楚什么是访问者模式,对此,我还专门学习写了一篇出来,请参考:浅学设计模式之访问者模式(9/23)

从命名上就很显然的看出:

  • Element是元素类,它定义了 accept(ElementVistor)方法,来接收访问者

  • ElementVistor是访问者的接口类,它代表的是一种状态,当给出一个状态时,它的子类要根据Element的种类,来做出对应的处理。

而上文中,提到的 ElementKinVistor8就是ElementVistor的一个子类(它是ElementVistor基于JDK8的实现),它根据在 process(Element)的不同的Element做出不同的反应:

  • 当传入的Element是构造函数时,调用 visitExecutableAsConstructor(),内部调用 InjectBindingRegistry.tryRegisterConstructor()实现

  • 当传入的Element是成员变量时,调用visitVariableAsField(),内部调用 InjectBindingRegistry.tryRegisterMembersInjectedType()实现

  • 当传入的Element是方法时,调用 visitExecutableAsMethod(),内部调用 InjectBindingRegistry.tryRegisterMembersInjectedType()实现

这说明了什么:@Inject注解可以修饰:构造函数、成员变量、方法。对于不同的元素,用不同的处理。

这就体现了访问者模式。

在源码中我们可以看到具体的实现是由 InjectBindingRegistry类实现,它是一个抽象类,真正的实现是在 InjectBindingRegistryImpl中, 上面三个方法最终都会调用 tryRegisterMembersInjectedType(),我们来看看它的实现:

// dagger/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java

private Optional tryRegisterMembersInjectedType(

TypeElement typeElement,

Optional resolvedType,

boolean warnIfNotAlreadyGenerated) {

DeclaredType type = MoreTypes.asDeclared(typeElement.asType()); // 1

Key key = keyFactory.forInjectConstructorWithResolvedType(type); // 2

MembersInjectionBinding cachedBinding = membersInjectionBindings.getBinding(key); // 3

if (cachedBinding != null) {

return Optional.of(cachedBinding);

}

ValidationReport report =

injectValidator.validateMembersInjectionType(typeElement); // 4

report.printMessagesTo(messager); // 5

if (report.isClean()) {

MembersInjectionBinding binding = bindingFactory.membersInjectionBinding(type, resolvedType); // 6

registerBinding(binding, warnIfNotAlreadyGenerated); // 7

for (Optional supertype = types.nonObjectSuperclass(type);

supertype.isPresent();

supertype = types.nonObjectSuperclass(supertype.get())) {

getOrFindMembersInjectionBinding(keyFactory.forMembersInjectedType(supertype.get()));

}

return Optional.of(binding);

}

return Optional.empty();

}

private void registerBinding(ProvisionBinding binding, boolean warnIfNotAlreadyGenerated) {

provisionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated); // 8

if (binding.unresolved().isPresent()) {

provisionBindings.tryToGenerateBinding(binding.unresolved().get(), warnIfNotAlreadyGenerated);

}

}

注释1:将TypeElement转换成 DeclaredType

注释2:通过注释1获取的type,通过Key工厂获得一个Key

注释3:通过Key去内存中看看之前有没有缓存过该注解内容,如果缓存过则取缓存的的内容

注释4、5:在 messager上打印TypeElement信息

注释6、7:通过工厂模式创建出 注解信息 MembersInjectonBinding,它是 ProvisionBinding抽象类的子类,它的作用是描述一个被注解修饰的信息 。然后调用 registerBinding()

注释8:将 注释7的 ProvisionBinding注册到 provisionBindings中,它的类型是 BindingsCollection<ProvisionBinding>,即专门用来收集注解信息。

到这里,通过 InjectProcessingStep.process()方法,@Inject修饰的所有信息,全都被注册到了 provisionBindings中。

我们都知道注解处理器无非就做两件事情:①收集注解信息 ②将这些信息写成Java文件

怎么实现第二步呢?在 BasicAnnotationProcessor中,在调用完 process()遍历执行完每个ProcessingStep后,还会执行postRound(),它会让子类来实现。

在 @Inject中,其Java文件的产生是在 ComponentProcessor中:

// ComponentProcessor.java

protected void postRound(RoundEnvironment roundEnv) {

statisticsCollector.roundFinished();

if (roundEnv.processingOver()) {

statisticsCollector.processingStopped();

} else {

try {

injectBindingRegistry.generateSourcesForRequiredBindings(

factoryGenerator, membersInjectorGenerator); // 1

} catch (SourceFileGenerationException e) {

e.printMessageTo(processingEnv.getMessager());

}

}

clearableCaches.forEach(ClearableCache::clearCache);

}

}

注释1:调用 injectBindingRegistry.generateSourcesForRequiredBindings实际上就是调用:InjectBindingRegistryImpl.generateSourcesForRequiredBindings()

// dagger/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java

@Override

public void generateSourcesForRequiredBindings(

SourceFileGenerator factoryGenerator,

SourceFileGenerator membersInjectorGenerator)

throws SourceFileGenerationException {

provisionBindings.generateBindings(factoryGenerator); // 1

membersInjectionBindings.generateBindings(membersInjectorGenerator); // 2

}

注释1:生成 XXX_Factory 类的Java文件

注释2:生成 XXXX_MemberInjector 类的Java

由于其细节都是文件生成,到这里就不再讲解下去了。

<>1.3 源码总结


虽然上面我讲解了只有关于 @Inject的注解处理,但是我相信触类旁通,Dagger2对其他的注解肯定也是做了类似的处理。

这里来总结一下 1.1、1.2节中所讲的知识。

(1)关于Dagger2 注解处理架构:

由于Dagger所要处理的注解很多, Inject、Module、Scope、Qualifier、Name、Component...为每个注解写一个Processor未免太枯燥,且难于管理,并且他们之间又存在着一些共性。

所以Dagger把这些共性抽了出来,并编写了 BasicAnnotationProcessor继承自AbstractProcessor。

并且实现了内部类ProcessingStep对应着每一个注解,它负责收集自己那部分注解修饰的信息。比如处理 @InjectInjectProcessingStep等。

BasicAnnotationPrcocessor.process() 会去遍历所有的 ProcessingStep.process()收集信息。

BasicAnnotationPrcocessor.postRound() 还会去根据需要产生Java文件(当然有些注解产生文件的入口不在这里)

(2)关于 XXXProcessingStep

间接的继承了 ProcessingStep,具备收集、注册信息的功能。

例如在 InjectProcessingStep中,它会把收集到的 Element,通过访问者模式 ,将Element信息注册到集合BindingsCollection中。

最后写文件的时候,就是根据这个集合的内容编写的。

<>2. dagger.android

===================================================================================

Dagger针对Android平台提供了专门的库来简化注入过程。需要我们添加额外的依赖库。

在Android中Dagger的注入是分层级的,有对应用层级全局属性或服务(OkHttp、Retrofit、Database)的注入,有对Activity中的变量注入,也有对Fragment中的变量注入,对应Component的层级如下图所示:

下面举几个例子,看下使用 dagger.android 在上面层级中的使用。

<>2.1 MainActivity中一般对象的注入


(1)创建 MainActivitySubComponent:

@Subcomponent

public interface MainActivitySubComponent extends AndroidInjector {

@Subcomponent.Builder

abstract class Builder extends AndroidInjector.Builder{}

}

MainActivitySubComponent 是MainActivity对应的Component,它是应用层级Component的子Component,所以使用 @Subcomponent注解。

MainActivitySubComponent 继承自 AndroidInjectorAndroidInjector是Android中的核心类(Activity、Fragment等)的注入器,可以完成对Activity或Fragment的成员变量的注入。

另外,我们还需要创建一个继承自AndroidInjector.Builder 的抽象类Builder,它的作用是创建 MainActivitySubComponent的实例。

(2)创建ActivityModule

@Module(subcomponents = MainActivitySubComponent.class)

public abstract class ActivityModule {

//返回MainActivity对应的注入器工厂

@Binds

@IntoMap

@ActivityKey(MainActivity.class)

abstract AndroidInjector.Factory<? extends Activity> bindMainActivityInjectorFactory(MainActivitySubComponent.Builder builder);

//返回其他Activity对应的注入器工厂

...

}

ActivityModule 可以提供各个Activity对应的AndroidInjector.Factory,也就是注入器工厂类。

subcomponents指定为MainActivitySubComponent,表示MainActivitySubComponent是任何使用ActivityModule的Component的子Component。

(3)创建应用层级的 AppComponent

@Component(modules = {AndroidInjectionModule.class, ActivityModule.class})

public interface AppComponent {

void inject(App instance);

}

应用层级的 AppComponent安装了两个Module,一个是 AndroidInjectionModule(由dagger-android提供),一个是ActivityModule。

另外还提供注入到Application类中的注入方法inject,值得注意的是,inject方法中的参数为我们自定义的App类型,而非Application类。

(4)注入到 Application类

public class App extends Application implements HasActivityInjector {

@Inject

DispatchingAndroidInjector dispatchingAndroidInjector;

@Override

public void onCreate() {

super.onCreate();

DaggerAppComponent.create().inject(this);

}

@Override

public AndroidInjector activityInjector() {

return dispatchingAndroidInjector;

}

}

在应用的Application类中,我们需要实现接口HasActivityInjector返回一个全局的Activity的注入器 dispatchingActivityInjector,而它的初始化由 AppComponent来完成,即由AppComponent对应生成的 DaggerAppComponent在Application类的onCreate方法中注入。

(5)注入到MainActivity

@Override

protected void onCreate(Bundle savedInstanceState) {

AndroidInjection.inject(this);

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

在MainActivity的 onCreate()中,调用 super.onCreate()前使用 AndroidInjection对MainActivity进行注入。

这个注入过程又是怎么完成的呢?实际上:

  1. AndroidInjection注入时会获取Application类里面的dispatchActivityInject

  2. 通过dispatchActivityInject查找到对应MainActivity的 AndroidInjector.Factory(即MainActivitySubComponent类中的Builder,该Buidler实现了AndroidInjector.Factory接口 )

  3. 然后使用Builder创建MainActivity的注入器AndroidInjector(即MainActivitySubComponent)

  4. 最终使用MainActvitySubComponent实现对MainActivity的注入,完成对MainActivity中对象的赋值。

我们可以看下 AndroidInjection.inject()

// AndroidInjection.java

public static void inject(Activity activity) {

checkNotNull(activity, "activity");

Application application = activity.getApplication(); // 获取Application对象

if (!(application instanceof HasActivityInjector)) { // 希望Application实现了HasActivityInjector接口

throw new RuntimeException(

String.format(

"%s does not implement %s",

application.getClass().getCanonicalName(),

HasActivityInjector.class.getCanonicalName()));

}

AndroidInjector activityInjector =

((HasActivityInjector) application).activityInjector(); // 获取Application中的 dispatchingAndroidInjector对象

checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());

activityInjector.inject(activity); //调用这个对象的inject方法(就是调用了MainActivitySubComponent的inject)

}

(6)@ContributesAndroidInjector

通过上述的步骤,就可以完成对Activity的注入,但是整体略显繁琐,当我们想要注入某个Activity中,就必须要创建该Activity的Component,而且这些代码都是模板代码,不需要动脑子,另外每次增加一个Component,就要在ActivityModule的Subcomponent集合中添加它。

事实上,Activity对应的Component的创建可以使用注解 @ContributesAndroidInjector自动生成。

我们只需要修改 ActivityModule就可以了:

@Module

public abstract class ActivityModule {

// //返回MainActivity对应的注入器工厂

// @Binds

// @IntoMap

// @ActivityKey(MainActivity.class)

// abstract AndroidInjector.Factory<? extends Activity> bindMainActivityInjectorFactory(MainActivitySubComponent.Builder builder);

@ContributesAndroidInjector

abstract MainActivity contributesMainActivityInjector();

}

<>2.2 MainActivity中TextView对象的注入


由于TextView对象需要TextViewModule提供,在构建MainActivity对应的Component时需要创建一个TextViewModule的实例,所以在ActivityModule中不能使用 @ContributesAndroidInjector自动生成MainActivitySubComponent,还是需要手动创建。

在MainActivitySubComponent内部类Builder中实现 seedInstance(),并创建抽象方法textViewModule接收TextViewModule对象作为参数,在seedInstance()中,调用 textViewModule方法传入一个TextViewModule实例。

这时MainActivityStubCoponent就具有注入TextView的能力。

@Subcomponent(modules = TextViewModule.class)

public interface MainActivitySubComponent extends AndroidInjector {

@Subcomponent.Builder

abstract class Builder extends AndroidInjector.Builder{

abstract void textViewModule(TextViewModule textViewModule);

@Override

public void seedInstance(MainActivity instance) {

textViewModule(new TextViewModule(instance));

}

}

}

<>2.3 Fragment的注入


Activiy对应的注入器一般作为Application对应注入器的 Subcomponent,而Fragment对应的注入器可以是其他Fragment、Activity或Application的对应Component的Subcomponent,这要看Fragment的Module是安装到哪个Component上。

现在我们在MainActivity内部的MainFragment中也注入一个Java对象,步骤如下:

(1)创建FragmentModule

在FragmentModule中使用注解 @ContributesAndroidInjector,这样就可以自动生成MainFragment对应的Component。

@Module

public abstract class FragmentModule {

@ContributesAndroidInjector

abstract MainActivity.MainFragment contributeMainActivityInjector();

}

(2)安装FragmentModule

那么生成的Component是作为谁的Subcomponent呢?这就要看FragmentModule安装在哪里了。

这里,我们将FragmentModule安装到AppComponent中,那么MainFragment的Component将是AppComponent的Subcomponent,代码如下:

@Component(modules = {AndroidInjectionModule.class, ActivityModule.class

, FragmentModule.class})

public interface AppComponent {

void inject(App instance);

}

(3)注入Application

在Application类中实现 HasFragmentInjector接口,返回一个 dispatchingFragmentInjector,它同样会被AppComponent注入,完成初始化,后续会使用它来完成Fragment的注入:

public class App extends Application implements HasActivityInjector, HasFragmentInjector {

@Inject

DispatchingAndroidInjector dispatchingAndroidInjector;

@Override

public void onCreate() {

super.onCreate();

DaggerAppComponent.create().inject(this);

}

@Override

public AndroidInjector<android.app.Fragment> fragmentInjector() {

return dispatchingAndroidInjector;

}

}

(4)注入Fragment

相关推荐
虾球xz6 分钟前
游戏引擎学习第279天:将实体存储移入世界区块
c++·学习·游戏引擎
njsgcs10 分钟前
opencascade.js stp vite webpack 调试笔记
开发语言·前端·javascript
yanyanwenmeng18 分钟前
智能体制作学习笔记2——情感客服
笔记·学习
学地理的小胖砸26 分钟前
【Python 异常处理】
开发语言·python
小猪写代码27 分钟前
Ubuntu 系统默认已安装 python,此处只需添加一个超链接即可
linux·python·ubuntu
老哥不老27 分钟前
Python调用SQLite及pandas相关API详解
python·sqlite·pandas
T0uken1 小时前
【前端】:单 HTML 去除 Word 批注
前端·html·word
Cloud Traveler1 小时前
从 “学会学习” 到高效适应:元学习技术深度解析与应用实践
人工智能·学习·自然语言处理
蹦蹦跳跳真可爱5891 小时前
Python----神经网络(《Inverted Residuals and Linear Bottlenecks》论文概括和MobileNetV2网络)
网络·人工智能·python·深度学习·神经网络
st紫月1 小时前
用vue和go实现登录加密
前端·vue.js·golang