前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在 Android 和 Hilt 中限定作用域

在 Android 和 Hilt 中限定作用域

作者头像
Android 开发者
发布2020-11-16 17:31:34
1.4K0
发布2020-11-16 17:31:34
举报
文章被收录于专栏:Android 开发者Android 开发者

将对象 A 的作用域限定到对象 B,指的是对象 B 的整个生命周期内始终持有相同的 A 实例。当涉及到 DI (依赖项注入) 时,限定对象 A 的作用域为一个容器,则意味着该容器在销毁之前始终提供相同的 A 实例。

在 Hilt 中,您可以通过注解将类型的作用域限定在某些容器或组件内。例如,您的应用中有一个处理登录和注销的?UserManager?类型。您可以使用?@Singleton?注解将该类型的作用域限定为?ApplicationComponent?(ApplicationComponent?是一个被整个应用的生命周期管理的容器)。被限定作用域的类型在应用组件中沿 组件层次结构 向下传递: 在本案例中,相同的?UserManager?实例将被提供给层次结构内其余的 Hilt 组件。应用中任何依赖于?UserManager?的类型都将获得相同的实例。

注意 :?默认情况下,Hilt 中的绑定都 未限定作用域 。这些绑定不属于任何组件,并且可以在整个项目中被访问。每次被请求都会提供该类型的不同实例。当您将绑定的作用域限定为某个组件时,它会限制您使用该绑定的范围以及该类型可以具有的依赖项。

在 Android 中,您不使用 DI 库也可以通过 Android Framework 来手动限定作用域。让我们看看如何手动限定作用域,以及如何改用 Hilt 来限定作用域。最后,我们将比较使用 Android Framework 手动限定作用域和使用 Hilt 限定作用域的区别。

在 Android 中限定作用域

看了上文的定义,您可能会有这样的异议:?在某个特定类中使用一个类型的实例变量也可以做到限定该变量类型的作用域。没错!不使用 DI 时,您可以执行如下操作:

代码语言:javascript
复制
class ExampleActivity : AppCompatActivity() {

  private val analyticsAdapter = AnalyticsAdapter()
  ...

}
复制代码

analyticsAdapter?变量的作用域被限定为?MyActivity?的生命周期,这意味着只要 Activity 没有被销毁,该变量就是同一个实例。如果另一个类出于某种原因需要访问这个被限定了作用域的变量,每次访问也会获得相同实例。当新的?MyActivity?实例被创建时 (如系统设置改变),一个新的?AnalyticsAdapter?实例将会被创建。

使用 Hilt,等效代码如下:

代码语言:javascript
复制
@ActivityScoped
class AnalyticsAdapter @Inject constructor() { ... }

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

 @Inject lateinit var analyticsAdapter: AnalyticsAdapter

}
复制代码

每次创建的?MyActivity?都会持有一个?ActivityComponent?DI 容器的新实例,在 Activity 被销毁之前,该实例将向 组件层次结构 下的依赖项提供相同的?AnalyticsAdapter?实例。

更改系统设置后,您将获得一个新的 AnalyticsAdapter 和 MainActivity 实例

通过 ViewModel 限定作用域

然而,我们可能希望?AnalyticsAdapter?可以在系统设置更改后留存!或者说,我们希望直到用户离开 Activity 之前,都限定该实例的作用域为 Activity。

为此,您可以使用 组件架构中的 ViewModel,因为它可以在系统设置更改后留存。

不使用依赖项注入时,您可能有如下代码:

代码语言:javascript
复制
class AnalyticsAdapter() { ... }

class ExampleViewModel() : ViewModel() {
  val analyticsAdapter = AnalyticsAdapter()
}

class ExampleActivity : AppCompatActivity() {

  private val viewModel: ExampleViewModel by viewModels()
  private val analyticsAdapter = viewModel.analyticsAdapter

}
复制代码

