产品资讯

Jetpack Compose 2025 年 12 月版的最新动态

阅读用时:6 分钟
Nick Butcher
产品经理

今天,Jetpack Compose 2025 年 12 月版已成为稳定版。此版本包含核心 Compose 模块的 1.10 版和 Material 3 的 1.4 版(请参阅完整的 BOM 映射),并添加了新功能和重大性能改进。

如需使用今天的版本,请将 Compose BOM 版本升级到 2025.12.00

implementation(platform("androidx.compose:compose-bom:2025.12.00"))

性能改进

我们知道,应用的运行时性能对您和您的用户来说至关重要,因此性能一直是 Compose 团队的首要任务。此版本带来了多项改进,您只需升级到最新版本即可获得所有这些改进。我们的内部滚动基准比较结果显示,Compose 现在可实现与使用 View 相同的性能:

janky.png

滚动性能基准比较了不同版本的 Compose 中 View 和 Jetpack Compose 的性能

延迟预提取中的可暂停组合

现在,延迟预提取中的可暂停组合已默认处于启用状态。这是对 Compose 运行时调度工作方式的根本性更改,旨在显著减少繁重界面工作负载期间的卡顿。

以前,一旦组合开始,就必须运行到完成。如果合成较为复杂,则可能会阻塞主线程的时间超过单个帧,从而导致界面冻结。借助可暂停的组合,如果运行时时间不足,现在可以“暂停”其工作,并在下一帧中恢复工作。与延迟布局预取搭配使用时,这种做法尤其有效,可提前准备好帧。Compose 1.9 中引入的延迟布局 CacheWindow API 是一种预提取更多内容的好方法,可利用可暂停的组合来获得更流畅的界面性能。

pausable.gif

可暂停组合与延迟预提取相结合,有助于减少卡顿

我们还优化了其他方面的性能,改进了 Modifier.onPlacedModifier.onVisibilityChanged 和其他修饰符实现。我们会继续投入资源来提升 Compose 的性能。

新功能

保留

Compose 提供了许多 API 来在不同生命周期中保持和管理状态;例如,remember 可在重组期间保持状态,而 rememberSavable/rememberSerializable 可在重新创建 activity 或进程期间保持状态。retain 是介于这两个 API 之间的新 API,可让您在配置更改期间持久保留值,而无需序列化,但无法在进程终止后保留值。由于 retain 不会序列化您的状态,因此您可以持久保存 lambda 表达式、flow 和位图等无法轻松序列化的对象。例如,您可以使用 retain 管理媒体播放器(例如 ExoPlayer),以确保媒体播放不会因配置更改而中断。

@Composable

fun MediaPlayer() {

    val applicationContext = LocalContext.current.applicationContext

    val exoPlayer = retain { ExoPlayer.Builder(applicationContext).apply { ... }.build() }

    ...

}

我们要感谢 AndroidDev 社区(尤其是 Circuit 团队),他们对这项功能的设计产生了影响并做出了贡献。

Material 1.4

material3 库的 1.4.0 版新增了许多组件并进行了多项改进:

centered-hero-carousel.webp

横向居中核心轮播内容

请注意,Material 3 Expressive API 将继续在 material3 库的 Alpha 版中开发。如需了解详情,请观看以下最近的讲座:

新的动画功能

我们会继续扩展动画 API,包括更新用于自定义共享元素动画的 API。

动态共享元素

默认情况下,sharedElement()sharedBounds() 动画会尝试为

每当在目标状态中找到匹配的键时,布局都会发生变化。不过,您可能希望根据某些条件(例如导航方向或当前界面状态)动态停用此动画。

如需控制是否发生共享元素过渡,您现在可以自定义传递给 rememberSharedContentState()SharedContentConfigisEnabled 属性用于确定共享元素是否处于活跃状态。

