击剑编排软件_击剑:第一印象

击剑编排软件

The hilt of a dagger, sword or lightsaber is used to be able to hold the weapon without cutting yourself in the fingers. Hilt is the latest attempt from Google to attempt to prevent you from cutting yourself when using Dagger. Shortly before the alpha release I started playing with it a bit. Being a big dagger-android fan I must say I was skeptical at first, but diving into it I found it to be pretty amazing.

匕首,剑或光剑的柄被用来握住武器而不会割伤手指。 击剑是Google的最新尝试,目的是防止您在使用Dagger时割伤自己。 在发布Alpha版本之前不久,我开始使用它。 作为一个匕首Android的忠实粉丝,我必须说起初我对此表示怀疑,但是深入研究它后,我发现它非常惊人。

Based on the active development that is happening on GitHub, this article will attempt to explain how Hilt works and how it is different from the dagger-android artifact that it is supposed to replace.

基于GitHub上正在进行的积极开发,本文将尝试解释Hilt的工作原理以及它与应该替代的dagger-android工件的不同之处。

Before I continue, a quick disclaimer. The views in this article are based on what’s in the Dagger codebase before Hilt officially reached alpha. With only documentation in the code to rely on, interpretations might also be wrong. This article assumes previous knowledge of Dagger and dagger-android and does not aim to be a getting started guide. I’ve been in contact with the Hilt team and once Hilt will be released it will include multiple resources for getting started and migrating from Dagger and dagger-android.

在继续之前,请快速声明一下。 本文中的观点基于Hilt正式进入Alpha之前Dagger代码库中的内容。 仅依靠代码中的文档,解释也可能是错误的。 本文假定您具有Dagger和dagger-android的先前知识,并且不打算成为入门指南。 我一直与Hilt团队保持联系,一旦Hilt发布,它将包括多个资源,可用于从Dagger和dagger-android进行迁移。

With that out of the way, let’s take a look at what Hilt is and what it does. On a high level, it consists of three parts.

顺便说一下,让我们看一下Hilt是什么以及它做什么。 从高层次上讲,它包括三个部分。

  • An AAR artifact which contains a couple of new annotations, and some component and scope definitions. That’s right! That means that, with Hilt, you won’t need to define components yourself anymore. All components and scopes are already defined for you by the library.

    一个AAR工件,其中包含几个新的注释以及一些组件和作用域定义。 那就对了! 这意味着,使用Hilt,您不再需要自己定义组件。 库已经为您定义了所有组件和范围。
  • An annotation processor which saves you from writing tons of boilerplate code. You won’t need to define a component ever again.

    注释处理器可避免编写大量样板代码。 您无需再次定义组件。
  • A Gradle plugin which applies a byte-code transformation to make sure that your source code will use generated classes without you referencing them in source code. Although the plugin is technically not required to be used, it does make everything look a lot prettier.

    一个Gradle插件,该插件应用字节码转换以确保您的源代码将使用生成的类,而无需在源代码中引用它们。 尽管从技术上讲不需要使用该插件,但是它确实使所有内容看起来都更加漂亮。

To set up Hilt, all you need to do is define a dependency on the AAR, add the annotation processor and apply the Gradle plugin. Since it has not been published yet, I had to use a local snapshot, but once released the Gradle logic will look something like this:

要设置Hilt,您需要做的就是在AAR上定义一个依赖项,添加注释处理器并应用Gradle插件。 由于尚未发布,因此我不得不使用本地快照,但是一旦发布,Gradle逻辑将如下所示:

// in your root build.gradle
buildscript
repositories {
jcenter()
}
dependencies {
....
classpath "com.google.dagger:hilt-android-gradle-plugin:2.X"
}
}

// in modules where you use hilt
apply plugin: 'dagger.hilt.android.plugin'dependencies {
implementation 'com.google.dagger:hilt-android:2.X'
kapt 'com.google.dagger:hilt-android-compiler:2.X'}

But, before we continue with implementation, let’s take a step back.

但是,在继续执行之前,让我们退后一步。

希尔特做什么? (What does Hilt do?)

