Jetpack Compose实际上与类而不是函数工作?



当你声明@Composable Function时,你实际上是在声明一个类

  • 您在该函数中声明的每个变量实际上都作为类属性

  • 你在函数中声明的每个事件(像onClick)实际上作为类方法

这就是为什么变量值在onclick之间被记住。每次点击执行类方法,增加类属性。这可以在控制台中看到,因为我们此时没有更新UI(没有发生重组)。

所以Jetpack Compose的工作方式和解释完全是误导。它解释说,你与函数工作,但在后台,他们的行为像类,以使他们有状态。因此,而不是使用众所周知的结构,如类来实现UI视图,他们使用功能与许多魔术背后的场景,使他们的行为像类的状态。我错过什么了吗?

//======================================================================
// MAIN ACTIVITY
//======================================================================
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Column {
MyComposable("Object1") //Create Object of Class MyComposable
MyComposable("Object2") //Create Object of Class MyComposable
}
}
}
}
//======================================================================
// MY COMPOSABLE
//======================================================================
// Instead of Function you are actually creating Object behind the scene
// So this Function actually works as Constructor for MyComposable Class    
@Composable
fun MyComposable(name: String) { //CLASS
//CLASS PROPERTY
var myProperty = name
//CLASS METHOD: onClick
Text(text = myProperty, modifier = Modifier.clickable(onClick = {
myProperty += " clicked"
println(myProperty)
}))
}

控制台

Object1 clicked
Object1 clicked clicked
Object2 clicked

选自《Jetpack Compose Internals》

任何被注释为@Composable的函数都会被Jetpack Compose编译器翻译成一个函数,该函数隐式地将Composer上下文的实例作为参数传递给该函数,并将该实例转发给它的Composable子函数。我们可以把它看作是一个注入的隐式参数,运行时和开发人员可以保持不可知。它看起来像这样。假设我们有以下组合来表示一个铭牌:

NamePlate.kt

@Composable
fun NamePlate(name:String,lastname:String){ 
Column(modifier = Modifier.padding(16.dp)) {
Text(text = name)
Text(text = lastname, style = MaterialTheme.typography.subtitle1)
}
}

编译器将为树上的每个可组合调用添加一个隐式的可组合参数,并在每个可组合调用的开始和结束处添加一些标记。注意,下面的代码是简化的,但结果将是这样的:

fun NamePlate(name:String,lastname:String, $composer:Composer<*>){
$composer.start(123)
Column(modifier = Modifier.padding(16.dp), $composer) {
Text(text = name, $composer            
Text(text = lastname, style = MaterialTheme.typography.subtitle1, $composer)
$composer.end()
}
}

在你展示的例子中,你基本上是使用一个闭包,通过引用它的外部上下文来保持本地状态。

你不需要Compose来实现这一点,香草Kotlin(或Java)的行为是这样的:

fun main() {
val createCounter = {
var x = 0
{
x += 1
"x=$x"
}
}
val nextValue = createCounter()
print(nextValue()) // x=1
print(nextValue()) // x=2
print(nextValue()) // x=3
}

虽然语言允许这样做,但实际上违反了Compose的先决条件之一,即函数应该是纯的,或者至少没有无法控制的副作用。

Compose函数的作用是类似于访问者模式,其中编写器沿着树向下传递,每个函数向该编写器发送。由于没有副作用,组合编译器可以假设,如果形参的值没有改变,那么函数发出的内容也不会改变,因此不需要重新组合(重新运行函数)。

最新更新