赞
踩
如果我们只专注于简单的语法,任何标准的Kotlin函数都可以成为一个可组合函数,只需将其注解为@Composable
:
通过这样做,我们实际上是在告诉编译器,该函数打算将一些数据转换为一个Node节点,以便注册到可组合树中。也就是说,如果我们将可组合函数看成是 @Composable (Input) -> Unit
,输入是数据,但输出却不是大多数人认为的函数返回的值,而是一个将元素插入树中的注册动作。我们可以认为这是一种函数执行带来的副作用。
这里所谓的 “注册动作” 在 Compose 中通常称为 “emitting”(发射)。emit 动作是在可组合函数被执行时进行的,这发生在组合过程中。
执行Composable函数的唯一目的是构建或更新树的内存表示状态。这将使它始终与其所代表的树结构保持同步更新状态,因为可组合函数会在其读取的数据每次发生变化时重新执行。为了保持和树的同步更新状态,它们可以发出插入新节点的操作(如上所述),但同样也可以删除、替换或移动节点。可组合函数还可以从树中读取或向树中写入状态。
将函数注解为Composable
还有其他相关含义。@Composable
注解有效地更改了它所应用的函数或表达式的类型,并且与任何其他类型一样,它对其施加了一些约束或属性。这些属性与Jetpack Compose非常的紧密相关,因为它们可以解锁Compose库的相关功能。
Compose runtime
期望Composable
函数遵守上述属性,因此它可以假定某些行为,并利用不同的运行时优化,如并行组合、基于优先级的任意组合顺序、智能重组或位置记忆等等。
一般来说,只有当运行时对需要运行的代码有一定的确定性时,才可能进行运行时优化,因此它可以从中假设特定的条件和行为。这解锁了执行的时机,或者换句话说,利用上述确定性来 “消费” 这段代码,以便遵循不同的执行策略或评估技术。
这些确定性的一个例子是代码中不同元素之间的关系。他们是否相互依赖? 我们可以在不影响程序的情况下,并行或以不同的顺序运行它们吗?我们能把每个原子逻辑片段解释为一个完全孤立的单元吗?
可组合函数的大多数属性是由Compose编译器启用的。因为它是一个Kotlin编译器插件,所以它在正常的编译器阶段运行,并且可以访问到所有的Kotlin编译器可以访问的信息。这允许它拦截和转换来自我们所有可组合函数的 IR (intermediate representation,中间产物表示),以便添加一些额外的信息。
其中,Compose编译器 为每一个 Composable
函数都会新增的一个东西就是在参数列表的末尾附加了一个 Composer
参数。这个参数是隐式的,也就是说开发者在编写代码时并不会感知到这一点。它的实例是在运行时被注入的,并被转发给所有的子Composable
调用,因此它可以从树的所有级别中被访问到。
假设我们有如下代码:
那么Compose编译器会将其翻译成下面这样:
我们可以看到,Composer
被转发到了body内的所有Composable
调用。在此基础上,Compose编译器对可组合函数施加了严格的规则:它们只能从其他可组合函数调用。因为这实际是需要调用上下文,它确保树只由可组合函数组成,以便Composer
可以被向下转发。
Composer
是我们作为开发者编写的可组合代码与Compose runtime
之间的连接桥梁。可组合函数将使用它来发射对树的更改操作,从而通知 Compose runtime
树的形状,以便构建或更新其内存表示状态。
可组合函数相对于它们生成的节点树是幂等的。也就是说:使用相同的输入参数多次重新执行一个Composable函数应该会得到相同的树。Jetpack Compose运行时依赖于这个假设来进行重新组合之类的事情。
在Jetpack Compose中,重组是在可组合函数的输入发生变化时重新执行的动作,这样它们就可以发射更新后的信息并更新树。 Compose runtime
必须能够在任意时间,根据各种原因重新组合可组合函数。
重组过程将遍历整个树,检查哪些节点需要重新组合(重复执行)。只有具有输入变化的节点将重新组
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。