Hilt builds on top of the functionality that Dagger already provides and makes it easier to use Dagger in an Android project. It is an opinionated library so chances are that you might not be able to just plug it into your project (for me that didn’t work).

Hilt建立在Dagger已提供的功能之上,并使其在Android项目中更易于使用Dagger。 这是一个自以为是的库,因此您可能无法仅将其插入到您的项目中(对我来说这没用)。

Being opinionated might be one of the best things about Hilt. Yes, it is a bit more limiting than handling the Dagger beast by itself, and yes, it might mean that it will take a bit more effort to migrate to it. However, there were so many ways in which you could do Dagger before, a lot of them contained tons of unnecessary boilerplate code.

自以为是可能是Hilt最好的事情之一。 是的,这比单独处理Dagger野兽要有更多的限制,是的,这可能意味着要花很多时间才能迁移到它。 但是,以前有很多方法可以使用Dagger,其中许多包含大量不必要的样板代码。

The choices made by the Hilt team might seem limiting to Dagger experts, but in the end it brings uniformity and forces you to use it in a way that has barely any boilerplate code which is a big plus when a large team is working on a project together, or when new members join a project.

Hilt团队所做的选择似乎仅限于Dagger专家,但最终它带来了统一性,并迫使您以几乎没有任何样板代码的方式使用它,这对于大型团队进行项目而言是一大优势。在一起,或者新成员加入项目时。

定义绑定 (Defining bindings)

Something that hasn’t changed a lot is how you define bindings. You’ll still have constructor injection, and you’ll still define modules annotated with @Module. Those modules will still have @Binds and @Provides methods.

定义绑定的方式并没有太大变化。 您仍然需要构造函数注入,并且仍然可以定义使用@Module注释的模块。 这些模块将仍然具有@Binds@Provides方法。

The one thing that did change is that you will now need to add an additional annotation. Every module in Hilt needs to be annotated with an @InstallIn annotation. This annotation requires one argument which indicates what component the module’s bindings should be used in. @InstallIn(ApplicationComponent::class) on a class called AppModule will have the same behavior as when you would previously do @Component(modules = AppModule::class).

发生变化的一件事是您现在需要添加一个附加注释。 Hilt中的每个模块都需要使用@InstallIn注释进行注释。 此批注需要一个参数,该参数指示应在哪个组件中使用模块的绑定。名为AppModule @InstallIn(ApplicationComponent::class)上的@InstallIn(ApplicationComponent::class)与以前使用@Component(modules = AppModule::class)时)具有相同的行为。 @Component(modules = AppModule::class)

This is a really cool change in the way dependencies are provided because a module definition like this is much clearer on its own.

这是提供依赖项的方式的一个非常酷的变化,因为这样的模块定义本身就更加清晰了。

The reason that this is so much nicer is that, by just looking at the module class, you know exactly where it will be used, and what instances will be available for defining @Provides and @Binds methods. Availability of instances is very dependent on the component that the module is included in. In dagger-android a module could be included anywhere, in some cases the bindings might be valid, in other cases not. Or, in other words, the bindings and the component that they are installed in are logically very tightly coupled. Having tightly coupled concepts in the same file is a huge win.

之所以这么好,是因为仅查看模块类,就可以确切地知道将在哪里使用它,以及哪些实例可用于定义@Provides@Binds方法。 实例的可用性很大程度上取决于模块所包含的组件。在dagger-android中,模块可以包含在任何位置,在某些情况下绑定可能有效,而在其他情况下则无效。 或者,换句话说,绑定和安装它们的组件在逻辑上非常紧密地耦合在一起。 在同一文件中拥有紧密耦合的概念是一个巨大的胜利。

定义入口点 (Defining entry points)

The next thing you will need to do is define EntryPoints, first you’ll need to add an @HiltAndroidApp annotation to your application class. This will replace the DaggerApplication class of dagger-android. Annotating your application class with this is all you need to get member injection in the application started. You won’t need to build any component, and you won’t need to call an inject method manually.

接下来,您需要定义EntryPoints,首先,您需要在应用程序类中添加@HiltAndroidApp批注。 这将替换dagger-android的DaggerApplication类。 以此注释您的应用程序类就是在应用程序中启动成员注入所需的全部。 您不需要构建任何组件,也不需要手动调用inject方法。

