☰
Flutter OH 外接纹理问题定位指南
2026/10/3 23:08:17 网站建设 项目流程

返回 Flutter OH平台 DFX 问题定位导航


1. 什么是外接纹理?

1.1 通俗解释

外接纹理(External Texture)是一种让原生侧(视频播放器、相机、动画等)把画面"喂"给 Flutter 显示的机制。

打个比方:想象一条"画面传送带":

  • 生产者(视频播放器/相机)把画面一帧一帧放到传送带上
  • 消费者(Flutter 引擎)从传送带上取画面,画到屏幕上

如果传送带出了问题(生产太快消费不过来、生产者停了、消费者卡了),画面就会黑屏、卡顿或冻结。

1.2 什么场景会用到外接纹理?

场景生产者典型问题
视频播放视频播放器插件画面黑屏/卡顿
相机预览相机插件预览画面不更新
动画播放动画插件动画卡顿
WebViewWebView 插件画面不更新
直播直播 SDK画面延迟/卡顿

1.3 问题类型速查

你看到的现象可能的原因去看哪一节
视频/相机画面黑屏纹理创建失败 / 生产者没产出帧§5
画面第一帧后不更新帧闸门开启 / Surface 销毁 / onInactive 被误触发§6, §7
画面卡顿/丢帧消费过慢 / 跳帧§8
画面拉伸/变形尺寸变更未收敛§9
应用闪退纹理释放后还在访问§10
退后台后还在耗电可见区域监控未启用§7

2. 外接纹理架构

2.1 画面传送带模型

原生生产者 Flutter 消费者 (视频/相机/动画) (Raster 线程) │ │ │ 把画面写入窗口 │ 从窗口取出画面 ▼ ▼ ┌─────────────────────────────────────┐ │ OH_NativeImage 缓冲队列 │ │ (画面传送带) │ └─────────────────────────────────────┘ │ │ │ "新画面来了!" │ 生成 GPU 图像 ▼ ▼ 通知 Flutter 重绘 画到屏幕上
角色通俗理解实现类
生产者把画面放到传送带上的人原生侧通过 surfaceId 获取窗口写入
消费者从传送带取画面画到屏幕的人OHOSExternalTexture(Raster 线程)
传送带中间的缓冲队列OH_NativeImageBufferQueue

2.2 两种渲染后端

后端什么时候用通俗解释
OpenGL ESRenderingApi = kOpenGLES用 GL 纹理显示画面
Vulkan (Impeller)RenderingApi = kImpellerVulkan用 Vulkan 信号量同步画面

注册时会输出RegisterExternalTexture api type <N> texture_id <id>,N就是渲染后端编号。

2.3 开发者 API

如果你是插件开发者,通过TextureRegistry使用外接纹理:

方法作用什么时候用
registerTexture(id)注册纹理,返回 surfaceId创建视频/相机纹理时
registerPixelMap(pixelMap)注册静态图片纹理显示一张图片
unregisterTexture(id)注销纹理不再需要时(先停生产者再注销!)
setTextureBufferSize(id,w,h)设置生产者窗口尺寸画面尺寸变化时
setExternalNativeImagePtr(id,img)替换为外部 NativeImage高级用法

3. 纹理生命周期

3.1 注册流程(创建传送带)

registerTexture(textureId) │ ├─ 创建 OH_NativeImage(创建传送带) │ ├─ 成功 → 继续 │ └─ 失败 → 日志 "OH_NativeImage_Create() failed" │ ├─ 获取 NativeWindow(获取传送带入口) │ ├─ 成功 → 继续 │ └─ 失败 → 日志 "OH_NativeImage_AcquireNativeWindow() failed" │ ├─ 获取 surfaceId(给生产者用的地址) │ └─ 失败 → 日志 "OH_NativeImage_GetSurfaceId() failed" │ └─ 注册到引擎 → 日志 "RegisterExternalTexture api type N texture_id X"

成功的日志:

I Flutter: RegisterExternalTexture api type 2 texture_id 1 I Flutter: OH_NativeImage_AcquireNativeWindow() success I Flutter: OH_NativeImage_GetSurfaceId() success, surfaceId = 12345678

3.2 注销流程(拆除传送带)

顺序很重要!必须先停生产者,再注销纹理。否则生产者还在往已拆除的传送带上放东西,会崩溃。

