Jetpack Compose是一个现代声明式工具包,用于在Kotlin中构建Android界面。开发人员通过composable函数描述UI,工具包会自动仅重绘已更改的部分。根据Android Developers (2026),Jetpack Compose可在Android 5.0(API 21)及以上版本运行,支持Material Design 3,并凭借其自身的Recomposition系统——一种仅更新已更改小部件的智能diff算法——在中端设备上达到120 FPS。
要点
Jetpack Compose——是Google推出的用于构建Android用户界面的声明式框架,于2019年发布,2021年达到稳定版本。与旧的View System(XML + Activity/Fragment)不同,Compose使用带注解的Kotlin函数——@Composable。界面完全用Kotlin描述:XML和代码之间没有分离。这消除了与XML和Kotlin中id不匹配相关的一类错误(type-safe synthetic在重构时无济于事)。
Compose构建在自有渲染系统——Canvas之上,不绑定到View层次结构。每个Composable直接在Canvas上绘制,绕过View System的onMeasure/onDraw。这在复杂屏幕上带来了性能提升:在Google的测试(2023)中,包含200个元素的Compose屏幕比RecyclerView + ViewHolder上的类似屏幕快40%。
Compose工作需要minSdk 21(Android 5.0)和Kotlin 1.9+。Compose BOM(Bill of Materials)同步所有Compose库的版本。该框架与View System上的现有代码兼容:Compose通过ComposeView嵌入XML,旧View通过AndroidView嵌入Compose层次结构。根据Google Play Console(2025)的数据,Android 5.0+覆盖了97%的活跃设备,因此兼容性对大多数项目来说不是限制。
@Composable——是一个注解,将普通的Kotlin函数转换为UI构建块。Composable函数描述界面片段——文本、按钮、列表——的外观。该函数不返回值,而是将UI组件发射到组合中。这类似于生成器:每个函数在调用时向屏幕添加元素。
@Composable
fun ProfileCard(name: String, avatarUrl: String) {
Card(
modifier = Modifier.fillMaxWidth().padding(16.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
AsyncImage(
model = avatarUrl,
contentDescription = "头像",
modifier = Modifier.size(48.dp).clip(CircleShape)
)
Spacer(Modifier.width(12.dp))
Text(
text = name,
style = MaterialTheme.typography.titleMedium
)
}
}
}
ProfileCard函数接收参数(name, avatarUrl)并发射Card → Row → AsyncImage + Text。组合——是一次传递中已发射组件的树。如果参数没有更改,Compose会跳过函数调用(recomposition skip)。如果仅name发生更改,则仅调用Text,其他元素不会重绘。这种intelligent recomposition——是Compose相对于View System手动优化的关键性能优势。
Composable函数积极使用slot——trailing lambda,content: @Composable (() -> Unit)。这允许创建容器:Card、Column、Row接收content lambda,内容嵌入到slot位置。Slot API取代了android:layout_gravity等XML属性——现在子元素的位置由content块内的Kotlin代码确定。
状态在Compose中——是任何可以随时间变化的值。当状态更改时,Compose会为读取此状态的所有组件计划recomposition。该机制类似于React hooks:mutableStateOf返回MutableState,读取.value会自动将当前组合订阅到更改。
@Composable
fun CounterExample() {
var count by remember { mutableStateOf(0) }
Column(modifier = Modifier.padding(16.dp)) {
Text("已按下:$count")
Button(onClick = { count++ }) {
Text("增加")
}
}
}
@Composable
fun UserScreen(viewModel: UserViewModel) {
val userName by viewModel.userName.collectAsState()
Text("用户:$userName")
}
remember在重新组合之间保持值——否则mutableStateOf会在每次UI更新时重新创建。collectAsState()将ViewModel中的StateFlow转换为Compose兼容的状态。建议——对于屏幕状态使用ViewModel与StateFlow,对于局部状态(例如展开的卡片)使用mutableStateOf。这种分离符合"智能/笨拙"组件的原则。
State Hoisting——将状态从子组件提升到父组件的模式。父组件通过参数传递值和回调,子组件在更改时调用回调。父组件存储mutableStateOf,子组件仅存储参数。这使得组件可重用和可测试:同一个TextField可以与任何数据源一起使用。
Modifier——是描述Composable变换的对象:大小、间距、背景、点击处理、动画、滚动。修饰符通过调用链应用:Modifier.fillMaxWidth().padding(16.dp).background(Color.Blue).clickable { }。每次调用返回一个带有添加属性的新Modifier——不改变原始对象。
修饰符的顺序很重要。Modifier.padding(16.dp).background(Color.Blue)绘制带间距的区域。Modifier.background(Color.Blue).padding(16.dp)绘制内部矩形,间距保持透明。机制类似于CSS box模型:padding → background像margin + background一样工作;background → padding——像内部background + padding。开发人员只需记住:padding先 = 外部间距,padding后 = 内部。
如果内置修饰符不够,可以通过Modifier.composed { ... }或Modifier.then()创建自定义修饰符。在自定义修饰符内部,可以使用布局测量(Modifier.layout { measurable, constraints -> ... })、绘制(Modifier.drawWithContent { ... })、手势(Modifier.pointerInput { ... })。示例:用于按下时脉冲动画的修饰符——测量尺寸,点击时通过animateFloatAsState启动缩放动画。
对于动画,Compose提供animate*AsState(animateFloatAsState、animateColorAsState、animateDpAsState)——值在更改时在旧状态和新状态之间进行动画。如果需要显示/消失动画——AnimatedVisibility和AnimatedContent带有内置过渡(fade、slide、expand)。所有动画在graphics layer上工作,不会导致不必要的组合。
Composable函数不应直接执行副作用(网络请求、定时器、订阅)——它们在每次重新组合时被调用,这会导致请求重复。对于副作用,Compose提供Effect函数家族:LaunchedEffect在进入组合时启动协程并在退出时取消,DisposableEffect——用于需要显式清理的资源(传感器、BroadcastReceiver)。
@Composable
fun SensorReader() {
val context = LocalContext.current
var sensorValue by remember { mutableStateOf(0f) }
DisposableEffect(Unit) {
val sensor = registerSensorListener(context) { value ->
sensorValue = value
}
onDispose {
unregisterSensorListener(sensor)
}
}
Text("值:$sensorValue")
}
@Composable
fun UserGreeting(userId: String) {
LaunchedEffect(userId) {
val profile = api.fetchProfile(userId)
// 状态更新
}
}
LaunchedEffect(userId)如果userId更改则重新启动——先前的协程被取消,新的协程以新的userId启动。这消除了手动管理请求取消的问题。DisposableEffect(Unit)——具有固定键Unit的效果,在进入组合时触发,在退出时调用onDispose。SensorReader注册监听器并在离开屏幕时取消订阅——无内存泄漏风险。
如果协程需要不是在进入组合时启动,而是在事件(按钮按下)时启动,则使用rememberCoroutineScope()。它返回绑定到Composable生命周期的CoroutineScope,不需要DisposableEffect。示例:在点击按钮时启动网络请求——scope.launch { viewModel.loadData() }。
在Compose和View System之间选择——是2026年Android开发人员的主要架构问题。两种技术都得到Google的支持,但Compose是Google投入资源的主要方向。View System仅接收关键修复,不再发展。差异体现在语法、状态管理、性能和开发时间上。
| 方面 | Jetpack Compose | View System |
|---|---|---|
| UI描述 | Kotlin @Composable函数 | XML + Activity/Fragment |
| 状态 | mutableStateOf、StateFlow、自动重绘 | findViewById,手动:setText、notifyDataSetChanged |
| 性能 | Intelligent recomposition、Canvas渲染 | View层次结构、measure/layout/draw |
| 动画 | animate*AsState、AnimatedVisibility、内置 | ValueAnimator、ObjectAnimator、Transition |
| 兼容性 | minSdk 21、ComposeView/AndroidView桥接 | 所有版本 |
| APK大小 | Compose增加3-5 MB | 无额外开销 |
对于新项目,Google推荐Jetpack Compose作为UI开发标准。View System保留用于支持2021年之前编写的代码以及需要最小APK大小的情况(例如,针对入门级设备的新兴市场)。Compose凭借声明式语法和内置动画,将UI代码量比View System减少30-50%。
常见问题
可以,通过XML中的ComposeView。添加对Compose的依赖,并将屏幕或其部分包装在ComposeView { MyComposable() }中。迁移逐个屏幕进行。
原因——状态提升过高或使用了可变对象。修复:对于派生数据使用derivedStateOf,对于稳定引用使用remember。
使用LazyColumn(类似于RecyclerView)。元素在滚动时创建和重用。对于具有不同单元格类型的复杂列表——LazyColumn { items(items, key = { it.id }) { ... } }。
不需要,可以直接从Compose开始。了解View System有助于维护旧代码,但Compose是一个独立的生态系统,拥有自己的文档和模式。
支持,Material 3——是自2023年以来Compose的默认主题。通过implementation(“androidx.compose.material3:material3”)连接。Material 2被视为已过时。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。