Skip to content

Landscapist:Compose 图片加载封装 ​

回到总览:图片加载 / Image Loading相关模块:Compose 状态模型(重组与 remember 语义)· Bitmap 内存与 GC(图片降采样 / inBitmap 复用)· 性能与 jank(图片加载对帧率的影响)

一句话定义 ​

Landscapist 是 Jetpack Compose 的图片加载库(skydoves 维护),在 Glide / Coil / Fresco 之上提供统一的 Composable API(GlideImage / CoilImage / FrescoImage,以及统一的 LandscapistImage),并把占位、失败、动画、模糊、调色板等能力做成声明式插件;其 Coil 3 后端为 KMP 原生,可在 Android / iOS / macOS / JVM / Wasm 复用同一调用。

代码索引 ​

主题Lab 说明源码
Landscapist Compose 图片加载Android / Compose 真机 lab:同一张图用 GlideImage / CoilImage + 原生 BitmapFactory 解码,演示约束驱动采样与引擎缓存清理ImageLoadingDemoActivity.kt · lab README

1. 实现原理 ​

1.1 统一 Composable API 与后端抽象 ​

Landscapist 把三个引擎的差异收敛到同一组 Composable 与同一套请求模型 imageModel = { url }:

  • GlideImage → 走 Glide 的 RequestManager
  • CoilImage → 走 Coil 的 ImageLoader
  • FrescoImage → 走 Fresco 的 DraweeController
  • LandscapistImage → newer 统一入口,配合 imageOptions { } 与组件插槽

调用方只写 Compose 代码,不接触各引擎的 into() / Target 等命令式 API。

1.2 与 Compose 生命周期 / 尺寸的绑定(约束驱动采样) ​

这是它和「View 时代 Glide into(ImageView)」的本质区别:

  1. 生命周期绑定到 Composition:图片组件进入组合时发起请求,离开组合(DisposeEffect)时取消请求并释放资源,避免 Activity/Fragment 销毁后还在解码。
  2. 约束驱动采样:Compose 把 Constraints(来自 Modifier.size / 父布局)作为目标尺寸传给组件,Landscapist 据此向底层引擎请求降采样到显示尺寸的 bitmap,而不是先加载原图再缩放。这一步直接决定内存占用(与 02 的 inBitmap 复用同源)。

1.3 插件架构 ​

占位 / 失败 / 动画 / 模糊 / 调色板 / 圆角 / 缩放等能力以组件插件形式挂载,而非散落在业务代码:

kotlin
CoilImage(
    imageModel = { "https://example.com/photo.jpg" },
    modifier = Modifier.size(200.dp),
    component = ImageComponentBuilder.new {
        +BlurPlugin(blurRadius = 12)        // 模糊
        +PalettePlugin { palette ->          // 取主色
            val swatch = palette?.dominantSwatch
            // swatch?.rgb
        }
        +ShimmerPlugin(baseColor = Color.LightGray) // 骨架闪烁
    },
)

插件在「请求前 / 成功 / 失败」各阶段插入逻辑,组件本身保持声明式。

1.4 缓存与内存来自底层引擎 ​

Landscapist 自身主要做编排,不重新实现缓存:

  • Glide 后端:Bitmap 复用走 Glide BitmapPool(inBitmap),内存缓存 LruResourceCache + 磁盘 LRU。
  • Coil 后端:内存 MemoryCache + 磁盘 DiskCache。
  • Fresco 后端:含 native 内存的 CloseableReference 体系。

因此「Bitmap 复用、内存上限、淘汰策略」这些治理能力来自引擎,Landscapist 只是把结果画到 Compose DrawScope。

各引擎内部加载原理(Glide 的 Request 链与四级查找、Coil 的 ImageLoader + Interceptor 责任链、Fresco 的 Drawee + ImagePipeline producers、native 内存)见 02-图片加载方式总览与原理。

1.5 KMP 支持(Coil 3 后端) ​

Coil 3 是 KMP 原生的 ImageLoader;Landscapist 的 Coil 后端在 Coil 3 起可在 Android / iOS / macOS / JVM / Wasm 共用同一套 CoilImage 调用,差异只是各平台默认的 ImageLoader 与网络栈。

2. 特点与优势 ​

2.1 它解决什么 ​

把「请求生命周期管理 + 约束驱动采样 + 占位/失败/动画等横切关注点」从业务 Composable 里抽离,集中到库与插件中;同一组声明式 API 背后可换引擎、跨平台。

2.2 与其他实现方式 / 概念的区别 ​

  • vs 直接用 Coil 3 的 AsyncImage:AsyncImage 是单后端、Compose 原生的轻量原语,足够覆盖大多数 Android + Compose 场景;Landscapist 额外提供多后端统一 API + 插件 DSL(blur/palette/shimmer/resize/circular) 。若只用 Coil 且不需要这些插件,Landscapist 是可选而非必需。
  • vs 在 Compose 里用 AndroidView { Glide.with(it).load(url).into(it) }:AndroidView 桥接回 View 系统,生命周期与尺寸要手动对齐 Compose;Landscapist 是 Compose 原生,自动绑定组合生命周期与约束,避免桥接样板与生命周期错位。
  • vs Glide / Fresco 在 View 时代的 ImageView:引擎成熟度(Glide 的 inBitmap 池化、Fresco 的 native 内存)被保留,但调用面变成 Compose 友好的声明式组件,无需手动管理 Target / 取消。
  • vs 手写「占位 + 淡入 + 失败重试」:插件把这套逻辑写成可复用组件,业务侧只声明所需插件,减少重复与出错面。
  • 与 Koin 委托属性的回扣(边界) :图片加载是「随重组可能重算」的取值,用 Composable 的状态(remember / derivedStateOf)比属性委托的「首次缓存」更合适——和 Koin 4.0 弃用 by inject() 改用 koinInject() 是同一类判断(见 委托属性)。