SharedTransitionLayout {

        val transition = updateTransition(currentState)

        transition.AnimatedContent { targetState ->

            // Create the configuration that depends on state changing.

            fun animationConfig() : SharedTransitionScope.SharedContentConfig {

                return object : SharedTransitionScope.SharedContentConfig {

                    override val SharedTransitionScope.SharedContentState.isEnabled: Boolean

                        get() =

                            // determine whether to perform a shared element transition

                }

            }

}

如需了解详情,请参阅文档

Modifier.skipToLookaheadPosition()

此版本中添加了一个新的修饰符 Modifier.skipToLookaheadPosition(),用于在执行共享元素动画时保持可组合项的最终位置。这样一来,便可执行“显示”类型的动画过渡,如 Androidify 示例中相机逐步显示的效果。如需了解详情,请观看此处的视频提示:

共享元素过渡中的初始速度

此版本新增了一个共享元素过渡 API,即 prepareTransitionWithInitialVelocity,可让您将初始速度(例如来自手势的速度)传递给共享元素过渡:

Modifier.fillMaxSize()

    .draggable2D(

        rememberDraggable2DState { offset += it },

        onDragStopped = { velocity ->

            // Set up the initial velocity for the upcoming shared element

            // transition.

            sharedContentStateForDraggableCat

                ?.prepareTransitionWithInitialVelocity(velocity)

            showDetails = false

        },

    )
fling-shared.gif

一种从手势开始并具有初始速度的共享元素过渡

遮盖式转场

EnterTransition 和 ExitTransition 定义了 AnimatedVisibility/AnimatedContent 可组合项的显示或消失方式。借助新的实验性遮盖选项,您可以指定一种颜色来遮盖或覆盖内容;例如,在内容上淡入/淡出一个半透明的黑色图层:

veil_2.gif

含蓄的动画内容 - 请注意动画播放期间网格内容上方的半透明面纱(或幕布)

AnimatedContent(

    targetState = page,

    modifier = Modifier.fillMaxSize().weight(1f),

    transitionSpec = {

        if (targetState > initialState) {

            (slideInHorizontally { it } togetherWith

                    slideOutHorizontally { -it / 2 } + veilOut(targetColor = veilColor))

        } else {

            slideInHorizontally { -it / 2 } +

                    unveilIn(initialColor = veilColor) togetherWith slideOutHorizontally { it }

        }

    },

) { targetPage ->

    ...

}

即将发生的更改

弃用 Modifier.onFirstVisible

Compose 1.9 引入了 Modifier.onVisibilityChanged 和 Modifier.onFirstVisible。在审核您的反馈后,我们发现无法确定性地履行 Modifier.onFirstVisible 的合同;具体来说,当商品 首次 变为可见时,无法确定性地履行合同。例如,Lazy 布局可能会处置滚动到视口之外的项,然后在这些项滚动回视图中时再次对其进行合成。在这种情况下,由于是新组成的商品,系统会再次触发 onFirstVisible 回调。当返回到之前访问过的包含 onFirstVisible 的界面时,也会出现类似的行为。因此,我们决定在下一个 Compose 版本 (1.11) 中弃用此修饰符,并建议迁移到 onVisibilityChanged。如需了解详情,请参阅文档

测试中的协程调度

我们计划更改测试中的协程调度,以减少测试的不稳定性并捕获更多问题。目前,测试使用 UnconfinedTestDispatcher,这与生产行为不同;例如,效果可能会立即运行,而不是排队等待。在未来的版本中,我们计划推出一个默认使用 StandardTestDispatcher 的新 API,以匹配生产行为。您可以在 1.10 版中立即试用新行为:

@get:Rule // also createAndroidComposeRule, createEmptyComposeRule

val rule = createComposeRule(effectContext = StandardTestDispatcher())

使用 StandardTestDispatcher 会将任务排入队列,因此您必须使用 composeTestRule.waitForIdle()composeTestRule.runOnIdle() 等同步机制。如果您的测试使用 runTest,您必须确保 runTest 和您的 Compose 规则共享同一 StandardTestDispatcher 实例以进行同步。

// 1. Create a SINGLE dispatcher instance

val testDispatcher = StandardTestDispatcher()



// 2. Pass it to your Compose rule

@get:Rule

val composeRule = createComposeRule(effectContext = testDispatcher)



@Test

// 3. Pass the *SAME INSTANCE* to runTest

fun myTest() = runTest(testDispatcher) {

    composeRule.setContent { /* ... */ }

}

工具

出色的 API 值得搭配出色的工具,而 Android Studio 最近为 Compose 开发者新增了多项功能:

如需查看这些工具的实际应用,请观看以下最新演示:

祝您编程顺利

我们会继续投资于 Jetpack Compose,为您提供创建精美丰富界面的 API 和工具。我们非常重视您的意见,欢迎您在问题跟踪器中分享对这些变更的反馈,或提出您希望我们接下来做出的改进。

继续阅读