美文网首页Android开发者
在 Android 和 Hilt 中限定作用域

在 Android 和 Hilt 中限定作用域

作者: 谷歌开发者 | 来源:发表于2020-11-14 15:07 被阅读0次
image

将对象 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 时,您可以执行如下操作:

class ExampleActivity : AppCompatActivity() {

  private val analyticsAdapter = AnalyticsAdapter()
  ...

}

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

使用 Hilt,等效代码如下:

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

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

 @Inject lateinit var analyticsAdapter: AnalyticsAdapter

}

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

image

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

通过 ViewModel 限定作用域

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

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

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

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 也可以在系统设置更改后留存。

@ActivityRetainedScoped

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

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

@Inject lateinit var analyticsAdapter: AnalyticsAdapter

}
image

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

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

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。

相关文章

  • 在 Android 和 Hilt 中限定作用域

    将对象 A 的作用域限定到对象 B,指的是对象 B 的整个生命周期内始终持有相同的 A 实例。当涉及到 DI (依...

  • 作用域与作用域链

    作用域 通常代码中变量和函数并不总是可用的,而限定他们可用范围就被称作为作用域。作用域被分为全局作用域和局部作用域...

  • 微信小程序 + vant + westore + amap

    Shadow DOM 官方文档限定作用域css,在组件中隐藏DOM树。

  • 什么是作用域和作用域链,有什么作用

    作用域(scope) 一段代码中的函数和变量并不总是可用的,限定其可用性的范围叫做作用域,作用域的使用提高了程序逻...

  • var 和 let 的区别

    js的作用域只有全局作用域和函数作用域,为了能在{ }来限定变量,所以使用了let来限制变量只能在块作用域中有效 ...

  • 作用域与作用域链

    一、作用域 在 Javascript 中,作用域分为 全局作用域 和 函数作用域全局作用域:代码在程序的任何地方都...

  • js高级知识点(第五天)

    所用域 什么是作用域 变量起作用的范围 什么是块级作用域,使用代码块限定的作用域是块级作用域(let声明的变量是块...

  • ES6学习-块级作用域、let和const

    一、作用域  在以前的ES5中,作用域只有全局作用域和函数作用域,没有块级作用域。 在函数作用域或全局作用域中通过...

  • iOS: {}在OC中的几种常用写法

    1、 在switch中的写法, 用来确定变量的作用域 2、 限定相同变量名的作用域 3、创建控件的一种方法, 老项...

  • 如何让代码看起来有万佛朝宗的气势

    1.使用全限定作用域 使用全限定作用域有如下功效: 1.后来者被限定符吓到,料想代码写的如此严谨,这哥们必是大神 ...

网友评论

    本文标题:在 Android 和 Hilt 中限定作用域

    本文链接:https://www.haomeiwen.com/subject/gccwbktx.html