feat: 集成服务端驱动 UI 演示

新增 Remote Compose 播放端与服务端生成逻辑,支持按页面拉取并渲染二进制 UI 文档。
This commit is contained in:
2026-08-14 10:43:44 +08:00
parent c37b8ff0a4
commit 2bb3255704
11 changed files with 447 additions and 29 deletions

View File

@@ -28,6 +28,7 @@
</scm>
<properties>
<java.version>17</java.version>
<kotlin.version>2.1.0</kotlin.version>
</properties>
<dependencies>
<dependency>
@@ -40,6 +41,11 @@
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@@ -49,17 +55,78 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>annotationProcessor</scope>
<scope>provided</scope>
</dependency>
<!-- Remote Compose服务端生成二进制文档 -->
<dependency>
<groupId>androidx.compose.remote</groupId>
<artifactId>remote-creation-jvm</artifactId>
<version>1.0.0-alpha17</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>google</id>
<url>https://maven.google.com</url>
</repository>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<jvmTarget>17</jvmTarget>
<args>
<arg>-Xjsr305=strict</arg>
</args>
</configuration>
<executions>
<execution>
<id>compile</id>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/main/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<phase>process-test-sources</phase>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/test/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,33 @@
package com.ttstd.composeremote.server.controller;
import com.ttstd.composeremote.server.RemoteDocumentBuilderKt;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 服务端驱动 UI 接口。
*
* <p>一个页面一个接口:每个页面通过独立的路径暴露,例如
* <ul>
* <li>GET /api/remote-ui/home → 首页</li>
* <li>GET /api/remote-ui/detail → 详情页</li>
* </ul>
* 返回 {@code application/octet-stream} 的 Remote Compose 二进制文档,
* 由 Android 端 {@code RemoteComposePlayer} 原生渲染。
*/
@RestController
@RequestMapping("/api/remote-ui")
public class RemoteUIController {
@GetMapping(
value = {"/{page}", ""},
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE
)
public byte[] getPage(@PathVariable(name = "page", required = false) String page) {
String target = (page == null || page.isBlank()) ? "home" : page;
return RemoteDocumentBuilderKt.buildRemoteDocument(target);
}
}

View File

@@ -0,0 +1,38 @@
package com.ttstd.composeremote.server
import androidx.compose.remote.core.CoreDocument
import androidx.compose.remote.creation.JvmRcPlatformServices
import androidx.compose.remote.creation.RemoteComposeWriter
import androidx.compose.remote.creation.dsl.RcColumnScope
import androidx.compose.remote.creation.dsl.RcProfile
import androidx.compose.remote.creation.dsl.RcSp
import androidx.compose.remote.creation.dsl.createRcBuffer
import androidx.compose.remote.creation.dsl.fillMaxSize
import androidx.compose.remote.creation.dsl.fillMaxWidth
import androidx.compose.remote.creation.dsl.padding
import androidx.compose.remote.creation.dsl.background
import androidx.compose.remote.creation.dsl.Modifier
import androidx.compose.remote.creation.profile.Profile
import androidx.compose.remote.creation.profile.RemoteComposeWriterFactory
fun buildRemoteDocument(page: String): ByteArray {
val writerFactory = RemoteComposeWriterFactory { _, _, _ ->
RemoteComposeWriter(1080, 1920, "remote-ui", JvmRcPlatformServices())
}
val profile = Profile(
CoreDocument.DOCUMENT_API_LEVEL,
CoreDocument.PROFILE,
JvmRcPlatformServices(),
writerFactory
)
val rcProfile = RcProfile(profile, false)
return createRcBuffer(rcProfile) {
Column(Modifier.fillMaxSize().background(0xFFFFFFFF.toInt()).padding(16f)) {
Text(
text = if (page == "home") "Home Page" else "Detail Page",
fontSize = RcSp(36f),
color = 0xFF0000FF.toInt() // Blue
)
}
}
}