Fresco 渐进式 JPEG(Progressive JPEG)支持:网络流式解码原理与 Android 实战指南
移动开发图像处理【免费下载链接】frescoAn Android library for managing images and the memory they use.项目地址https://gitcode.com/gh_mirrors/fr/fresco点击查看免费下载渐进式 JPEGProgressive JPEG是提升弱网环境下图片体验的关键技术图片在下载过程中先显示模糊的完整轮廓再随数据到达逐步变清晰。本文以 Fresco 官方文档 progressive-jpegs.md 为骨架结合仓库内imagepipeline模块的源码实现与 showcase 示例应用讲解如何在 Fresco 中开启渐进式渲染、其底层解码链路如何工作以及有哪些可配置项与限制。读完本文你将掌握构建渐进式 JPEG 图片请求的完整写法并理解DecodeProducer、ProgressiveJpegParser、ProgressiveJpegConfig在其中的角色。一、渐进式 JPEG 是什么与普通 JPEG 的区别JPEG 图片有两种常见的编码组织方式基线Baseline编码图像的扫描数据从上到下按行顺序存放必须等整张图的数据全部到达后才能完整解码显示。渐进式Progressive编码图像数据被拆分成多次扫描scan第一次扫描只包含粗糙的整体信息后续每次扫描逐步补充细节。解码端每完成一次扫描就可以渲染出一版质量更高的图片。Fresco 利用这一特性支持对通过网络加载的渐进式 JPEG 图片进行流式解码Scans of the image will be shown in the view as you download them. Users will see the quality of the image start out low and gradually become clearer.图片扫描结果会随着下载进度逐帧显示在视图上用户会看到图片质量由低到高逐渐变清晰。需要强调的是这个能力有两个前提条件文档明确说明仅适用于网络图片本地图片如res、assets、本地文件是一次性完整解码的不存在渐进的需要。图片本身必须是渐进式编码并非所有 JPEG 都是渐进式编码对于基线编码的 JPEG无论下载多慢都无法做到渐进显示——数据不完整时根本无法解码出任何一版画面。这两个条件在源码中同样有印证DecodeProducer会根据 URI 是否为网络地址来选择不同的解码器详见下文第三节。二、构建图片请求显式开启渐进式渲染在当前版本中渐进式渲染必须由开发者显式开启ImageRequestBuilder提供了对应的方法。文档给出的标准写法如下Uri uri; ImageRequest request ImageRequestBuilder.newBuilderWithSource(uri) .setProgressiveRenderingEnabled(true) .build(); DraweeController controller Fresco.newDraweeControllerBuilder() .setImageRequest(request) .setOldController(mSimpleDraweeView.getController()) .build(); mSimpleDraweeView.setController(controller);关键点说明ImageRequestBuilder.newBuilderWithSource(uri)创建图片请求构建器.setProgressiveRenderingEnabled(true)开启渐进式渲染。查看 ImageRequestBuilder.java 的实现该方法只是将布尔值写入构建器内部的mProgressiveRenderingEnabled字段并返回 builder 自身链式调用最终由build()生成携带该标志的ImageRequest通过Fresco.newDraweeControllerBuilder()将请求与视图关联setOldController传入旧的 controller 以便复用最后mSimpleDraweeView.setController(controller)生效ImageRequest的toString()中也会携带该标志便于调试时确认请求配置见 ImageRequest.java。文档同时说明目前setImageURI快捷方式还不支持渐进式图片We hope to add support for using progressive images withsetImageURIin a future release.。也就是说凡是需要渐进式渲染的图片都必须走上述显式构造ImageRequest的完整流程而不能使用简化 API。三、底层原理DecodeProducer 如何选择渐进式解码路径开启渐进式渲染后图片请求会进入 Fresco 的生产者Producer流水线。负责解码的DecodeProducer是这条链路的核心节点其类注释明确写道Progressive JPEGs are decoded progressively as new data arrives.渐进式 JPEG 会随新数据到达而被渐进解码。看 DecodeProducer.kt 的produceResults方法它根据请求的 URI 是否为网络地址构造两种不同的解码器非网络 URI本地文件、资源等创建LocalImagesProgressiveDecoder。它的getIntermediateImageEndOffset直接返回整个编码图片的大小qualityInfo固定为质量 0 且非良好质量、非完整质量——即本地图片不做渐进解码与文档Local images are decoded at once完全一致网络 URI含自定义网络 URI通过ImageRequestBuilder.isCustomNetworkUri判断创建NetworkImagesProgressiveDecoder并注入一个ProgressiveJpegParser负责解析 JPEG 字节流和ProgressiveJpegConfig负责决策何时解码、解码到第几次扫描。也就是说DecodeProducer是是否走渐进式解码的分流器本地图片永远走一次性解码网络图片才有机会利用渐进式扫描。四、ProgressiveJpegParser逐字节扫描 JPEG 的有限状态机NetworkImagesProgressiveDecoder的渐进能力底层依赖 ProgressiveJpegParser.java 这个解析器。它的职责是随着网络数据分块到达持续解析 JPEG 字节流识别出已经完整到达的扫描scan并告诉调用方现在可以解码到哪个位置。从源码看它是一个典型的有限状态机用整数常量表示解析状态状态含义READ_FIRST_JPEG_BYTE期望读到0xFFJPEG 起始标记的第一个字节READ_SECOND_JPEG_BYTE已读到0xFF期望读到0xD8SOI 标记READ_MARKER_FIRST_BYTE_OR_ENTROPY_DATA下一个字节可能是熵编码数据或标记首字节READ_MARKER_SECOND_BYTE上一个字节是0xFF可能是某个标记的开始READ_SIZE_FIRST_BYTE/READ_SIZE_SECOND_BYTE读取段segment长度的两个字节NOT_A_JPEG已确认不是 JPEG 数据关键实现细节校验合法性任何以0xFFD8开头的字节序列才被认为是合法 JPEG构造器注释treats any sequence of bytes starting with 0xFFD8 as a valid jpeg image否则进入NOT_A_JPEG状态后续所有parseMoreData调用直接返回false持续增量解析调用方解码器每收到一块新数据就调用一次parseMoreData(encodedImage)解析器从上次停下的位置mBytesParsed继续而不是重复扫描整个缓冲区识别扫描边界解析到 SOSMARKER_SOS标记时记一次新扫描开始解析到 EOIMARKER_EOI标记时则记录图片结束遇到0xFF00转义字节或重复0xFF时按 JPEG 规范跳过避免把熵编码数据误判为标记产出解码建议getBestScanNumber()返回当前已完整到达的最佳扫描编号getBestScanEndOffset()返回应该截取的数据偏移——解码器会在扫描边界处截断数据进行部分解码见 DecodeProducer.ktgetIntermediateImageEndOffset返回bestScanEndOffset确保每个中间帧都是完整可用的画面而不是半张撕裂图。解析过程使用 16KBBUFFER_SIZE的池化缓冲流读取数据并复用ByteArrayPool以降低内存分配开销。五、何时解码、解码到第几帧ProgressiveJpegConfig 的决策逻辑ProgressiveJpegParser只负责解析而要不要现在解码、解码到哪一帧、当前帧质量如何则由 ProgressiveJpegConfig.java 决定。这是一个接口定义了四个核心方法方法作用boolean decodeProgressively(ImageRequest imageRequest)判断该请求是否应尝试渐进解码int getNextScanNumberToDecode(ImageRequest imageRequest, int scanNumber)给定当前已解码的扫描编号返回下一个应解码的扫描编号QualityInfo getQualityInfo(ImageRequest imageRequest, int scanNumber)返回某次扫描对应的质量信息是否足够好、是否完整质量long getTimeBetweenPartialResultsMs(ImageRequest imageRequest)两次中间结果向上游传播的最小时间间隔默认 100 毫秒DEFAULT_TIME_BETWEEN_PARTIAL_RESULTS_MS 100用于节流避免每一帧都立即上屏导致频繁重绘仓库提供的默认实现是 SimpleProgressiveJpegConfig.java它通过内部接口DynamicValueConfig暴露两个可定制点getScansToDecode(ImageRequest)指定应该解码哪些扫描编号的列表。默认返回空列表含义是每个扫描都允许被解码If no specific scans to decode are provided, every scan is allowed to be decodedgetGoodEnoughScanNumber(ImageRequest)指定质量足够好的扫描编号。默认值为 0getQualityInfo判断逻辑为当前扫描编号 goodEnoughScanNumber即为isOfGoodEnoughQuality而isOfFullQuality恒为false因为最终完整质量帧由最终结果事件携带。NetworkImagesProgressiveDecoder的解码调度逻辑见 DecodeProducer.kt可以概括为收到新的中间数据块IS_PARTIAL_RESULT且图片格式为 JPEG后先调用parseMoreData解析若解析出的最佳扫描编号bestScanNumber不大于上次已调度的扫描编号说明这一帧已经解码过直接跳过若bestScanNumber小于配置要求的下一扫描编号getNextScanNumberToDecode且尚未读到 EOI 结束标记说明还没到配置允许解码的门槛帧继续等待更多数据否则记录lastScheduledScanNumber安排解码任务把这一版部分结果作为中间帧onIntermediateImageSet交给上层渲染。这套门槛帧 结束标记的双重判断保证了既不会漏掉关键帧也不会在图片还有后续扫描时提前终止渐进流程。六、完整示例showcase 应用中的渐进式 JPEG 演示官方文档推荐直接查看 showcase 示例应用中的 ImageFormatProgressiveJpegFragment.java。该 Fragment 是一个可交互的渐进式 JPEG 演示页它提供 4 个不同的测试图片来源小/中/大尺寸样本图以及一个慢速渐进式 JPEG 示例createPJPEGSlow()用于模拟不同网速下的渐进加载通过SwitchCompat开关实时切换渐进式渲染的开启/关闭渲染时通过 Vito 新 API 展示图片该示例使用VitoView.show等价配置为ImageOptions.create().progressiveRendering(mProgressiveRenderingEnabled)对应旧 Drawee API 的setProgressiveRenderingEnabled注册BaseImageListener在onIntermediateImageSet中间帧与onFinalImageSet最终帧回调中取出QualityInfo把每次渲染的时间戳、isOfGoodEnoughQuality、isOfFullQuality、quality逐行打印到调试输出区——你可以直观看到同一张图片被解码渲染了多少个渐进帧、每个帧的质量评分是多少从而验证本文前述的扫描机制。仓库中还附带了该功能的真实演示视频 01-progressive-jpegs.mp4可以直观感受低质量轮廓 → 逐渐清晰 → 最终高清的完整过程该视频同时被官网文档引用为/static/videos/01-progressive-jpegs.mp4。七、注意事项与限制综合文档说明与源码实现使用渐进式 JPEG 时需要注意以下几点必须显式开启渐进式渲染不会默认启用必须通过ImageRequestBuilder.setProgressiveRenderingEnabled(true)显式请求setImageURI等简化 API 目前不支持渐进式渲染。仅网络图片有效本地图片在DecodeProducer中会被分流到LocalImagesProgressiveDecoder一次性完整解码不存在渐进过程。依赖图片本身的编码格式只有渐进式编码的 JPEG 才能渐进显示基线编码 JPEG 在网络图片场景下也只会等待全部数据到达后一次性显示。图片仍需等待足够的数据即使开启了渐进式渲染首次画面也要等到 JPEG 头部与第一次扫描的数据到达后才能出现扫描次数越少如两次扫描渐进效果越生硬但等待时间也越短。节流与质量门槛可调默认两次中间帧的最小间隔为 100ms通过自定义ProgressiveJpegConfig如配置scansToDecode、goodEnoughScanNumber可以控制解码哪些帧、何时把结果标记为足够好从而在流畅度与清晰度之间做取舍。配合进度条等视觉反馈效果更佳showcase 示例中同时叠加了ProgressBarDrawable显示下载进度中间帧与进度条协同能给用户更完整的加载感知。结语渐进式 JPEG 是 Fresco 图片流水线中一个开箱即用但又高度可定制的特性一句setProgressiveRenderingEnabled(true)即可启用底层则由ProgressiveJpegParser字节流状态机、ProgressiveJpegConfig解码决策器与DecodeProducer网络/本地分流三者协同完成流式解码。理解这条链路后你既可以按官方文档快速接入也能通过自定义ProgressiveJpegConfig针对业务场景微调渐进帧的渲染节奏。深入阅读 DecodeProducer.kt 与 ProgressiveJpegParser.java并结合 showcase 示例动手调试是掌握这一能力的最快路径。赞分享移动开发图像处理【免费下载链接】frescoAn Android library for managing images and the memory they use.项目地址https://gitcode.com/gh_mirrors/fr/fresco点击查看免费下载相关推荐Front-End-Checklist 图像性能清单渐进式 JPEGProgressive JPEG编码实践、检测与代码审查指南Front End Checklist 图像性能清单渐进式 JPEGProgressive JPEG编码实践、检测与代码审查指南 渐进式 JPEGPro如何通过Fresco渐进式JPEG流式加载优化大型图片显示体验如何通过Fresco渐进式JPEG流式加载优化大型图片显示体验 Fresco是Facebook开源的Android图片加载库专为优化内存管理和提升图片显示性能移动开发图像处理ExoPlayer 渐进式播放Progressive Playback容器格式支持全解析与恒定比特率 Seek 实战ExoPlayer 渐进式播放Progressive Playback容器格式支持全解析与恒定比特率 Seek 实战 ExoPlayer 可以在不依赖任何自音视频移动开发上一篇蚂蚁百灵开源Ring-mini-2.01.4B激活参数实现7B级推理性能MoE架构改写效率规则下一篇Gauge插件系统深度解析如何扩展你的测试能力创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

