Remote Compose 是一个 Jetpack 库,可让您在应用中使用 Compose 样式的 API 构建界面,并在系统进程(例如应用 widget 或 Wear OS widget)中显示该界面。应用会将界面记录为紧凑文档,而宿主进程中的播放器会呈现该文档,并在本地处理互动、状态更改和动画,而无需回调到应用。
本指南介绍了远程 Compose 架构及其与标准 Jetpack Compose 的区别。本指南重点介绍如何使用组件构建界面。
Remote Compose 用例
Remote Compose 可在任何具有支持播放器的平台上使用。在 Android 上,它为应用 widget 和 Wear OS widget 提供支持。
运作方式:三块式架构
Remote Compose 与标准 Jetpack Compose 之间的根本架构区别在于,Remote Compose 将界面定义与执行解耦,分为三个高级块:
- 创建文档:您的应用会运行
@RemoteComposable函数来记录文档,即界面的紧凑序列化说明,包括其布局、远程状态和操作。当数据发生变化时,您的应用可以记录并发送新文档。 - 将文档传输到远程主机:您的应用会将文档传递给主机,即显示该文档的进程,例如启动器或 Wear OS。
- 播放文档:主机中的 Remote Compose 播放器会渲染文档,并在本地处理布局、输入、用户体验状态更改和动画,而无需回调到您的应用。
此架构具有以下主要优势,可直接降低延迟:
- 文档大小较小:序列化文档快照保持紧凑和轻量。
- 减少往返次数:由于远程主机播放器在本地处理互动和状态更改,因此返回应用或服务器的往返通信会降至最低。
网页/HTML 类比:Remote Compose 的运作方式类似于提供网页。应用服务器会生成并发送一次 HTML/文档结构,浏览器主机会在本地呈现该结构并处理互动,而无需不断查询后端。
组件
Remote Compose 提供了高级界面组件来构建布局。使用 Remote Compose 组件和 RemoteModifier,而不是其标准 Compose 等效项。其中包括基本布局容器以及专为处理动态屏幕尺寸而设计的自适应组件:
- 基本布局组件:常见的类似 Compose 的布局元素,例如
RemoteBox、RemoteRow和RemoteColumn。 - 自适应布局组件:
RemoteFitBox、RemoteCollapsibleRow和RemoteCollapsibleColumn等专用组件,旨在跨远程界面进行自适应。 - Material Design 主题设置:主题设置对 Wear OS widget 的作用方式相同,由
RemoteMaterialTheme提供颜色和排版层次结构。这样,您就可以在应用和微件中实现一致的品牌和设计系统。
使用 RemoteCanvas 自定义绘图
对于自定义图形和基于时间的动画,请使用 RemoteCanvas,它会记录玩家在主机上运行的绘制命令。本指南重点介绍如何使用组件构建界面。
主要区别
请查看以下核心概念,了解 Remote Compose 如何管理状态、逻辑和操作。
符号状态引用
在标准 Compose 中,状态容器在本地应用内存中保存活跃的可读值,这些值会在发生事件时由应用代码(lambda 表达式)进行更改。在 Remote Compose 中,状态声明充当符号引用。它们不会在本地应用内存中保留活跃值,而是会声明由远程播放器在运行时在本地管理和更新的绑定。在 Remote Compose 中声明状态时,请注意以下事项:
- 使用 Remote Compose 工厂函数声明状态,例如
rememberMutableRemoteInt、rememberMutableRemoteFloat或rememberMutableRemoteString。 - 在组合期间,应用代码无法直接检查或读取远程状态变量的值。
- 当用户与文档互动时,播放器会在本地存储和更新这些值。
远程状态与 Kotlin 控制流
只有当应用创建文档时,Kotlin 代码才会运行。Kotlin 语句(如 if、when 和 for)可以根据当时已知的值来调整文档(例如为列表中的每个项重复一行),但它们无法对稍后发生变化的远程状态做出反应。在 Remote Compose 中工作时,请注意以下几点:
- 分支逻辑:动态界面切换是使用
RemoteStateLayout通过将状态映射到不同的RemoteComposable槽来完成的。 - 文本求值:避免使用标准 Kotlin 字符串插值 ("
$remoteValue"),因为这种插值仅在创建文档时求值。而是将RemoteString传递给RemoteText。
使用 RemoteModifier 的动态样式
宿主上的动态样式不会重新运行组合;而是将远程状态变量直接传递到修饰符(例如 RemoteModifier.background(remoteColor))中。
远程系统上下文
CompositionLocal 提供程序(例如 LocalConfiguration 或 LocalDensity)仅反映记录文档时的本地应用环境。当界面调整大小、更改显示状态或随着时间推移而发生变化时,它们不会在远程主机上动态更新。而是使用播放器在运行时在主机上解析的值,例如组件的当前大小、RemoteConfiguration 或 RemoteDensity。
声明式操作
在标准 Compose 中,点击处理程序是在应用中运行的 lambda。宿主无法运行应用的代码,因此在 Remote Compose 中,您需要改为附加操作,例如使用 RemoteModifier.clickable(action)。操作可以使用 valueChange 直接在主机上更新远程状态,也可以使用 pendingIntentAction 或 hostAction 将控制权移交给应用或主机。如需通过一次点击运行多项操作,请使用 combinedAction。
高级范式比较
功能网域 |
标准 Jetpack Compose |
远程撰写 |
|---|---|---|
点击和触摸事件 |
进程内同步 lambda |
声明式序列化 Action 对象 |
条件逻辑 |
Kotlin if 和 when 控制流 |
对于生成文档时已知的静态状态,标准 Kotlin 动态分支使用 |
控制流循环 |
标准 Kotlin for 和 while 循环 |
标准 Kotlin 循环(repeat、for、forEach)仅在创建文档时运行。无法在主机上循环执行远程状态 |
执行主机 |
应用进程 |
启动器 (AppWidget)、Wear OS 微件或任何具有支持播放器的设备规格 |
进程生命周期 |
进程必须保持活跃状态才能重组和渲染 |
进程会记录界面快照文档;Remote Player 引擎会解析组件树、跟踪状态变化,并在本地重新绘制,而无需回调到应用中。 |
副作用 |
协程生命周期 (LaunchedEffect) |
无法在主机上使用。组合在应用中运行以创建文档,因此协程效应无法驱动远程界面,除非集成使组合保持打开状态。 |
状态范式 |
本地记忆状态值 |
返回符号状态引用而非原始值的工厂函数(rememberMutableRemoteInt()、rememberNamedRemoteColor())。 |