You might be asking yourself, how does this work? Well, when you annotate your MyApplication class with @HiltAndroidApp the annotation, hilt will generate a class named Hilt_MyApplication which extends whatever class your application originally extends. So if you had class MyApplication : BaseApplication() the generated class will be class Hilt_MyApplication : BaseApplication(). The generated Hilt_MyApplication will then create the dagger component in its onCreate.

您可能会问自己,这如何工作? 好了,当您使用@HiltAndroidApp注释MyApplication类时, @HiltAndroidApp将生成一个名为Hilt_MyApplication的类, Hilt_MyApplication将扩展应用程序最初扩展的任何类。 因此,如果您有class MyApplication : BaseApplication()类,则生成的类将为class Hilt_MyApplication : BaseApplication() 。 然后,生成的Hilt_MyApplication将在其onCreate创建匕首组件。

The byte-code transformation that is installed using the Gradle plugin will modify the MyApplication.class file that was generated by javac to change the super class to be Hilt_MyApplication. So in code you have MyApplication extends BaseApplication but at runtime it will actually be MyApplication extends Hilt_MyApplication extends BaseApplication. This is a super clever trick that the Dagger team came up with to significantly reduce the boilerplate code required for Dagger.

使用Gradle插件安装的字节码转换将修改javac生成的MyApplication.class文件,以将超类更改为Hilt_MyApplication 。 因此,在代码中您具有MyApplication extends BaseApplication但是在运行时它将实际上是MyApplication extends Hilt_MyApplication extends BaseApplication 。 这是Dagger团队想出的一种超级巧妙的技巧,可以大大减少Dagger所需的样板代码。

Because the annotation processor will read your original super class, and make sure that the generated class has the same super class, this will give you the freedom to use any base class you would want to use for your application, activities or fragments. The generated intermediate class will also do the injection before calling the super.onCreate (or super.onAttach in the Fragment case), so if you have a super class that also has injected dependencies those will be injected before it’s onCreate is invoked.

因为注释处理器将读取您的原​​始超类,并确保生成的类具有相同的超类,所以这将使您可以自由使用要用于应用程序,活动或片段的任何基类。 生成的中间类还将在调用super.onCreate (或Fragment情况下的super.onAttach )之前进行注入,因此,如果您还具有注入依赖项的超类,则将在调用onCreate之前注入依赖项。

If you want to make Activities, Fragments, Services, BroadcastReceivers or Views (yes the last one is very cool) also compatible with hilt, all it takes is to annotate them with @AndroidEntryPoint and the same will happen where a super class will be generated for it. To provide bindings for these you can install modules into the following components, and all of these components come with a corresponding scope:

如果要使“活动”,“片段”,“服务”,“广播接收器”或“视图”(是的,最后一个很酷)也与@AndroidEntryPoint兼容,则只需使用@AndroidEntryPoint进行注释,并且将在生成超类的地方进行相同的操作为了它。 要为这些提供绑定,您可以将模块安装到以下组件中,并且所有这些组件都具有相应的作用域:

  • ActivityRetainedComponent for providing dependencies that survive orientation changes and basically have the same lifetime as a ViewModel obtained from the Activity. This component will have the ApplicationComponent as parent, so you’ll be able to depend on all bindings defined in the ApplicationComponent. When providers are annotated with the @ActivityRetainedScoped that means that you will get the same instance, even across orientation changes. I think this one will be super helpful.

    ActivityRetainedComponent,用于提供在方向变化后ViewModel生存并且基本上与从Activity获得的ViewModel具有相同生存期的依赖项。 该组件将把ApplicationComponent作为父组件,因此您将能够依赖ApplicationComponent定义的所有绑定。 如果使用@ActivityRetainedScoped注释提供@ActivityRetainedScoped ,则意味着即使方向发生变化,您也将获得相同的实例。 我认为这会很有帮助。

  • ActivityComponent for providing dependencies that require an instance of Activity. This component will have the ActivityRetainedComponent as parent, so you’ll be able to depend on anything that is available for injection in the ActivityRetainedComponent (which includes everything in the ApplicationComponent). When scoped with @ActivityScoped you will get the same instance as long as that Activity exists.

    ActivityComponent用于提供需要Activity实例的依赖项。 该组件将把ActivityRetainedComponent作为父组件,因此您将能够依赖ActivityRetainedComponent可用于注入的任何内容(包括ApplicationComponent所有内容)。 使用@ActivityScoped作用域限定时,只要该Activity存在,您将获得相同的实例。

  • FragmentComponent for providing dependencies that require an instance of the Fragment. This component will have the ActivityComponent as parent, so you’ll be able to depend on all bindings defined in the ActivityComponent, ActivityRetainedComponent or ApplicationComponent. When scoped with @FragmentScoped you will get the same instance as long as the Fragment exists.

    FragmentComponent,用于提供需要Fragment实例的依赖项。 该组件将把ActivityComponent作为父组件,因此您将能够依赖在ActivityComponentActivityRetainedComponentApplicationComponent定义的所有绑定。 当使用@FragmentScoped作用域限定时,只要Fragment存在,您将获得相同的实例。

  • ViewComponent for providing dependencies that require an instance of View. This component will have the ActivityComponent as parent, so you’ll also be able to depend on all bindings defined in the ActivityComponent, ActivityRetainedComponent or ApplicationComponent. When annotated with @ViewScoped dependencies will be the same instance in the lifetime of the View.

    ViewComponent用于提供需要View实例的依赖项。 该组件将把ActivityComponent作为父组件,因此您还可以依赖在ActivityComponentActivityRetainedComponentApplicationComponent定义的所有绑定。 当使用@ViewScoped注释时,依赖项将在View的生存@ViewScoped成为同一实例。

  • ViewWithFragmentComponent for providing dependencies that require an instance of the view, and an instance of the Fragment that the view is attached to. This subcomponent will have the FragmentComponent as parent, so you’ll be able to depend on all bindings defined in the FragmentComponent, ActivityComponent, ActivityRetainedComponent or ApplicationComponent. When annotated with @ViewScoped dependencies will be the same instance in the lifetime of the View.

    ViewWithFragmentComponent用于提供需要视图实例和视图附加到的Fragment实例的依赖项。 该子组件将FragmentComponent作为父组件,因此您将能够依赖FragmentComponentActivityComponentActivityRetainedComponentApplicationComponent定义的所有绑定。 当使用@ViewScoped注释时,依赖项将在View的生存@ViewScoped成为同一实例。

  • ServiceComponent for providing dependencies that require an instance of Service. This component will have the ApplicationComponent as parent, so you’ll be able to depend on all bindings defined in the ApplicationComponent. When annotated with @ServiceScoped dependencies will be the same instance in the lifetime of the service.

    ServiceComponent,用于提供需要Service实例的依赖项。 该组件将把ApplicationComponent作为父组件,因此您将能够依赖ApplicationComponent定义的所有绑定。 用@ServiceScoped注释时,依赖项将在服务的生命周期中是相同的实例。

For some reason there is no component definition for BroadcastReceivers, they are supported in Hilt, but you’d only be able to inject dependencies that are provided in the ApplicationComponent.

出于某种原因,没有为BroadcastReceivers定义组件,Hilt支持它们的定义,但是您只能注入ApplicationComponent中提供的依赖项。

从Dagger-android发生了什么变化? (What changed from dagger-android?)

Besides being much simpler to set up, there are a couple of things that have changed from dagger-android.

除了设置简单得多之外,dagger-android还改变了几件事。

There is only one FragmentComponent definition

只有一个FragmentComponent定义

In dagger-android you could have fragment bindings, specific to certain fragment implementations. If you included a module in a @ContributesAndroidInjector for an AccountSettingsFragment, in that module you’d be able to define providers that take the AccountSettingsFragment instance as one of the parameters. In Hilt you’ll always install modules into a generic FragmentComponent so you’d only be able to inject the Fragment instance. Note that the generated code will still contain different implementation of the FragmentComponent for each Fragment that is annotated with @AndroidEntryPoint. That generated subcomponent will only contain the providers for the dependencies that are actually required by that Fragment.