PX4直连遥控器控制舵机:绕过飞控逻辑的PWM硬实时方案

PX4直连遥控器控制舵机:绕过飞控逻辑的PWM硬实时方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/21 2:53:34 阅读更多 →
Browser Harness 跨源 iframe 操控指南:iframe_target 目标附着与合成器层坐标点击

Browser Harness 跨源 iframe 操控指南:iframe_target 目标附着与合成器层坐标点击

Browser Harness 跨源 iframe 操控指南:iframe_target 目标附着与合成器层坐标点击 【免费下载链接】browser-harness Browser Harness | Self-healing harness that enables LLMs to complete any task. 项目地址: https://gitcode.com/gh_mirrors/br/browser-ha…

2026/9/21 2:53:34 阅读更多 →
STM32 HardFault深度解析:寄存器快照与堆栈回溯实战指南

STM32 HardFault深度解析:寄存器快照与堆栈回溯实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/21 2:52:34 阅读更多 →

最新新闻

react-admin 实时订阅实战:深入掌握 `useSubscribeToRecord` 单记录事件订阅 Hook

react-admin 实时订阅实战:深入掌握 `useSubscribeToRecord` 单记录事件订阅 Hook

react-admin 实时订阅实战:深入掌握 useSubscribeToRecord 单记录事件订阅 Hook 【免费下载链接】react-admin A frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design 项目地址: https:/…