通过这种方式,您将?AnalyticsAdapter?的作用域限定为 ViewModel。因为 Activity 具有 ViewModel 的访问权限,所以在该 Activity 中可以始终获得相同的?AnalyticsAdapter?实例。

通过使用 Hilt,您可以通过限定?AnalyticsAdapter?的作用域为?ActivityRetainedComponent?来实现相同的行为,因为?ActivityRetainedComponent?也可以在系统设置更改后留存。

代码语言:javascript
复制
@ActivityRetainedScoped

class AnalyticsAdapter @Inject constructor() { ... }

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

@Inject lateinit var analyticsAdapter: AnalyticsAdapter

}
复制代码

通过使用 ViewModel 或者 Hilt 中的 ActivityRetainedScope 注解,您可以在系统设置更改后获得相同的实例

如果您希望在遵循良好的 DI 实践的同时,保留 ViewModel 用于处理视图逻辑,您可以使用 @ViewModelInject 提供 ViewModel 的依赖项,该注解的详细描述请参见:?文档 | 使用 Hilt 注入 ViewModel 对象。这样一来,AnalyticsAdapter?的作用域就无需被限定为?ActivityRetainedComponent,因为此时它的作用域被手动限定为 ViewModel:

代码语言:javascript
复制
class AnalyticsAdapter @Inject constructor() { ... }

class ExampleViewModel @ViewModelInject constructor(
  private val analyticsAdapter: AnalyticsAdapter
) : ViewModel() { ... }

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  private val viewModel: ExampleViewModel by viewModels()
  private val analyticsAdapter = viewModel.analyticsAdapter

}
复制代码

我们刚才所看到的内容,可以应用到任何由 Android Framework 生命周期类管理的 Hilt 组件中。点击查看 全部可用作用域。回到我们最初的示例,将作用域限定为?ApplicationComponent,等同于不使用 DI 框架时在 Application 类中持有该实例。

对比 Hilt 及 ViewModel 限定作用域

使用 Hilt 限定作用域,优势为您可在 Hilt 组件层次结构中使用被限定的类型;而对于 ViewModel,则必须通过 ViewModel 手动访问被限定作用域的类型。

使用 ViewModel 限定作用域,优势为您可以在应用中任何?LifecyclerOwner?对象中持有 ViewModel。例如,如果您使用了?Jetpack Navigation 库,则可以将 ViewModel 绑定到?NavGraph 上。

Hilt 提供的作用域数量有限。可能没有符合您特定使用场景的作用域。例如嵌套 Fragment,对于这种情况,您可以退一步使用 ViewModel 限定作用域。

使用 Hilt 注入 ViewModel

如上文所述,您可以使用?@ViewModelInject?向 ViewModel 注入依赖项。其原理是这些绑定关系保存在?ActivityRetainedComponent?中,这也是为什么您只能注入未限定作用域的类型,或者是限定作用域为?ActivityRetainedComponent?以及?ApplicationComponent?的类型。

如果 Activity 或 Fragment 被?@AndroidEntryPoint?注解修饰,就可以通过?getDefaultViewModelProviderFactory() 方法获取 Hilt 生成的 ViewModel 工厂了。由于可以在?ViewModelProvider?中使用这些 ViewModel 工厂,使您获取 ViewModel 的方式变得更加灵活。例如: 将作用域限定为?BackStackEntry?的 ViewModel。

限定作用域会有一些代价,因为提供的对象在持有者被销毁之前将一直保留在内存中。请在应用中慎重地考虑使用限定作用域的对象。如果对象的内部状态要求使用同一实例,对象需要同步,或者对象的创建成本很高,那么限定作用域是恰当的做法。

当然,当您需要限定作用域时,您可以使用 Hilt 中的作用域注解,也可以直接使用 Android Framework。

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 在 Android 中限定作用域
  • 通过 ViewModel 限定作用域
  • 对比 Hilt 及 ViewModel 限定作用域
  • 使用 Hilt 注入 ViewModel
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com