3. 用法 ​

3.1 依赖与后端选择 ​

kotlin
// 选一个后端即可(版本以官方最新为准;坐标前缀是 com.github.skydoves 而非 io.github)
implementation("com.github.skydoves:landscapist-glide:<version>")
implementation("com.github.skydoves:landscapist-coil:<version>")
implementation("com.github.skydoves:landscapist-fresco:<version>")
// 插件按需引入:landscapist-blur / -palette / -shimmer / -animation / -transformations

3.2 基础用法(加载 / 占位 / 失败) ​

kotlin
GlideImage(
    imageModel = { "https://example.com/photo.jpg" },
    modifier = Modifier
        .size(128.dp)
        .clip(RoundedCornerShape(8.dp)),
    loading = {
        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            CircularProgressIndicator()
        }
    },
    failure = { error ->
        Text("加载失败: ${error?.message}")
    },
    previewPlaceholder = painterResource(R.drawable.preview), // 预览占位
)

3.3 插件(模糊 / 调色板 / 骨架 / 圆角) ​

kotlin
CoilImage(
    imageModel = { url },
    modifier = Modifier.size(200.dp),
    component = ImageComponentBuilder.new {
        +BlurPlugin(blurRadius = 12)
        +PalettePlugin { palette -> /* 取 dominantSwatch.rgb 做背景 */ }
        +ShimmerPlugin(baseColor = Color.LightGray)
    },
)

圆角 / 缩放等也可通过 Modifier.clip() 或 transformations 插件处理,不必回到命令式引擎 API。

3.4 Coil 3 后端 + KMP ​

在 commonMain 直接调用 CoilImage,各平台提供默认 ImageLoader 即可共用同一 UI 代码;差异只在平台网络/缓存实现,对业务 Composable 透明。

3.5 注意点(版本钉死) ​

landscapist-glide 内部钉死 Glide 4.16.0;若你的项目其它地方用了不同 Glide 版本,需 exclude 或对齐版本,否则会出现重复 Glide 类或方法签名冲突。

本页回答的问题(一句话答) ​

  • Landscapist 和直接用 Coil 3 的 AsyncImage 有什么区别?
    • 一句话答:AsyncImage 是单后端轻量原语;Landscapist 多后端统一 API + 插件 DSL(blur/palette/shimmer),并借 Coil 3 后端打通 KMP。只用 Coil 且不需插件时不必引入。
  • 「约束驱动采样」解决什么?
    • 一句话答:按 Compose 布局约束把图片降采样到显示尺寸再解码,减少 bitmap 内存与 OOM 风险;生命周期同时绑定到组合。
  • Bitmap 复用 / 内存缓存是谁提供的?
    • 一句话答:来自底层引擎(Glide BitmapPool/inBitmap、Coil MemoryCache、Fresco native);Landscapist 只做编排与绘制。
  • landscapist-glide 有什么版本坑?
    • 一句话答:内部钉死 Glide 4.16.0,项目若用其它 Glide 版本需 exclude 或对齐,避免类冲突。

对应实验 ​

  • Android / Compose 真机 lab:ImageLoadingDemoActivity.kt + lab README。同一张网络图用 GlideImage / CoilImage + 原生 BitmapFactory 解码加载,演示约束驱动采样与引擎缓存清理;Fresco 因 native 依赖链较重、本机代理下不易拉全,runnable lab 暂不含,原理见 02 / 03。

复习检查题 ​

  1. Landscapist 相比直接用 Coil 3 的 AsyncImage,多提供了什么?什么情况下其实不需要 Landscapist? 答:多提供「多后端统一 Composable + 插件 DSL(blur/palette/shimmer/resize/circular)」与 KMP 故事。若只用 Coil 且不需要这些插件,AsyncImage 更轻,不必引入 Landscapist。
  2. 为什么说 Landscapist 的「约束驱动采样」对内存很关键? 答:它按 Compose 布局约束把图片降采样到显示尺寸再解码,避免加载远大于屏幕的原图 bitmap,直接降低内存峰值与 OOM 概率;请求生命周期还绑定到组合,离开即取消。
  3. Landscapist 自身实现 bitmap 复用和内存缓存吗? 答:不实现。复用与缓存来自底层引擎(Glide BitmapPool/inBitmap、Coil MemoryCache、Fresco native),Landscapist 只负责编排请求并把结果画到 Compose。
  4. 接入 landscapist-glide 时最常见的版本坑是什么? 答:它钉死 Glide 4.16.0;项目若用其它 Glide 版本会冲突,需要 exclude 或对齐 Glide 版本。

速记 ​

  • 统一 API:GlideImage / CoilImage / FrescoImage + 统一 LandscapistImage,imageModel = { url }。
  • 两绑定:组合生命周期绑定(离开取消)+ 约束驱动采样(按尺寸降采样)。
  • 插件化:placeholder/blur/palette/shimmer 走 ImageComponentBuilder,业务只声明。
  • 内存来自引擎:BitmapPool / MemoryCache / Fresco native,Landscapist 只编排。
  • KMP:Coil 3 后端原生跨端;landscapist-glide 钉死 Glide 4.16.0。

站点构建时间:2026/8/24 23:43:17