2026/9/21 3:27:56 阅读更多 →
在 Vue 3 应用中接入 json-render DevTools:@json-render/devtools-vue 完整接入与源码解析

在 Vue 3 应用中接入 json-render DevTools:@json-render/devtools-vue 完整接入与源码解析

在 Vue 3 应用中接入 json-render DevTools:json-render/devtools-vue 完整接入与源码解析 【免费下载链接】json-render The Generative UI framework 项目地址: https://gitcode.com/GitHub_Trending/js/json-render json-render/devtools-vue 是 json-ren…

2026/9/21 3:27:56 阅读更多 →
Etherpad 自更新子系统 Tier 3 深度解析:带宽限窗口的自动升级(Auto-Update with Grace Window)

Etherpad 自更新子系统 Tier 3 深度解析:带宽限窗口的自动升级(Auto-Update with Grace Window)

后端协同办公WebSocket前端富文本 【免费下载链接】etherpad Etherpad: A modern really-real-time collaborative document editor. 项目地址: https://gitcode.com/gh_mirrors/et/etherpad 点击查看 免费下载 Etherpad 内置的"自更新子系统"&#xff0…

2026/9/21 3:27:55 阅读更多 →
lark-cli apps +plugin-list 命令完全指南:妙搭应用插件声明与安装状态核验

