Dagger2进阶学习,2024Android笔试题总结

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;

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
img
img
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司21年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

详细整理在GitHub可以见;

Android架构视频+BAT面试专题PDF+学习笔记​

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)**
[外链图片转存中…(img-4kXzCwIF-1710833814880)]

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司21年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-kDSUwcNU-1710833814881)]

详细整理在GitHub可以见;

Android架构视频+BAT面试专题PDF+学习笔记​

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值