✅ 正确顺序: 1. 停止生产者(暂停视频/相机) 2. 调用 unregisterTexture ❌ 错误顺序: 1. 调用 unregisterTexture(传送带拆了) 2. 生产者还在写入 → 崩溃!

4. 帧调度(传送带怎么运转)

4.1 帧序号机制

引擎用两个计数器跟踪画面的生产和消费:

计数器通俗理解含义
now_new_frame_seq_num“生产了多少帧”生产者产出的帧数
now_paint_frame_seq_num“画了多少帧”消费者已绘制的帧数

如果"生产的"远大于"画了的",说明消费跟不上,引擎会主动跳帧(丢掉一些旧画面,只画最新的),保证实时性。

4.2 跳帧策略

缓冲队列大小跳帧阈值通俗解释
≤ 5size - 1普通场景,保留 1 个缓冲
> 5size * 2/3视频/相机场景,保留更多缓冲

跳帧日志:

I Flutter: external_texture skip one frame(slow consumer): ... buffer_queue_size 5 max_jank_frame 4 I Flutter: MarkNewFrameAvailable avail-seq 120 paint-seq 100 texture_id 1

skip one frame(slow consumer)= 消费太慢了,跳过了一些帧


5. 纹理黑屏/不显示

5.1 排查流程

画面黑屏 │ ├─ 搜索 "RegisterExternalTexture api type" │ └─ 没搜到 → 纹理根本没注册,检查 registerTexture 调用 │ ├─ 搜索 "OH_NativeImage_Create() failed" │ └─ 搜到了 → 创建失败,检查系统资源 │ ├─ 搜索 "OH_NativeImage_GetSurfaceId() failed" │ └─ 搜到了 → surfaceId 获取失败,纹理没真正注册 │ ├─ 搜索 "No DlImage available" │ └─ 搜到了 → 传送带上没有画面(生产者没产出帧) │ ├─ 检查原生侧是否已向 surfaceId 写入数据 │ └─ 检查 Texture 控件的 size 是否为 0 │ └─ 检查渲染后端是否匹配

5.2 常见原因和修复方法

原因通俗解释怎么修
纹理未注册压根没调用registerTexture检查注册代码
NativeImage 创建失败系统资源不足检查系统资源
生产者没产出帧视频还没开始播放 / 相机没启动确保生产者已开始写入
Texture 控件 size 为 0画面区域是 0x0检查 Widget 布局
surfaceId 不对生产者写入了错误的 surfaceId确认 surfaceId 传递正确

6. 帧闸门(Frame Gate)

6.1 通俗解释

当应用退到后台时,引擎会开启"帧闸门"——继续排空传送带上的画面(防止生产者阻塞),但不再调度重绘(反正用户看不到)。

打个比方:就像快递柜——你退后台后快递还在往柜子里放(排空队列),但你不取快递了(不渲染)。回前台后恢复正常取件。

6.2 状态切换

时机帧闸门状态日志
退后台开启(不渲染但排空)frame gate enabled, drain-only for texture X
回前台关闭(恢复正常)ExecuteReclaimRestore - restoring foreground state

6.3 排查"纹理不更新"

如果纹理在后台回前台后不更新:

  1. 搜索frame gate enabled→ 确认帧闸门是否仍开启
  2. 搜索ExecuteReclaimRestore→ 确认是否已恢复
  3. 搜索Surface REBUILT→ 确认 Surface 重建是否成功
  4. 搜索NotifyDestroyed→ 确认 Surface 是否已销毁

7. 可见区域监控

7.1 通俗解释

3.35 版本新增了"可见区域监控"功能。当 PlatformView(嵌入 Flutter 的原生组件)不可见时,可以自动暂停纹理的生产(比如暂停视频/动画),避免不可见时的无效耗电。

打个比方:就像你走开不看 TV 时,TV 自动暂停播放。

7.2 怎么启用?

默认是关闭的(enable=false),插件需要重写getPlatformViewVisibleAreaEventOptions()来开启:

