FEATURED · 精选文章

Compose Multiplatform LazyGrid 图片网格性能基准:跨 Android、iOS 与 Desktop 的三端同构实现

发布时间 / 2026/9/13 20:34:00
来源 / 创域科博编辑部
栏目 / 资讯中心
Compose Multiplatform LazyGrid 图片网格性能基准:跨 Android、iOS 与 Desktop 的三端同构实现 Compose Multiplatform LazyGrid 图片网格性能基准跨 Android、iOS 与 Desktop 的三端同构实现【免费下载链接】compose-multiplatformCompose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.项目地址: https://gitcode.com/GitHub_Trending/co/compose-multiplatform本篇文章以仓库中 LazyGridImageView 基准示例 为主线系统拆解如何用同一套 Compose Multiplatform 代码在 Android、iOS、Desktop以及 JS/Wasm 目标上渲染 999 张图片的 3 列懒加载网格并与原生 AndroidJetpack Compose Coil和原生 iOSSwiftUI AsyncImage两套纯原生实现进行逐平台对比。读者在读完本文后将掌握 Compose Multiplatform 资源目录composeResources、LazyVerticalGrid懒加载网格、Coil 异步图片加载与自动滚动压力测试脚本的完整落地写法以及一套可复现的三端基准测量工作流大小、启动时间、FPS、CPU/GPU 占用等可直接照搬到自己的性能评测或图片类应用项目中。项目定位为什么需要三个平台同一套功能LazyGridImageView是仓库benchmarks/showcases目录下专门用于横向对比 Compose Multiplatform 与纯原生实现性能的示例工程。它用三份独立实现完成完全相同的用户场景Kotlin Multiplatform Compose位于composeApp模块共享一份commonMain代码同时产出 Android、iOS、Desktop、JS 与 Wasm 目标Native Android位于nativeAndroidApp模块纯 Android 实现Jetpack Compose Coil图片来自 Android assetsNative iOS位于nativeiosApp模块纯 SwiftUI 实现图片来自 Bundle 资源。三个实现都渲染999 张 512×512 的图片排列成3 列的懒加载网格并提供一个Auto Scroll 自动滚动开关用于在基准测试时制造持续的滚动负载。这个同一功能、三份代码的设计使得对比指标应用体积、启动时间、FPS、CPU/GPU 占用、内存等具有可对照的公平基础正如 README 所述The project is used to compare Compose Multiplatform performance metrics with native counter-parts such as size, startup time, FPS, CPU/GPU usage, etc.仓库还额外提供了 compare_sizes.main.kts 与 measure_sizes.main.kts 两个 Kotlin 脚本分别用于对比三端产物体积与测量各产物大小支撑应用体积这一项基准维度。项目结构与三端模块划分整个工程位于benchmarks/showcases/LazyGridImageView/目录组织如下目录 / 文件职责composeApp/Kotlin Multiplatform 实现含commonMain、androidMain、iosMain、desktopMain、wasmJsMain、jsMain等源集nativeAndroidApp/原生 AndroidJetpack Compose图片资源放在app/src/main/assets/downloaded_images/nativeiosApp/原生 iOSSwiftUI图片放在nativeiosApp/downloaded_images/通过 Xcode 工程构建download_images.sh从 Picsum Photos 批量下载 999 张测试图片并分发到三端资源目录compare_sizes.main.kts/measure_sizes.main.kts产物体积对比 / 测量脚本gradle/libs.versions.toml版本目录Version Catalog统一管理三方库版本settings.gradle.kts声明:composeApp模块并启用TYPESAFE_PROJECT_ACCESSORS特性composeApp一份代码五个平台目标从 composeApp/build.gradle.kts 可以看到完整的平台矩阵androidTargetAndroid 应用JVM 11iosX64 / iosArm64 / iosSimulatorArm64三个 iOS 目标统一产出名为ComposeApp的静态 frameworkisStatic truejvm(desktop)桌面 JVM 目标wasmJs与js浏览器目标均配置browser下的commonWebpackConfig输出composeApp.js。commonMain的依赖中既有compose.runtime / foundation / material3 / ui全套 Compose 库也有compose.components.resources资源组件用于访问composeResources和coil.composeCoil 3 的 Compose 集成。桌面端额外引入kotlinx.coroutines.swingAndroid 端引入androidx.activity:activity-compose与compose.preview。桌面入口在 main.ktiOS 入口在 MainViewController.ktComposeUIViewController { App() }Android 入口在 MainActivity.ktsetContent { App() }——三个平台的界面代码全部收敛到commonMain的同一个App()。资源放置差异三套体系同一批图片平台资源目录加载方式Compose MultiplatformcomposeApp/src/commonMain/composeResources/files/Res.getUri(files/xxx.jpg) Coil原生 AndroidnativeAndroidApp/app/src/main/assets/downloaded_images/file:///android_asset/前缀 Coil原生 iOSnativeiosApp/nativeiosApp/downloaded_images/Bundle 内Bundle.main.url(...) SwiftUIAsyncImage准备数据一键下载 999 张测试图片项目要求在任何构建之前先运行下载脚本README 明确说明Please run the script before building the project。脚本位于 download_images.sh./download_images.sh脚本核心逻辑如下预创建三个目标目录composeApp/src/commonMain/composeResources/files、nativeiosApp/nativeiosApp/downloaded_images、nativeAndroidApp/app/src/main/assets/downloaded_images循环1..999构造文件名downloaded_image%03d.jpg即downloaded_image001.jpgdownloaded_image999.jpg每个编号用固定 ID 从https://picsum.photos/id/$i/512/512.jpg下载失败时自动回退到随机图片https://picsum.photos/512/512.jpg下载成功文件非空后cp复制到 iOS 与 Android 两个目录保证三端使用完全相同的图片数据每张图片之间sleep 0.1防止请求过快被限流。注意两点图片统一为512×512 的 JPG与后续 UI 中aspectRatio(1f)的正方形卡片一一对应由于某些编号在 Picsum 上可能不存在404脚本会对失败项使用随机图兜底因此本地实际成功数可能少于 999代码运行时也会对缺失资源做防御性处理见下文Res.getUri的 try/catch。仓库中已附带downloaded_image001.jpg等少量示例图片便于在没有联网环境时先跑通工程完整数据仍需执行脚本生成。Compose Multiplatform 实现commonMain 中的网格与图片加载资源枚举与防御式解析App.kt 中首先在remember块里遍历 1..999用 Compose 资源的类型安全生成器Res.getUri(files/downloaded_imageXXX.jpg)解析每个资源 URI并用 try/catch 忽略下载失败产生的缺失资源val uris remember { val availableResources: MutableListString mutableListOf() for (index in 1..999) { try { val resUri Res.getUri(files/downloaded_image${index.toString().padStart(3, 0)}.jpg) availableResources.add(resUri) } catch (e: Exception) { //ignore } } List(999) { index - availableResources[index % availableResources.size] } }index % availableResources.size的取模写法保证了即使实际可用图片少于 999 张列表仍能凑满 999 项从而保证基准负载的一致性。这段代码依赖compose.components.resources提供的Res生成类对应lazygridimage.composeapp.generated.resources.Res导入并使用OptIn(ExperimentalResourceApi::class)声明实验 API。3 列 LazyVerticalGrid 与自动滚动网格本体使用LazyVerticalGrid列数由常量numOfColumns 3决定LazyVerticalGrid( columns GridCells.Fixed(numOfColumns), contentPadding PaddingValues(4.dp), modifier Modifier.fillMaxSize(), state gridState ) { items(uris) { uri - ImageCard(uri, modifier Modifier.padding(4.dp)) } }LazyVerticalGrid只在视口内组合可见条目这正是999 张图片仍能流畅滚动的关键。配合rememberLazyGridState()代码实现了一个自动往返滚动逻辑勾选顶部 Auto Scroll 复选框后LaunchedEffect(autoScroll)中每delay(100)毫秒调用一次gridState.animateScrollToItem(index currentIndex)每次按numOfColumns3 行步进到达底部currentIndex uris.size - numOfColumns - 1后翻转方向向上滚动到达顶部再翻转。这样在基准测试时无需人工操作即可持续制造滚动压力。图片卡片Coil AsyncImage 正方形裁剪每个网格条目封装在ImageCard中Composable fun ImageCard(uri: String, modifier: Modifier Modifier) { Card( modifier modifier .aspectRatio(1f) // Square aspect ratio .fillMaxWidth(), elevation CardDefaults.cardElevation(defaultElevation 4.dp) ) { AsyncImage( model uri, contentDescription null, contentScale ContentScale.Crop, modifier Modifier.fillMaxSize() ) } }aspectRatio(1f)让每个卡片呈正方形与 512×512 的源图比例一致ContentScale.Crop做居中裁剪填充图片加载使用 Coil 3 的coil3.compose.AsyncImage模型直接传Res.getUri解析出的资源 URI——这正是 README 所述images are loaded using Coil library from the Compose Multiplatform resources的实现位置。Coil 负责异步解码、缓存与生命周期管理因此App()中没有任何手写协程加载逻辑。平台薄壳三个平台的入口都只是把App()塞进各自宿主AndroidMainActivity.kt ——setContent { App() }iOSMainViewController.kt ——ComposeUIViewController { App() }Desktopmain.kt ——application { Window(title LazyGridImage) { App() } }。原生 Android 实现Jetpack Compose Coil assetsnativeAndroidApp用纯 Android 技术栈重写同一功能入口在 nativeAndroidApp/app/src/main/java/org/jetbrains/lazygridimage/MainActivity.kt。与 Compose Multiplatform 版本最大的差异在资源来源这里通过context.assets.list(downloaded_images/)枚举 assets 目录下的全部图片文件名然后拼出file:///android_asset/downloaded_images/xxx.jpg形式的 URIval uris remember { val imagesFolder downloaded_images/ val availableResources context.assets.list(imagesFolder) List(999) { index - file:///android_asset/ imagesFolder availableResources!![index % availableResources.size] } }其余骨架与共享版本高度一致LazyVerticalGrid(columns GridCells.Fixed(3))、rememberLazyGridState()、Auto Scroll 复选框 LaunchedEffectanimateScrollToItem自动滚动、ImageCard中AsyncImage(model uri, contentScale ContentScale.Crop)。也就是说只要把资源 URI 的获取方式这一层换掉网格与滚动逻辑在 Jetpack Compose 与 Compose Multiplatform 之间可以近乎原样复用——这正是两套实现便于直接对比的前提。原生 iOS 实现SwiftUI LazyVGrid AsyncImagenativeiosApp是纯 SwiftUI 工程主要文件ContentView.swift网格 自动滚动 图片名加载GridItemView.swift单个网格条目的AsyncImage渲染App.swift应用入口。ContentView 中同样固定 3 列GridItem(.flexible())× 3网格本体是LazyVGrid(columns:columns, spacing: 8)。自动滚动用Timer.publish(every: 0.1, on: .main, in: .common)每 100ms 触发一次updateScrollPosition()配合ScrollViewReader的proxy.scrollTo(newPosition, anchor: .top)完成往返滚动步长同样是列数 3——与 Compose 版delay(100)animateScrollToItem的频率与步进完全对齐保证两边滚动负载等价。图片名通过loadImageNames()构造downloaded_image001…downloaded_image999并保留testingMode开关测试模式下先用UIImage(named:)过滤掉未成功下载的图片existingImages再取模补满 999 项与 Compose 版的防御式处理思路一致。GridItemView.swift 展示了资源定位与加载的差异点let imageUrl Bundle.main.url(forResource: imageName, withExtension: jpg) AsyncImage(url: imageUrl) { phase in switch phase { case .success(let image): image .resizable() .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) default: Color.gray } }资源来自Bundledownloaded_images目录被打包进 app对应 README 所述uses iOS asset catalog to load imagesAsyncImage是 SwiftUI 自带的异步图片加载器加载中/失败时渲染Color.gray占位条目外层.aspectRatio(1, contentMode: .fill)与.id(index)供ScrollViewReader定位背景白色 cornerRadius(8)圆角卡片视觉上与 Compose 版的Card保持一致。三端构建与运行指南按 README 的说明三套实现的启动方式如下实现操作Compose MultiplatformAndroid用 Android Studio 打开工程运行composeApp配置Compose MultiplatformiOS打开iosApp目录中的 Xcode 工程运行注意composeApp产物是名为ComposeApp的静态 frameworkCompose MultiplatformDesktop在 IntelliJ IDEA 或 Android Studio 中运行desktopApp配置原生 Android用 Android Studio 打开nativeAndroidApp目录直接运行原生 iOS用 Xcode 打开nativeiosApp/nativeiosApp.xcodeproj运行前置条件先执行./download_images.sh下载 999 张图片否则资源目录为空、网格将无图可显iOS 端testingMode即为此场景的容错。关于 Web 目标需要说明composeApp的构建脚本虽然声明了wasmJs与js两个浏览器目标但commonMain中的Res.getUri解析与 Coil 加载在当前资源配置下主要面向桌面/移动端验证浏览器端运行请以实际构建产物为准本文档仅记录移动与桌面三端的官方运行路径。基准测量的配套工具工程目录下还提供了两个.main.kts脚本用于自动化体积维度的测量measure_sizes.main.kts测量各平台产物体积compare_sizes.main.kts对三端产物进行体积对比输出。结合 README 提到的对比维度应用大小、启动时间、FPS、CPU/GPU 占用等建议的完整基准流程是运行./download_images.sh准备 999 张 512×512 图片分别构建 Compose Multiplatform、原生 Android、原生 iOS 三个产物用measure_sizes.main.kts/compare_sizes.main.kts量化体积差异启动 App 后勾选 Auto Scroll让三端以相同的 100ms 步进做往返滚动再用系统级 ProfilerAndroid Profiler、Xcode Instruments、桌面性能分析工具采集 FPS、CPU/GPU 占用与内存汇总数据并对照三份实现得出结论。小结一套 UI、三种落地、可比可测LazyGridImageView示例的核心价值可以概括为三点代码复用极限App()一份 commonMain 代码同时支撑 Android、iOS、Desktop 三个平台平台差异被压缩到三个各不超 20 行的入口文件等价负载设计三端均固定 999 张图、3 列网格、100ms 步进自动往返滚动保证基准测试输入一致可复制的测量链路download_images.sh统一数据源measure/compare_sizes.main.kts量化体积外部 Profiler 采集运行时指标。无论是想评估 Compose Multiplatform 在真实图片密集型场景下的表现还是想为自己的项目搭建一套跨平台网格 Coil 异步图片的参考实现都可以直接以本示例为起点复制composeApp目录替换composeResources/files中的图片数据即可在最短时间内得到一套可运行的跨平台 LazyGrid 图片浏览器。【免费下载链接】compose-multiplatformCompose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.项目地址: https://gitcode.com/GitHub_Trending/co/compose-multiplatform创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