在dagger-android中,您可以具有特定于某些片段实现的片段绑定。 如果您在@ContributesAndroidInjectorAccountSettingsFragment包含一个模块,则可以在该模块中定义将AccountSettingsFragment实例作为参数之一的提供程序。 在Hilt中,您将始终将模块安装到通用FragmentComponent因此您只能注入Fragment实例。 请注意,对于使用@AndroidEntryPoint注释的每个片段,生成的代码仍将包含FragmentComponent的不同实现。 生成的子组件将仅包含该Fragment实际需要的依赖项的提供程序。

A side-effect of this is, that you will probably need to start working a lot more with qualifiers or @Named dependencies. For example, right now with dagger-android you might be binding a different LifecycleObserver for two different fragments. In the one fragment module you have:

这样做的副作用是,您可能需要使用限定符或@Named依赖项开始更多工作。 例如,现在使用Dagger-android,您可能为两个不同的片段绑定了一个不同的LifecycleObserver。 在一个片段模块中,您具有:

@Binds 
abstract LifecycleObserver observer(SettingsObserver observer)

and in another fragment module you can have:

在另一个片段模块中,您可以拥有:

@Binds
abstract LifecycleObserver observer(MenuObserver observer);

because these modules would be included in different subcomponents this would work fine. With Hilt, these would both be installed in the FragmentComponent so you would end up with a duplicate bindings error. In cases like this, the easiest solution would be to use qualifiers to bind and inject specific instances of LifecycleObserver.

因为这些模块将包含在不同的子组件中,所以可以正常工作。 使用Hilt,它们都将安装在FragmentComponent因此最终将出现重复的绑定错误。 在这种情况下,最简单的解决方案是使用限定符来绑定和注入LifecycleObserver特定实例。

Nested Fragment subcomponents

嵌套片段子组件

In dagger-android, you’d be able to make a nested graph of subcomponents for nested fragment. When you have a childFragment and a parentFragment, the component of the childFragment would be a subcomponent of the parentFragment. That would result in being able to inject anything that is provided from the parentFragment, into the childFragment.

在dagger-android中,您可以为嵌套片段制作子组件的嵌套图。 当你有一个childFragmentparentFragment的的成分childFragment将是一个子parentFragment 。 这将导致能够将parentFragment提供的任何内容注入childFragment

This functionality is not supported in Hilt. No matter how deeply nested your Fragment is, the component will always be a direct subcomponent of the ActivityComponent. This is one of the places where the limitations that Hilt imposes is a good thing, because I’ve worked with nested subcomponents before, and you can do pretty cool tricks with them, but in the end they are often very confusing.

Hilt不支持此功能。 无论您的Fragment嵌套了多深,该组件将始终是ActivityComponent的直接子组件。 在这里,Hilt施加的限制是一件好事,因为我以前使用过嵌套子组件,并且可以对它们做一些很酷的技巧,但最后它们常常令人困惑。

结论 (Conclusion)

There is still tons more to explore. Like the testing support (which looks to be a total game changer) and support for Jetpack components like ViewModel and WorkManager. Overall I am very impressed by what Hilt does, and how it is implemented. I definitely think it will be an amazing handle for Dagger and will save a lot of boilerplate. I’m very excited to start using it and can’t wait for this to reach its first stable release. .

还有更多的探索空间。 像测试支持(看起来可以改变游戏规则)和对Jetpack组件(如ViewModel和WorkManager)的支持。 总的来说,我对Hilt的功能及其实现方式印象深刻。 我绝对认为这将是Dagger的绝佳处理方式,并且可以节省很多样板。 我很高兴开始使用它,迫不及待要等到它的第一个稳定版本。 。

P.S. Only after writing this I realised that the cutting yourself in the fingers reference in in intro is not an English saying, but it is the literal translation of the Dutch version of shooting yourself in the foot.

PS仅在写完此书后,我才意识到在介绍中用手指指点自己并不是一个英语谚语,而是荷兰语版本中射击自己的脚的直译。

Photo by ARTUR KERKHOFF on Unsplash

摄影: ARTUR KERKHOFF摄Unsplash

翻译自: https://proandroiddev.com/hilt-first-impressions-31f55cf57b3f

击剑编排软件

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值