lark-cli apps +plugin-list 命令完全指南:妙搭应用插件声明与安装状态核验

CLIAI 技能 【免费下载链接】cli The official Lark/飞书 CLI tool, maintained by the larksuite team — built for humans and AI Agents. Covers core business domains including Messenger, Docs, Base, Sheets, Calendar, Mail, Tasks, Meetings, and more, with 200 co…

2026/9/21 3:27:55 阅读更多 →
Gradle 属性命名规范 ADR-0010:org.gradle 前缀体系下的 public/internal 与特性稳定性契约

Gradle 属性命名规范 ADR-0010:org.gradle 前缀体系下的 public/internal 与特性稳定性契约

构建工具开发工具 【免费下载链接】gradle Adaptable, fast automation for all 项目地址: https://gitcode.com/gh_mirrors/gr/gradle 点击查看 免费下载 本文是 Gradle 仓库 architecture/standards/0010-gradle-properties-naming.md 这份架构决策记录&#xff…

2026/9/21 3:27:55 阅读更多 →
V8 字符串表示体系详解:从 SeqString 到 ConsString 的内部表示、internalization 与 String Table

V8 字符串表示体系详解:从 SeqString 到 ConsString 的内部表示、internalization 与 String Table

语言运行时编译器JIT编译解释器内存管理 【免费下载链接】v8 The official mirror of the V8 Git repository 项目地址: https://gitcode.com/gh_mirrors/v81/v8 点击查看 免费下载 导读 JavaScript 中的字符串是最基础的数据类型,V8 并没有使用单一的…

2026/9/21 3:26:55 阅读更多 →

日新闻

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and …

2026/9/21 0:00:01 阅读更多 →
gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析 【免费下载链接】gin-vue-admin 🚀ViteVue3Gin拥有AI辅助的基础开发平台,企业级业务AI开发解决方案,内置mcp辅助服务,内置skills管理,…

2026/9/21 0:00:01 阅读更多 →
Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

桌面应用AI 应用插件系统 【免费下载链接】Wox A cross-platform launcher that simply works 项目地址: https://gitcode.com/gh_mirrors/wo/Wox 点击查看 免费下载 全功能插件(Full-featured Plugin)是 Wox 三类插件实现方式中能力最完整的…

2026/9/21 0:00:01 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/21 3:13:20 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/21 2:19:36 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →