在Android和Hilt中进行作用域

Scoping an object A to another object B means that throughout the lifecycle of B, it’ll always have the same instance of A. When it comes to dependency injection (DI), an object A scoped to a container means that the container will always provide the same instance of A until the container is destroyed.

范围界定对象A到另一个对象B手段,在整个生命周期B ,它永远具有相同的实例A 。 当涉及依赖项注入(DI)时,范围为容器的对象A意味着该容器将始终提供相同的A实例,直到该容器被销毁为止。

In Hilt, you can scope types to containers or components using annotations. For example, let’s say your app has a UserManager type that handles logins and logouts. You could scope this type to the ApplicationComponent (which is a container managed by the application’s lifecycle) using the @Singleton annotation. The scoped types in the application component flow down the component hierarchy: in the example, the same instance of UserManager will be provided to the rest of the Hilt components in the hierarchy. Any type in the app that depends on UserManager will receive the same instance.

在“抬头”中,您可以使用批注将类型范围限定为容器或组件。 例如,假设您的应用程序具有UserManager类型,用于处理登录和注销。 您可以使用@Singleton批注将这种类型的作用域限定为ApplicationComponent (由应用程序的生命周期管理的容器)。 应用程序组件中的作用域类型沿组件层次结构向下流动:在示例中,将向层次结构中的其余Hilt组件提供UserManager的相同实例。 应用程序中依赖UserManager任何类型都将接收相同的实例。

Note: By default, bindings in Hilt are unscoped. They are not part of any component and they can be accessed throughout the entire project. A different instance of that type will be provided every time it is requested. When you scope a binding to a component, it limits where that binding can be used and which dependencies the type can have.

注意:默认情况下,Hilt中的绑定是未作用域的 。 它们不是任何组件的一部分,并且可以在整个项目中进行访问。 每次请求时都会提供该类型的不同实例。 当您将绑定范围限定到组件时,它会限制可在何处使用该绑定以及类型可以具有的依赖项。

In Android, you can do scoping manually without a DI library by using the Android framework. Let’s see how you can do that and how that maps to scoping with Hilt. By the end, we’ll compare the differences between scoping manually with the Android framework and scoping with Hilt.

在Android中,您可以使用Android框架在没有DI库的情况下手动进行范围界定。 让我们看看如何做到这一点,以及如何将其映射到Hilt的作用域。 最后,我们将比较使用Android框架手动进行范围界定和使用Hilt进行范围界定之间的区别。

Android中的作用域 (Scoping in Android)

With the above definition, you could argue that you can scope a type by using an instance variable of that type in a specific class, and that’s true! Without DI, you could do something like this:

通过上面的定义,您可能会争辩说,可以通过在特定类中使用该类型的实例变量来限定类型的范围,这是事实! 没有DI,您可以执行以下操作:

class ExampleActivity : AppCompatActivity() {
private val analyticsAdapter = AnalyticsAdapter()
...
}

The analyticsAdapter variable is scoped to ExampleActivity’s lifecycle, which means it’ll be the same instance as long as this activity isn’t destroyed. If another class needs to access this scoped variable for some reason, they’ll get the same instance every time too. When a new instance of ExampleActivity is created (e.g. the activity goes through a configuration change), a new instance of AnalyticsAdapter will be created.

analyticsAdapter变量的作用范围为ExampleActivity的生命周期,这意味着只要不破坏此活动,它将是同一实例。 如果另一个类由于某种原因需要访问此范围变量,则它们每次也会获得相同的实例。 当创建ExampleActivity的新实例时(例如,活动进行了配置更改),将创建AnalyticsAdapter的新实例。

With Hilt, the equivalent code is:

使用Hilt,等效代码为:

@ActivityScoped
class AnalyticsAdapter @Inject constructor() { ... }@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { @Inject lateinit var analyticsAdapter: AnalyticsAdapter}

Every time ExampleActivity is created, it’ll hold a new instance of the ActivityComponent DI container that will provide the same instance of AnalyticsAdapter to dependencies below it in the component hierarchy until the activity is destroyed.

每次创建ExampleActivity ,它将保存一个ActivityComponent DI容器的实例,该实例将向组件层次结构中位于其下方的依赖项提供相同的AnalyticsAdapter实例,直到该活动被销毁。

Image for post
You get a new instance of AnalyticsAdapter and MainActivity after a configuration change 配置更改后,您将获得一个新的AnalyticsAdapter和MainActivity实例

使用ViewModel进行作用域 (Scoping with ViewModel)

However, we might want AnalyticsAdapter to survive configuration changes! We could say that we want to scope that instance to the activity until the user moves away from it.

但是,我们可能希望AnalyticsAdapter保留配置更改! 可以说,我们希望将该实例的作用域限定为活动,直到用户离开活动为止。

For that, you can use an Architecture Components ViewModel because it survives configuration changes.

为此,您可以使用架构组件ViewModel,因为它可以保留配置更改。

Without dependency injection, you could have this code:

如果没有依赖项注入,您可能具有以下代码:

class AnalyticsAdapter() { ... }class ExampleViewModel() : ViewModel() {
val analyticsAdapter = AnalyticsAdapter()
}class ExampleActivity : AppCompatActivity() { private val viewModel: ExampleViewModel by viewModels()
private val analyticsAdapter = viewModel.analyticsAdapter}

And in this way, you scope the AnalyticsAdapter to the ViewModel. As the activity has access to the ViewModel, it can always grab the same instance of AnalyticsAdapter.

通过这种方式,您可以将AnalyticsAdapter范围限定为ViewModel。 由于活动具有对ViewModel的访问权,因此它始终可以获取同一AnalyticsAdapter实例。

With Hilt, you could achieve the same behavior by scoping AnalyticsAdapter to the ActivityRetainedComponent which also survives configuration changes:

使用Hilt,您可以通过将AnalyticsAdapter范围限定到ActivityRetainedComponent来实现相同的行为,该行为也可以保留配置更改:

@ActivityRetainedScopedclass AnalyticsAdapter @Inject constructor() { ... }@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { @Inject lateinit var analyticsAdapter: AnalyticsAdapter}
Image for post
You get the same instance of AnalyticsAdapter after a configuration change using ViewModel or Hilt’s ActivityRetainedScope annotation 使用ViewModel或Hilt的ActivityRetainedScope注释进行配置更改后,您将获得相同的AnalyticsAdapter实例

If you still want to keep the ViewModel because it needs to perform some view logic while complying with good DI practices, you can use Hilt to provide the ViewModel dependencies using @ViewModelInject as specified in the docs. This time, AnalyticsAdapter doesn’t need to be scoped to ActivityRetainedComponent because it is now manually scoped to the ViewModel:

如果你仍想保留视图模型,因为它需要同时具有良好的DI做法符合执行一些观点的逻辑,你可以用剑柄提供视图模型依赖关系使用@ViewModelInject在指定的文档 。 这次, AnalyticsAdapter不需要限定为ActivityRetainedComponent因为现在它已手动限定为ViewModel:

class AnalyticsAdapter @Inject constructor() { ... }class ExampleViewModel @ViewModelInject constructor(
val analyticsAdapter: AnalyticsAdapter
) : ViewModel() { ... }@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { private val viewModel: ExampleViewModel by viewModels()
private val analyticsAdapter = viewModel.analyticsAdapter}

What we’ve seen so far can be applied to all Hilt components that are managed by Android framework lifecycle classes; see the full list of scopes available here. Circling back to our initial example, scoping to the ApplicationComponent is the same as having an instance of that type in the application class without using DI principles.

到目前为止,我们已经看到的内容可以应用于由Android框架生命周期类管理的所有Hilt组件; 在此处查看可用范围完整列表 。 回到我们的初始示例,对ApplicationComponent范围界定与在不使用DI原理的情况下在应用程序类中具有该类型的实例相同。

Hilt与ViewModel的作用域 (Scoping with Hilt vs ViewModel)

The advantage of scoping with Hilt is that scoped types are available in the Hilt component hierarchy whereas with ViewModel, you have to manually access the scoped types from the ViewModel.

使用Hilt进行作用域的优势在于,在Hilt组件层次结构中可以使用范围类型,而对于ViewModel,则必须从ViewModel手动访问范围类型。

The advantage of scoping with ViewModel is that you can have ViewModels for any LifecycleOwner objects in your application. For example, if you use the Jetpack Navigation library, you can have a ViewModel attached to your NavGraph.

使用ViewModel进行范围界定的优势在于,您可以为应用程序中的任何LifecycleOwner对象提供ViewModels。 例如,如果您使用Jetpack导航库 ,则可以将ViewModel附加到NavGraph

Hilt provides a limited number of scopes. You might find that you don’t have a scope for your particular use case — for example, when using nested fragments. For that case, you can fall back to scoping using ViewModel.

Hilt提供了有限的范围。 您可能会发现自己没有针对特定用例的范围,例如,在使用嵌套片段时。 对于这种情况,您可以使用ViewModel进行范围界定。

插入带有视图的ViewModel (Injecting ViewModels with Hilt)

As seen above, you can use @ViewModelInject to inject dependencies into ViewModels. Under the hood, these bindings are kept in the ActivityRetainedComponent, which is why you can only inject types that are either unscoped, or scoped to ActivityRetainedComponent or ApplicationComponent.

如上所示,您可以使用@ViewModelInject将依赖@ViewModelInject注入到ViewModels中。 在内部,这些绑定保留在ActivityRetainedComponent ,这就是为什么您只能注入未限制范围或作用域为ActivityRetainedComponentApplicationComponent

The ViewModel factory generated by Hilt is available in the getDefaultViewModelProviderFactory() method of @AndroidEntryPoint-annotated activities and fragments. That gives you more flexibility as you can use it in ViewModelProvider to obtain other ViewModels, e.g. those scoped to a BackStackEntry.

由Hilt生成的ViewModel工厂可在@AndroidEntryPoint活动和片段的getDefaultViewModelProviderFactory()方法中@AndroidEntryPoint 。 这可以为您提供更大的灵活性,因为您可以在ViewModelProvider使用它来获取其他ViewModel,例如,范围为BackStackEntry ViewModel。

Scoping can be costly because the provided object stays in memory until the holder is destroyed. Be thoughtful about the use of scoped objects in your application. It is appropriate to scope objects with an internal state that requires the same instance to be used, objects that need synchronization, or objects that you have measured to be expensive to create.

由于提供的对象会一直保留在内存中,直到支架被销毁为止,作用域设置可能会很昂贵。 考虑在应用程序中使用范围对象。 范围内对象的内部状态需要使用相同的实例,需要同步的对象,或者您认为创建起来昂贵的对象是合适的。

However, when you do need to scope, you can use Hilt’s scoping annotations or the Android framework directly.

但是,当需要确定范围时,可以直接使用Hilt的作用域注释或Android框架。

翻译自: https://medium.com/androiddevelopers/scoping-in-android-and-hilt-c2e5222317c0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值