// 在你的 PlatformView 子类中重写getPlatformViewVisibleAreaEventOptions():PlatformViewVisibleAreaEventOptions{return{enable:true,// 开启监控ratios:[0.0,1.0],// 可见比例阈值expectedUpdateInterval:1000,// 期望回调间隔(毫秒)onInactiveThreshold:0.0,// 可见比例 ≤ 此值时触发 onInactive()(暂停)onActiveThreshold:1.0,// 可见比例 ≥ 此值时触发 onActive()(恢复)}asPlatformViewVisibleAreaEventOptions;}// 暂停纹理生产(比如暂停视频)onInactive():void{this.videoPlayer?.pause();}// 恢复纹理生产onActive():void{this.videoPlayer?.play();}

7.3 关键日志

I Flutter: setPlatformViewVisibleAreaEventCallback surfaceId:12345, enable:true I Flutter: PlatformViewVisibleAreaEventCallback surfaceId:12345, isExpanding:false, currentRatio:0
日志含义
isExpanding:false, currentRatio:0不可见了,触发onInactive()(暂停)
isExpanding:true, currentRatio:1完全可见,触发onActive()(恢复)

8. 纹理卡顿/丢帧

8.1 排查流程

画面卡顿 │ ├─ 搜索 "skip one frame(slow consumer)" │ └─ 搜到了 → 消费过慢,引擎在跳帧 │ ├─ 检查 Raster 线程是否被其他任务阻塞 │ └─ 检查 buffer_queue_size 是否过小 │ ├─ 搜索 "MarkNewFrameAvailable avail-seq" │ ├─ avail-seq 不增长 → 生产者没产出帧 │ └─ avail-seq 增长但 paint-seq 不增长 → Raster 线程卡了 │ ├─ 搜索 "GpuReclaim" │ └─ 搜到了 → GPU 回收导致中断(详见 dfx-memory.md) │ └─ 搜索 "get error buffer queue size" └─ 搜到了 → 缓冲队列异常(>100)

9. 画面拉伸/变形

9.1 排查

搜索size change相关日志:

日志含义
size change took N frames尺寸变更在 N 帧内完成(正常)
stop size change state: frame > 10尺寸变更超过 10 帧(异常)
direct release size changed buffer缓冲尺寸变了但绘制区域没变(防拉伸)

修复:确保setTextureBufferSize和notifyTextureResizing调用一致。


10. 纹理访问崩溃

10.1 通俗解释

就像你把快递箱扔了,但还有人去箱子里拿东西——当然会出问题。

常见原因:

  1. unregisterTexture后原生侧还在写入 surfaceId
  2. 外部 NativeImage 被提前释放
  3. GPU 上下文销毁后还引用 GPU 资源

修复:先停生产者,再注销纹理:

// ✅ 正确顺序stopProducer();// 先停textureRegistry.unregisterTexture(textureId);// 后注销

详见 Flutter OH 崩溃问题定位指南 §2.6 场景 2。


11. 日志关键字速查表

搜索这个关键字含义严重程度
RegisterExternalTexture api type纹理注册—
OH_NativeImage_Create() failed创建失败高
No DlImage available无可绘制画面(黑屏)中
frame gate enabled, drain-only后台帧闸门开启正常
skip one frame(slow consumer)消费过慢跳帧中
MarkNewFrameAvailable avail-seq帧序号监控—
OnGrContextCreated texture_idGPU 上下文重建—
size change took N frames尺寸变更完成正常
PlatformViewVisibleAreaEventCallback可见区域变化—
UnRegisterExternalTexture纹理注销—
~OHOSExternalTexture纹理析构—

12. 排查清单

黑屏/不显示

  • 搜索RegisterExternalTexture确认纹理已注册
  • 搜索OH_NativeImage_Create() failed确认创建成功
  • 搜索No DlImage available确认是否有画面
  • 检查原生侧是否已向 surfaceId 写入数据
  • 检查 Texture 控件 size 是否为 0

不更新

  • 搜索frame gate enabled确认帧闸门状态
  • 搜索NotifyDestroyed确认 Surface 是否存活
  • 搜索OnGrContextCreated/Destroyed确认 GPU 上下文
  • 搜索PlatformViewVisibleAreaEventCallback确认是否被onInactive暂停

卡顿

  • 搜索skip one frame确认跳帧类型
  • 搜索MarkNewFrameAvailable avail-seq对比生产/消费序号
  • 搜索GpuReclaim确认是否伴随 GPU 回收

后台行为

  • 搜索frame gate enabled确认帧闸门开启
  • 搜索ExecuteReclaimRestore确认回前台后恢复
  • 检查onInactive/onActive是否实现

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询