视频编解码学习十二之Android疑点
一、android.view.SurfaceControl.setDisplaySurface的作用
android.view.SurfaceControl.setDisplaySurface
是 Android 系统中一个 native 层级别的 API,主要用于 设置某个物理显示屏(Display)的输出 Surface,属于 SurfaceFlinger 的一部分功能。这个 API 并不是应用开发常用的接口,而是系统或原始设备制造商(OEM)用于控制显示输出的低层接口。
🔧 作用解析
SurfaceControl.setDisplaySurface(SurfaceControl displayToken, Surface surface)
的主要作用是:
将一个 Surface 与特定的 Display(显示设备)绑定,使该 Surface 成为该 Display 的渲染目标。
也就是说,这个接口决定了 某个显示屏最终画面要显示到哪个 Surface 上,可以理解为是 SurfaceFlinger 告诉硬件:“你应该把图像输出到这个 Surface 对应的物理设备上。”
📦 参数说明
-
displayToken
: 一个代表物理 Display 的 token,通常通过SurfaceControl.getInternalDisplayToken()
或类似接口获得。 -
surface
: 目标 Surface,用于接收最终的图像内容。比如一个SurfaceTexture
或SurfaceView
所提供的Surface
。
📍 使用场景
此函数多用于系统级的图像输出控制,比如:
-
投屏系统(屏幕镜像):系统想要将主屏内容输出到一个 HDMI 显示器或虚拟显示上。
-
多屏协作/扩展显示:比如一台 Android 设备连接多个显示器时,需要绑定每个显示器的输出 Surface。
-
截图或录屏:系统通过一个 Surface 捕获整个 Display 的输出内容。
-
系统 UI 渲染控制:用于系统引导期间或锁屏、开机动画等控制显示。
🧪 注意事项
-
这个 API 是 隐藏的、系统级别的,普通应用无法直接调用。
-
使用它需要 系统权限,并且通常配合
SurfaceComposerClient
、SurfaceFlinger
或 AIDL 接口工作。 -
设置错误或参数异常可能会导致显示黑屏、卡死或输出内容不正常。
📚 相关源码路径(AOSP)
在 AOSP 源码中,你可以看到它的实现大致在以下路径中:
plaintext
复制编辑
frameworks/base/core/java/android/view/SurfaceControl.java frameworks/native/libs/gui/SurfaceComposerClient.cpp frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
具体来说,Java 层只是包装,最终会调用到 native 层通过 binder 调用 SurfaceFlinger
的 setDisplaySurface
.
二、Display是物理显示设备吗?
是的,在 SurfaceControl.setDisplaySurface
的上下文中,Display
是指物理显示设备(physical display device)。
📌 详细说明
✅ 在这个上下文中的 Display
含义:
-
指的是一个实际存在的硬件输出设备,比如:
-
内置屏幕(手机、平板的主屏)
-
外接 HDMI 显示器
-
无线显示器(Miracast、WiDi)
-
虚拟显示设备(在系统看来也被当成一个“显示”,但可能是用于录屏或投屏)
-
-
Android 系统中的
DisplayToken
(在 native 层)代表的就是这种显示设备的引用,和 Java 层Display
类不是同一个概念,但都是为了描述某个显示目标。
🎯 举个例子:
你可以通过 native 层(比如 C++ 中的 SurfaceComposerClient
)调用:
cpp
复制编辑
sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
这个 display
就是系统主屏幕的 display token,它在调用:
java
复制编辑
SurfaceControl.setDisplaySurface(display, surface);
时被用来绑定输出 surface。
这表示:“我希望把主屏幕的输出,显示到这个 surface 上。”
🔁 对比:Java 层的 Display
在 Java 层中,也有一个 android.view.Display
类,这个类通常是代表系统中抽象出来的显示输出,常用于多窗口、投屏等,但它和 SurfaceControl.setDisplaySurface
所用的 displayToken
并不直接等价。
🧠 补充:物理 Display 的获取
在系统或 native 开发中,可以通过如下方式获取 display:
cpp
复制编辑
sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); // 主屏幕 sp<IBinder> token = SurfaceComposerClient::getExternalDisplayToken(); // 外接屏幕
✅ 结论
是的,
Display
在setDisplaySurface()
中指的是 一个物理或系统级的显示设备,通常代表的是 硬件屏幕输出接口,比如内屏、HDMI、虚拟显示等。
三、display和surface是什么关系?
这是个很核心的问题,在 Android 图形系统(特别是 SurfaceFlinger
和 SurfaceControl
)中:
🎯 简明结论:
Display
是“输出目标”(比如屏幕),而
Surface
是“内容来源”(谁要被显示)。
通过 setDisplaySurface(display, surface)
,你告诉系统:
“请把这个
Surface
的内容,作为这个Display
的显示输出。”
🧱 各自的定义和角色
概念 | 定义 | 在图形系统中的作用 |
---|---|---|
Display | 代表一个物理显示设备或虚拟显示目标。 | 最终图像的去处(输出目标) |
Surface | 一个可以被写入图像内容的缓冲区,比如由应用或系统提供 | 图像内容的来源(输入图层或缓冲区) |
📦 具体关系理解:
在 Android 中,一个典型的图像渲染流程是这样的:
-
应用或系统绘制内容 → 写入某个
Surface
-
SurfaceFlinger
收集所有Surface
→ 合成(Compose) -
合成好的最终图像 → 输出到某个
Display
这个流程可以简化为:
rust
复制编辑
多个 Surface(图层) --合成--> 最终帧图像 --送出--> Display
🧩 setDisplaySurface()
的作用
这个函数的作用是:
“把这个显示设备(
Display
)的最终输出图像放入这个Surface
中。”
常用于这些场景:
-
录屏:你把一个
Surface
设置为显示的输出,这样它会收到完整的画面内容 -
投屏:将主显示的画面输出到一个外接 HDMI/无线显示的 Surface
-
多显示系统:为每个显示设备配置自己的输出 Surface
🖼️ 举个实际例子
比如你在开发一个系统级的投屏功能:
-
你创建一个虚拟显示
Display
(比如通过SurfaceComposerClient.createDisplay()
) -
你创建一个
Surface
(比如基于 SurfaceTexture 或 BufferQueue) -
你用
setDisplaySurface(display, surface)
将两者绑定
这表示:这个虚拟显示产生的画面,将写入这个 Surface 中,你可以用这个 Surface 编码、传输或其他处理。
🧠 类比理解
可以把它类比为:
-
Display
是 电视机 -
Surface
是 HDMI 线接口的输入源
你可以把某个视频源(Surface)接到电视(Display)上,让它显示出来。
✅ 总结:
项目 | Display | Surface |
---|---|---|
是什么 | 显示目标(物理或虚拟屏幕) | 内容缓冲区(应用写图像的地方) |
作用 | 显示画面的“去向” | 提供画面的“来源”或“容器” |
使用位置 | 输出、目标端 | 输入、内容端 |
SurfaceControl.setDisplaySurface(display, surface)
就是连接两者的桥梁 —— 把 谁要显示的内容(surface) 输出到 哪个屏幕(display) 上。
四、setDisplaySurface()中,当display是主屏,surface是虚拟屏的surface,是相当于把主屏的内容复制到虚拟屏上,还是会影响主屏的显示?
下面我来给你准确、深入地解析。
✅ 简要回答:
setDisplaySurface(display=主屏, surface=虚拟屏的 surface)
表示:你将主屏的最终图像输出到这个 surface 中,
✅ 是复制主屏内容(画面镜像),而不是影响主屏显示。
即:
-
主屏继续正常显示
-
你只是在“拷贝”它的画面到另一个 Surface,用于录制、投屏等目的
🧠 更详细解释:
📌 setDisplaySurface()
的行为是什么?
它不是把 surface
当作某个图层,而是把它当成 Display 的输出缓冲区(Framebuffer)。
这意味着:
-
Display 原本输出是发到物理屏的,现在你通过这个 API 拦截了这个输出,让它 输出到你指定的 Surface。
-
被指定的
surface
会接收到合成后的整帧图像(就是用户当前看到的整个屏幕),相当于屏幕的“镜像帧”拷贝。
📊 举个例子(场景对照)
场景 | display | surface | 结果 |
---|---|---|---|
录屏 | 主屏 | 一个 Surface (比如 MediaProjection 提供的) | 你会收到主屏的内容帧,但不影响主屏显示 |
投屏 | 主屏 | 外接设备的 buffer(通过网络或 HDMI 虚拟) | 外设看到主屏内容,主屏依然显示 |
反例 | 主屏 | App 提供的 Surface,并作为图层提交 | 你会替换部分图层内容,不是复制 |
🚧 注意事项(系统开发中):
-
你是在设置合成后的结果的输出目标,不是控制图层。
-
这个操作通常 需要系统权限或 Root 权限。
-
如果你只想“显示”某个 Surface 的内容,应该用
SurfaceControl.setLayer()
之类的 API,而不是setDisplaySurface()
。 -
不能频繁更改
setDisplaySurface()
,它是低频、系统级的配置操作。
📚 对应 Android 系统中的用法:
比如 Android 的录屏、投屏服务(如 SurfaceFlinger::doComposition()
的输出流程),就是:
-
使用一个虚拟
Surface
作为输出 -
设置主屏的
Display
输出到该Surface
-
继续正常渲染主屏,不会影响物理屏幕的显示
这种方式是目前 Android 中录屏、无线投屏的基础机制。
✅ 结论重申:
setDisplaySurface(主屏, 虚拟 Surface)
是一种不影响主屏显示的“画面复制”操作,
你只是“截获”了主屏的合成输出结果,输出到了另一个 Surface 中。
五、像这样把主屏内容复制到另一个surface中通常需要花多长时间?
这个问题非常关键,尤其涉及 性能、时延 和 帧同步。我们来拆解一下:
✅ 简短答案:
把主屏内容复制到另一个
Surface
(如录屏、投屏用的)通常不需要额外的耗时,拷贝几乎是“零成本”的 DMA 或硬件合成输出操作。延迟一般控制在 1~2 帧内(16~33ms 左右)甚至更低。
🧠 为什么这么快?背后发生了什么?
这并不是“CPU 上逐像素拷贝”,而是:
🔧 GPU/HWComposer/SurfaceFlinger 一体完成:
-
SurfaceFlinger
将所有 Layer 合成为一帧时,会根据设置:-
将这一帧输出给物理 Display(正常显示)
-
同时输出到你指定的
Surface
(投屏、录屏)
-
✅ 输出操作不是“复制”,而是“多路输出”
-
通常使用 硬件层的双输出/双写 DMA(Dual-Write DMA) 或 GPU多目标渲染
-
输出到显示器 + 输出到 Surface 可以并行进行
-
所以这个操作几乎不会增加帧合成的总时长(不会影响 UI 响应)
📉 实测延迟范围(参考值):
场景 | 输出方式 | 延迟(帧) | 延迟(毫秒) |
---|---|---|---|
系统级录屏(MediaProjection) | framebuffer copy / virtual display | 1~2 帧 | ~16ms - 33ms |
Wireless Display(Miracast) | surface + 编码 + 网络发送 | 2~4 帧 | ~30ms - 60ms |
HDMI 拓展屏幕复制 | 多路输出 | 接近 0 帧 | < 10ms |
🔍 有哪些因素可能拖慢这个操作?
虽然复制本身快,但以下环节可能造成延迟:
-
Surface 所连接的 BufferQueue 阻塞
-
如果下游(编码器、网络)处理慢,
surface
的消费端来不及消费,就会卡住生产者
-
-
图像格式转换
-
如果目标
Surface
只接受某种格式(如 YUV),而主屏是 RGB,系统需要转换格式,会增加 GPU 负担
-
-
目标
Surface
在另一个进程或 Binder 中跨进程通信-
会带来一次内核用户态上下文切换,产生 1~2ms 延迟
-
-
设备性能差、GPU 压力大
-
可能会造成合成时间整体变慢(但这是系统问题)
-
🔧 优化建议(如你在开发系统服务):
-
使用 硬件合成直出路径(如通过 HWC2 设置 Dual output)
-
减少
Surface
Buffer 格式转换(保持与 Display 相同) -
为目标
Surface
设置较高的BufferCount
(避免生产端等待) -
使用
setDisplaySurface()
而非copyLayer()
,避免 CPU 拷贝
✅ 总结
问题 | 回答 |
---|---|
会不会影响主屏显示? | ❌ 不会,属于多路输出,不干扰主屏 |
是 CPU 复制吗? | ❌ 不是,是 GPU 或 DMA 输出 |
速度有多快? | ✅ 通常在 1 帧内完成,约 16~33ms 延迟 |
有哪些性能影响因素? | Surface 堵塞、格式转换、跨进程通信等 |
六、延迟在什么范围内,才可以玩实时游戏?
你正在触及实时图像传输(如投屏游戏、云游戏、远程控制等)系统中的核心指标之一:端到端延迟。
✅ 简明结论
🎮 要实现“可玩”的实时游戏体验,总延迟应控制在 60ms 以下,理想情况在 30ms 以内。
🎯 延迟的等级划分(针对实时交互游戏)
延迟范围 | 用户感知 | 游戏体验 |
---|---|---|
< 30ms | 几乎无感知,媲美本地 | ✅ 理想、顺畅 |
30~60ms | 有轻微感觉,但可接受 | ✅ 可玩、流畅 |
60~100ms | 有明显延迟感 | ⚠️ 勉强能玩 |
> 100ms | 明显卡顿/延迟严重 | ❌ 不适合玩游戏 |
🧱 实时游戏延迟的组成部分(链路分析)
以“把主屏画面复制到另一个 surface 上用于远程实时游戏”为例,总延迟来自多个阶段:
阶段 | 操作 | 典型耗时 |
---|---|---|
🖼️ 渲染阶段 | 游戏帧在主屏被绘制完成 | 16ms(60fps) |
📤 复制阶段 | 主屏内容复制到目标 Surface | 12ms(DMA/GPU) |
🎞️ 编码阶段 | H.264/H.265 视频编码 | 5~15ms(硬编) |
🌐 传输阶段 | 网络发送(局域网或无线) | 1~20ms |
📥 解码阶段 | 接收方解码画面 | 5~15ms |
🖥️ 显示阶段 | 解码后帧显示到屏幕 | 16ms(60fps) |
总计 | 30~80ms |
✅ 如何优化以达成实时游戏体验?
系统设计角度:
项目 | 推荐做法 |
---|---|
Surface 数据复制方式 | 使用 setDisplaySurface() ,避免 CPU 复制 |
编码方式 | 使用 硬件编码器(MediaCodec) 并调成低延迟模式 |
编码参数 | 使用低 GOP(关键帧间隔)、低缓存 |
网络传输 | 避免 Wi-Fi 干扰,使用 5GHz Wi-Fi,支持 QoS |
解码端 | 使用硬解码 + 提前渲染机制 |
显示刷新机制 | 使用 SurfaceView + Choreographer 提前预渲染 |
🎮 示例:Steam Link / Moonlight 等系统的延迟设计
系统/协议 | 理想延迟 | 技术手段 |
---|---|---|
Moonlight + NVIDIA GameStream | ~30ms | H.265 硬编 + UDP + 低延迟模式 |
Steam Link | 4060ms | 软件优化 + 局域网传输 + 减缓压缩 |
Android 无线投屏 | 50100ms | setDisplaySurface() + MediaProjection |
云游戏系统(如Xbox云游戏) | 80120ms | 云端计算 + 网络优化 |
✅ 结论
🎮 如果你希望在屏幕复制(主屏 -> surface -> 编码传输)场景中实现可玩级别的实时游戏体验:
必须确保 端到端延迟 ≤ 60ms
系统设计上建议使用:
SurfaceControl.setDisplaySurface()
(高效复制)硬件编码(低延迟 H.264/H.265)
局域网(如 5GHz Wi-Fi)
减少 BufferQueue 滞后与双缓冲
七、渲染和显示的区别
这是一个图形系统(无论是 Android、游戏引擎,还是桌面系统)中非常基础但又极易混淆的两个概念:
✅ 简明区分:
概念 | 渲染(Rendering) | 显示(Display) |
---|---|---|
作用 | 生成图像(把场景变成一帧帧像素) | 将生成的图像呈现在屏幕上 |
阶段 | 图像 生产 阶段 | 图像 消费 阶段 |
发生位置 | GPU(或 CPU 渲染引擎) | 显示控制器 / SurfaceFlinger / HWC |
产物 | Frame Buffer(帧缓冲) | 屏幕上的像素内容 |
频率 | 游戏引擎控制(如 60fps、120fps) | 通常与屏幕刷新率同步(如 60Hz VSync) |
🧠 更深入解释
🖼️ 渲染(Rendering)是什么?
-
是“画图”的过程,把游戏场景、UI、视频帧等“抽象内容”变成 具体像素
-
这个过程可能由:
-
应用(Canvas、Skia、OpenGL、Vulkan)
-
系统组件(WindowManager、SurfaceFlinger)
-
游戏引擎(Unity、UE)
-
举例:
把一个按钮绘制成图像,把 3D 场景变成一帧 1080p 图像帧,这都是渲染。
📺 显示(Display)是什么?
-
是把“已经生成好的像素”送到屏幕上、肉眼可见的过程。
-
通常通过:
-
SurfaceFlinger 合成所有图层 → 输出一帧
-
通过 HWC / DRM 驱动 → 输出到显示设备
-
屏幕扫描刷新一次(VSync)
-
举例:
游戏已经生成了一帧图像,但要等到下一次屏幕刷新(比如 16.6ms 后)才能真正出现在屏幕上。
🎯 类比理解:做 PPT 的比喻
-
渲染 = 你在 PowerPoint 中画了一张幻灯片
-
显示 = 你按下播放键,让观众在投影仪上看到它
🔁 在 Android 中的对应:
流程阶段 | 渲染还是显示? | 说明 |
---|---|---|
App 使用 Skia 绘制 UI | 渲染 | 使用 GPU 或 CPU 绘制到 Surface 的缓冲区中 |
Surface 被提交 | 渲染 | 图像还没显示,只是准备好了 |
SurfaceFlinger 合成 | 渲染(最后阶段) | 多个图层合成为一帧 |
Frame 输出到屏幕 | 显示 | 真正肉眼可见,需要 VSync 配合 |
🔄 典型延迟链(从渲染到显示)
-
App 发起 draw call →
RenderThread
开始渲染 →Surface
中 buffer 填满 -
buffer 被提交给
SurfaceFlinger
-
等待下一次 VSync 时机,
SurfaceFlinger
合成所有图层 -
合成帧提交给显示控制器 → 屏幕刷新 → 用户看到内容
通常这整个过程叫做 "Frame pipeline",耗时约为 16.6ms ~ 33ms。
✅ 总结:
问题 | 渲染 | 显示 |
---|---|---|
是干什么的? | 画出图像 | 把图像呈现出来 |
谁负责? | App / GPU / 系统组件 | SurfaceFlinger + HWC + 屏幕 |
图像状态 | “生成了但未显示” | “显示出来了” |
是否可见? | ❌ 不可见(存在于内存) | ✅ 可见(出现在屏幕) |
八、如此看来,投屏做不到延迟低于30ms
你说得很敏锐 —— 常规的投屏系统确实很难做到低于 30ms 的延迟。但是否**“做不到”**,要具体看实现方式、硬件支持程度、网络状况等。
✅ 简明结论:
🎯 传统投屏系统(如 Miracast、AirPlay、Cast)难以低于 30ms,
但高度优化的投屏方案(例如 NVIDIA GameStream + Moonlight)在特定条件下可以做到 20~30ms 延迟,但也极为挑剔环境和实现。
📉 为什么传统投屏难低于 30ms?
投屏链路必须经过以下阶段(每步都吃延迟):
阶段 | 典型耗时 | 说明 |
---|---|---|
渲染 | 16ms | 游戏/系统生成一帧图像(60fps) |
复制到 Surface | 1~2ms | 使用 setDisplaySurface 或虚拟 Display |
编码(硬编) | 5~10ms | 通常是 H.264 baseline |
网络传输 | 5~15ms | 取决于 Wi-Fi / LAN 稳定性 |
解码(硬解) | 5~10ms | 通常使用 MediaCodec 或 GPU |
显示刷新 | ~16ms | 等下一次 vsync,才真正看到帧 |
🔗 总计:约 40~70ms(这是典型系统投屏的真实表现)
🎯 有哪些场景“接近”或“突破”30ms延迟?
系统 / 技术 | 条件 | 延迟范围 |
---|---|---|
✅ Moonlight + NVIDIA GameStream | 局域网、RTX GPU、优化参数 | 20~35ms |
✅ Steam Link(局域网) | 5GHz Wi-Fi + 硬编 + TV 硬解 | 30~45ms |
⚠️ Android MediaProjection + 硬编 | 基于 setDisplaySurface() | 50~80ms |
⚠️ AirPlay / Miracast | 无优化 | 70~120ms |
👉 注意:
-
即使你用最快的编码器,显示刷新(VSync)+网络波动会拖住延迟底线。
-
编码器通常也需要一些 帧缓存,比如 2 帧(就是额外 30ms)。
✅ 结论:能不能做到低于 30ms?
问题 | 答案 |
---|---|
普通投屏能否低于 30ms? | ❌ 几乎不可能(Miracast、AirPlay 等) |
特定方案能否做到? | ✅ 有条件可行(如 Moonlight + 局域网) |
需要什么支持? | - 低延迟编码(快速帧间编码) - 高带宽低干扰 Wi-Fi(最好是 5GHz) - 最小化 buffer、VSync 对齐 - 接收端支持快速解码和低延迟渲染 |
🛠️ 补充建议:如果你自己做投屏系统
你可以考虑如下策略来尽量压缩延迟:
-
使用
setDisplaySurface()
拦截主屏输出,避免在应用层采集 -
使用 MediaCodec 硬编,开启
low-latency
profile -
GOP 设置为 1~2(低 B 帧甚至无 B 帧)
-
目标帧率拉高到 90fps(加快帧传递频率)
-
接收端用 SurfaceView +
async
解码路径 -
推流协议使用 RTP over UDP(而非 RTSP/TCP)
九、高分辨率的原始图像,通过低分辨率的设备用setDisplaySurface投屏到高分辨率的设备,会丢失吗?
触及到了 分辨率适配 和 像素数据保真度 的核心。
✅ 简短回答:
是的,如果你使用
setDisplaySurface()
将一个低分辨率设备的原始图像投屏到高分辨率显示器,最终投出的画面分辨率将受限于原设备的显示分辨率,会失真或模糊,并不会自动变得高清。
📌 为什么会“丢失”高分辨率?
情况还原:
-
你在一个 低分辨率设备(比如 720p 手机)上运行应用
-
使用
setDisplaySurface()
将 主屏输出投到一个高分辨率设备(比如 4K 显示器或虚拟屏) -
你以为能在高分屏上显示更清晰图像,但结果发现仍然是 720p 放大后的效果
原因在于:
点 | 解释 |
---|---|
✅ setDisplaySurface() 拷贝的是合成结果 | 它不会重新渲染或生成高分图像,而是复制低分屏渲染完的最终帧 |
⚠️ 原始渲染内容已是低分辨率 | Android 上应用或系统界面是基于设备 DPI 和屏幕大小渲染的,渲染目标分辨率就是低的 |
❌ 不会自动“高清化” | 除非你在原设备上以更高分辨率渲染图像(通常不可能) |
🧠 类比理解:
这就像你拿一个 720p 的截图,在 4K 显示器上全屏播放 —— 画面还是糊的,像素已经定死了。
✅ 如何判断你最终的投屏分辨率是多少?
你可以通过以下方式验证:
方法 1:查看系统的实际显示分辨率
bash
复制编辑
adb shell dumpsys SurfaceFlinger
在输出中查找你绑定的 Surface
的显示分辨率,以及主屏的 DisplayDevice
信息。
方法 2:通过 MediaCodec 编码时查看输入帧大小
-
如果你设置
setDisplaySurface(surface)
给一个MediaCodec
的input surface
,那编码器收到的帧分辨率就是源设备的屏幕分辨率,不会自动 upscale。
✅ 如何实现“清晰的高分辨率投屏”?
如果你希望:
“用低分辨率设备运行 App,但在高分屏上看到更清晰的图像”
你需要 重新设计渲染路径,如下:
方法 | 效果 | 代价 |
---|---|---|
❌ 直接 setDisplaySurface() | 画面模糊 | 简单,低分辨率限制 |
✅ 使用虚拟 Display 并指定高分辨率 | 高清可行 | 要求应用支持多分屏 |
✅ App 主动渲染到高分辨率 Surface | 高清 | 需改造 App 或引擎 |
✅ 投送原始图像数据(非屏幕采集) | 原始质量 | 通常用于图像类 App |
🎯 示例场景
场景 | 是否可获得高清图像? | 原因 |
---|---|---|
用 setDisplaySurface() 投屏手机 UI | ❌ 模糊,低分限制 | 源图像已是低分 |
用虚拟 Display 指定 1080p 渲染 | ✅ 高清(如果 App 支持) | 渲染目标是高分 |
游戏使用 Vulkan 渲染到 4K Surface | ✅ 高清 | 自定义渲染控制 |
✅ 总结:
问题 | 答案 |
---|---|
setDisplaySurface() 会丢失分辨率吗? | ✅ 是,受限于源屏幕的分辨率 |
会自动高清化吗? | ❌ 不会 |
如何获得高清效果? | ✅ 使用虚拟 display 或高分渲染 |
十、创建了高分的虚拟设备,同时虚拟设备要和低分的主设备同步,会有什么影响?
这个问题触及到了系统合成路径、刷新策略、buffer同步等多个 Android 图形子系统的核心点。我们分步骤来讲:
🎯 场景还原
你现在有两个 Display:
-
主屏(低分辨率,比如 1280x720)
-
虚拟屏(高分辨率,比如 1920x1080 或 4K)
你希望这两个屏幕同步显示相同内容(例如镜像主屏到虚拟屏),但由于它们分辨率不同,系统需要做缩放或重新合成。
✅ 会产生的主要影响
类别 | 影响情况 | 原因说明 |
---|---|---|
📉 性能开销 | ✅ 明显增加 GPU/合成压力 | 系统需要额外执行一次 缩放或 re-composite |
⏱️ 延迟 | ✅ 可能 增加 1 帧以上延迟 | 同步时需要等待 buffer/合成对齐 |
🔁 同步 | ⚠️ 不一定能做到真正的帧级同步 | 两个 Display 的 VSync 信号可能不同步 |
📐 清晰度 | ✅ 虚拟屏虽是高分,但内容是低分缩放上去 | 除非应用对虚拟屏单独高分渲染 |
🌀 帧率差异 | ⚠️ 如果两个屏幕刷新率不同,会导致卡顿或撕裂 | 例如主屏 60Hz,虚拟屏 30Hz 或 90Hz |
🔍 细节分析:SurfaceFlinger 是怎么处理的?
👉 当你开启镜像模式(或自己同步绘制两个屏幕):
-
App 渲染的是 720p 分辨率
-
系统将这帧图像:
-
输出到主屏,照常显示
-
同时缩放到虚拟屏的分辨率(如 1080p)并显示
-
SurfaceFlinger 面临的问题:
问题 | 表现 |
---|---|
Buffer 尺寸不匹配 | 需要 GPU 或硬件合成器执行 scale |
两屏 vsync 不一致 | 可能导致 tearing、掉帧 |
Buffer 填充与提交延迟 | 合成管线拉长,丢帧概率升高 |
🧪 举个真实例子
比如你有个 App,渲染一帧 720p 界面,显示在两个屏幕上:
主屏 720p:完美适配
虚拟屏 4K:SurfaceFlinger 要将内容拉伸到 3840x2160,GPU 消耗上升,部分设备甚至用软件做缩放,非常慢
✅ 如何减少影响?
方法 | 效果 | 说明 |
---|---|---|
使用虚拟 Display 单独渲染高分内容 | ✅ 真正高清、不卡顿 | 代价是需要 App 支持多分屏 |
限制虚拟屏分辨率接近主屏 | ⚠️ 清晰度受限,性能轻 | 保证合成路径一致性 |
不做镜像,而是单独推流内容到虚拟屏 | ✅ 更灵活 | 适合投屏/游戏/视频场景 |
使用硬件合成模块(HWC2 + Display HAL) | ⚠️ 设备依赖强 | 一些 SoC 有 mirror support |
✅ 总结结论
问题 | 是否会发生? | 说明 |
---|---|---|
会不会增加合成和性能开销? | ✅ 明显会 | 多一次缩放、双屏合成开销 |
会不会导致同步问题、撕裂? | ⚠️ 可能会 | 两屏 vsync 不同步是常态 |
虚拟屏内容会更清晰吗? | ❌ 如果仅是镜像 | 渲染内容决定清晰度 |
虚拟屏能不能独立显示高分图像? | ✅ 可以 | 需要专门渲染逻辑 |
🎯 建议你考虑的问题:
-
你是做 UI 镜像?游戏镜像?视频推送?
-
你控制的是 App 层,还是系统层?能不能控制合成/渲染方式?
-
虚拟 Display 是用
DisplayManager.createVirtualDisplay()
创建的吗?
十一、如果不要求虚拟屏和主屏同步,只是把高分的虚拟屏投屏到高分的显示设备上呢
如果你不要求主屏和虚拟屏同步,而是:
✅ 只在高分虚拟屏上渲染内容,并把它独立投屏到一个高分显示设备上
那么,你可以做到:
✅ 高清显示、低延迟、高质量输出!
这实际上是最推荐的方式,比“主屏镜像+缩放”更清晰、更高效,尤其适合:
-
无线投屏(如 DLNA / 自研协议)
-
扩展屏应用(演示模式、PPT、游戏)
-
自研图形应用输出到远程大屏幕
🔧 实现逻辑(推荐路径):
-
使用 DisplayManager#createVirtualDisplay() 创建一个高分辨率虚拟显示设备
java
复制编辑
VirtualDisplay virtualDisplay = displayManager.createVirtualDisplay( "MyVirtualDisplay", 1920, 1080, 320, surface, // 用于接收该显示内容的 Surface(可用于编码) DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC );
-
指定该
VirtualDisplay
的Surface
作为编码输入,使用MediaCodec
进行实时硬编码(比如 H.264) -
将编码结果通过你自己的网络协议(比如 RTP / RTMP / WebRTC / Socket)发送到远端设备
-
在高分显示设备上解码并显示该视频流(1080p / 4K)
✅ 好处对比
点 | 主屏镜像 + 缩放 | 独立高分虚拟屏 |
---|---|---|
图像清晰度 | ❌ 受限于主屏分辨率 | ✅ 高分原始渲染 |
渲染控制 | ❌ 无法分离 | ✅ 可单独绘制 |
多屏逻辑 | ❌ 共用 UI 配置 | ✅ 独立 UI、逻辑 |
延迟控制 | ❌ 被系统帧同步影响 | ✅ 渲染→编码直通 |
可扩展性(多屏) | ❌ 很难 | ✅ 可以创建多个虚拟屏 |
🎮 举个典型例子
假设你做一个“云游戏客户端”:
-
主屏只显示菜单,不参与画面渲染
-
游戏画面渲染到虚拟屏(比如 1080p 60fps)
-
编码后推流到远端显示设备(如电视)
🟢 结果是:
-
主屏不卡顿、不发热
-
虚拟屏保持高清、高帧率、高效率传输
⚠️ 注意事项
问题 | 解决方法或注意点 |
---|---|
如何让 App 渲染到虚拟屏? | 创建新的 Context.createDisplayContext() 或用 Presentation |
虚拟屏没有输入事件? | 正常,不需要交互;如需交互可虚拟输入注入 |
虚拟屏能 4K 吗? | ✅ 可指定 3840x2160,但 GPU 带宽和编码器要跟得上 |
✅ 总结
你的目标 | 可行性 | 建议 |
---|---|---|
高分虚拟屏 → 投屏到高分设备 | ✅ 完全可行 | 推荐使用 VirtualDisplay + MediaCodec 路径 |
不同步主屏,独立渲染内容 | ✅ 高效、清晰 | 不走主屏镜像,性能更优 |
高清投屏,适配远端显示 | ✅ 控制渲染分辨率即可 | 编码传输控制帧率、码率 |
十二、点亮虚拟屏能达到像点亮主屏一样防止手机休眠的效果吗?
你的关注点是:
点亮虚拟屏是否可以像点亮主屏一样,防止手机进入休眠状态(Doze / Suspend / 黑屏)?
✅ 简短结论:
不可以直接等效防止休眠,虚拟屏本身不能阻止系统休眠,但可以通过特定方法间接达到防止休眠的效果。
📌 为什么虚拟屏不能自动防止休眠?
原因 | 解释 |
---|---|
虚拟 Display 不被视为物理屏幕 | 它不会像主屏一样参与电源管理策略 |
系统休眠判断基于主屏状态 | 即使虚拟屏在“输出内容”,主屏息屏或用户无操作,仍可能触发 Doze 或 Suspend |
虚拟屏没有“用户可见性”信号 | 没有接触/亮度/唤醒事件驱动系统保持活跃状态 |
✅ 怎么样才能 真正防止休眠?
你需要组合使用 系统唤醒机制,比如:
方法 | 是否推荐 | 原理 |
---|---|---|
PowerManager.WakeLock | ✅ 推荐 | 阻止 CPU 或屏幕休眠(需权限) |
FLAG_KEEP_SCREEN_ON | ⚠️ 仅限主屏有效 | 虚拟屏不生效 |
ForegroundService + WakeLock | ✅ 常见做法 | 在后台长时间保持活跃 |
播放音频或 MediaProjection | ⚠️ 非常规方法 | 一些厂商限制少时有效 |
✅ 推荐实现方式:使用 WakeLock
java
复制编辑
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); WakeLock wakeLock = powerManager.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "YourApp:VirtualDisplayWakeLock" ); wakeLock.acquire(); // 可选超时参数
如果你只需要防止 CPU 休眠而不需要点亮屏幕:
java
复制编辑
PowerManager.PARTIAL_WAKE_LOCK
记得使用完后 wakeLock.release()
,否则会造成耗电。
🧠 示例场景:你在开发投屏服务
场景 | 是否能阻止休眠? | 备注 |
---|---|---|
只创建虚拟屏 + 开始编码推流 | ❌ 不能 | 虽然图像在输出,但主屏休眠会中断编码 |
虚拟屏 + WakeLock + 前台服务 | ✅ 可以 | 这是推荐做法,适配性最强 |
虚拟屏 + 屏幕共享(MediaProjection) | ⚠️ 不一定 | 部分系统可阻止休眠,但不可依赖 |
❗ 特别提醒:Doze 模式下不保证行为稳定
某些厂商(如华为、小米)在电池策略中,会强制挂起后台虚拟屏服务,除非:
-
你是前台服务(通知栏常驻)
-
App 被加入电池白名单
-
获取了
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
权限(需要手动授权)
✅ 总结
问题 | 是否成立 | 建议方案 |
---|---|---|
虚拟屏会自动阻止系统休眠吗? | ❌ 不会 | 加 WakeLock |
虚拟屏保持内容输出会延长设备唤醒时间吗? | ⚠️ 不一定 | 不稳定,视厂商实现 |
如何确保系统不黑屏 / 不挂起 App? | ✅ 前台服务 + WakeLock | |
虚拟屏是否能用于长时间输出到显示设备? | ✅ 可以,但需配合防休眠机制 |
十三、通过电脑adb命令,用app_process拉起的进程,系统休眠后也会不工作,加 WakeLock确定能解决吗?
涉及到 app_process 启动的进程是否能在系统休眠后继续运行 —— 我们来深入拆解:
🎯 问题背景总结:
你用的是类似如下命令:
bash
复制编辑
adb shell app_process /system/bin com.example.YourMainClass
-
这种方式绕过了普通
Activity
/Service
启动路径 -
通常不受 Android 应用生命周期限制,但也不受系统 power manager 自动保护
现在你想确认:
如果系统休眠(息屏 / Doze / Suspend),这个进程是否会停止?如果加 WakeLock,能否解决?
✅ 简洁结论:
是的,理论上你可以使用
WakeLock
保持 CPU 运行,即使系统休眠,app_process 启动的进程也可以持续工作。
⚠️ 但前提是:
-
你必须获取 正确类型的 WakeLock
-
运行环境有权限申请 WakeLock
-
该进程没有被 SELinux 限制调用 PowerManager
✅ 你该怎么做?
🧩 步骤一:让进程获取 WakeLock
即使你通过 app_process
启动,只要你创建了 Java 进程并能访问系统服务,你可以这样写代码:
java
复制编辑
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::KeepAlive"); wakeLock.acquire();
-
PARTIAL_WAKE_LOCK 是关键!它不要求屏幕亮,但能防止 CPU 进入休眠。
-
你不需要
SCREEN_DIM_WAKE_LOCK
等类型,因为你可能没有界面。
🔐 权限要求:
你必须在代码中拥有:
xml
复制编辑
<uses-permission android:name="android.permission.WAKE_LOCK"/>
但对于 app_process
启动的进程,没有 AndroidManifest.xml
,所以你需要:
-
在 shell 用户或 system 用户下运行(拥有
WAKE_LOCK
权限) -
或使用
pm grant
赋予权限(如果你用 app 包运行)
bash
复制编辑
adb shell pm grant com.example.yourapp android.permission.WAKE_LOCK
⚠️ 注意:不解决以下几种“休眠”问题
休眠类型 | WakeLock 有效? | 说明 |
---|---|---|
屏幕息屏 | ✅ 有效 | PARTIAL_WAKE_LOCK 可以保持后台运行 |
Doze 模式 | ✅ 有效(需前台进程) | 但某些厂商会强杀 |
App standby | ✅ 有效 | 前提是你不通过应用层启动 |
深度挂起(suspend) | ⚠️ 设备依赖,不总有效 | 某些设备强制 system suspend,WakeLock 无效(尤其是长时间息屏后) |
🧠 补充:你应该避免的情况
错误做法 | 后果 |
---|---|
用 SCREEN_DIM_WAKE_LOCK 无界面时 | 无效、被忽略 |
不获取 WakeLock | 系统一旦 Doze 或 CPU suspend,app_process 启动的进程会被冻结或终止 |
依赖 Java Thread.sleep 来保持 | 会被系统挂起,不会真正运行 |
✅ 建议实践方案
如果你正在做的是:
-
开发一个通过 ADB 启动的常驻服务(例如性能监控、远程指令、投屏、流媒体处理)
-
不使用标准 Activity/Service 框架
那么最佳组合是:
功能 | 技术方案 |
---|---|
后台运行 | app_process + PARTIAL_WAKE_LOCK |
防休眠 | 使用 WakeLock.acquire() 持续持有 |
防杀进程 | 推荐使用 nohup 或 app_process & + 守护进程机制 |
稳定性增强 | 可搭配使用 ForegroundService (如用 shell 启 app 服务) |
✅ 结论总结:
问题 | 是否成立? | 备注 |
---|---|---|
app_process 启动的进程休眠后会暂停? | ✅ 是的 | CPU suspend 后会冻结 |
加 WakeLock 能防止进程被冻结? | ✅ 有效 | 使用 PARTIAL_WAKE_LOCK |
所有设备都一定有效? | ⚠️ 不一定 | 厂商可能有特殊电源策略 |
无界面程序也能拿到 WakeLock? | ✅ 可以 | 只要有权限并运行在有能力用户下 |
相关文章:
视频编解码学习十二之Android疑点
一、android.view.SurfaceControl.setDisplaySurface的作用 android.view.SurfaceControl.setDisplaySurface 是 Android 系统中一个 native 层级别的 API,主要用于 设置某个物理显示屏(Display)的输出 Surface,属于 SurfaceFlin…...
绘制时间对应的数据曲线
头文件#pragma once #include "ChartControl.h" #include <vector> #include "DBOperate.h&...
HTTP 请求中 Content-Type 头部
HTTP 请求中 Content-Type 头部可以设置的各种不同的传输格式。multipart/form-data 只是其中一种,主要用于传输包含文件的数据。 以下是一些常见的 HTTP 请求体的 Content-Type 及其用途: 常见的数据传输格式 (Content-Type) 列表: application/json: 描述: 用于传输 JSO…...

spring boot Controller 和 RestController 的区别
spring boot Controller 和 RestController 的区别 5.3.1常用注解 Spring MVC控制器中常使用的注解有如下几种。 Controller Controller 标记在类上。使用Controller 标记的类表示是Spring MVC的Controller对象。分发处理器将会扫描使用了该注解的类,并检测其中的…...

LeRobot 项目部署运行逻辑(七)—— ACT 在 Mobile ALOHA 训练与部署
全部流程为:硬件配置 -> 环境安装 -> 遥操作数据采集 -> 数据集可视化 -> 策略训练 -> 策略评估 在之前的笔记中已经完成了绝大部分,最后再记录一下最后的训练部署,算是最简单的部分了 目录 1 ACT 训练 2 ALOHA 部署 3 更…...

高频交直流电流测量技术:射频PA与MEMS测试的简单解决方案
随着5G通信和智能传感器技术的快速发展,传统电流测量手段已难以满足现代电子设备的测试需求。最新一代交直流混合测量探头通过突破性的技术创新,正在重新定义射频功率器件和微机电系统的测试标准。 射频功率放大器测试新范式: 在5G基站等高…...

原生微信小程序 textarea组件placeholder无法换行的问题解决办法
【问题描述】 微信小程序原生代码,使用文本域,placeholder使用\n 没有效果,网上找了一堆方案说使用 也没有效果 最后在一个前端大佬博客,找到解决办法,CSS设置word-wrap: break-word; white-space: pre-line; 【解决办…...

网络协议分析 实验二 IP分片与IPv6
文章目录 索引及重要内容实验2 IP 高级实验实验2.1 IPv4协议分片实验实验2.2 IPV6协议实验2.3 ARP初级 索引及重要内容 实验2 IP 高级实验 实验2.1 IPv4协议分片实验 icmp的不可达报文 实验2.2 IPV6协议 实验2.3 ARP初级 arp –a 查看ARP缓存表内容 arp –s IP地址(格式&…...

QT6 源(101)篇一:阅读与注释 QPlainTextEdit,其继承于QAbstractScrollArea,属性学习与测试
(1) (2) (3)属性学习与测试 : (4) (5) 谢谢...
【ROS2】编译Qt实现的库,然后链接该库时,报错:/usr/bin/ld: XXX undefined reference to `vtable for
【ROS】郭老二博文之:ROS目录 1、问题描述 在ROS2工程中,编译使用Qt实现的库,在其它ROS2包链接该库时,报错: /usr/bin/ld: XXX undefined reference to `vtable for2、原因分析 查看链接失败的几个函数接口都是,信号函数(signals 标记的函数)。因为信号函数都只有定…...

工业大数据的定义
目录 工业大数据的定义 工业大数据发展历程 工业大数据的特征 工业大数据的处理流程 工业大数据在处理上面临的挑战 工业大数据的有效处理方案 工业大数据处理相关案例 数益工联 x TDengine 中天钢铁 x TDengine 广州某企业工业互联网项目 x TDengine 格创东智 x TD…...

126.在 Vue 3 中使用 OpenLayers 实现绘制正方形、正三角形、正五边形
🗺️ 项目效果(先看效果) 支持点击按钮绘制图形,效果如下: ✅ 正三角形 ✅ 正方形 ✅ 正五边形 ✅ 支持停止绘制 📌 前言 在 Web GIS 的实际项目中,我们经常需要绘制一些规则的图形,比如:正方形、正三角形、正五边形等,这在标注地块、规划图形、布设区域等场景中都…...
大数据 vs. 小数据:AI项目中数据规模与质量的权衡之道
大数据 vs. 小数据:AI项目中数据规模与质量的权衡之道 引言:数据时代的辩证法则 在AI领域,一个根本性悖论日益凸显:数据规模与质量往往呈现此消彼长的关系。Google Brain团队的研究揭示,当数据规模增长10倍时&#x…...
互联网大厂Java求职面试实战:Spring Boot到微服务全景解析
💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精通 2. 我的免费工具站: 欢迎访问 https://tools-6wi.pages.dev/ 😁 3. 毕业设计专栏,毕业…...
最新版VSCode通过SSH远程连接Ubuntu 16.04等旧版Linux的方法
根据官方文档Remote Development FAQ - Can I run VS Code Server on older Linux distributions?,自Visual Studio Code 1.99起,VSCode Remote - SSH Server由于运行库依赖更新,会无法连接到旧版Linux发行版上。但是好在官方在文档中还给了…...
SQL Server中delete table和truncate table删除全表数据哪个快?
目录 1.TRUNCATE 2.DELETE 总结 在 SQL Server 中,DELETE 和 TRUNCATE 都可以用来删除表中的数据,但它们执行的方式和性能影响是不同的。这里简要说明两者的区别以及哪个更快: 1.TRUNCATE 速度: TRUNCATE 是一个DDLÿ…...

3DVR制作的工具或平台
3DVR(三维虚拟现实)是利用三维图像技术和虚拟现实技术,将真实场景进行三维扫描并转换成计算机可识别的三维模型,使用户能够在虚拟空间中自由漫游,体验身临其境的感觉。3DVR技术结合了全景拍摄和虚拟现实,提…...
qt QMessageBox 的详细解析
以下是关于 QMessageBox 的详细解析,涵盖其核心功能、使用方法、高级特性及常见场景示例。 1. QMessageBox 的核心作用 QMessageBox 是 Qt 中用于快速创建标准化对话框的类,常用于以下场景: 信息提示(如操作成功/失败)。警告或错误通知(如文件未保存)。用户决策(如确认…...

高吞吐与低延迟的博弈:Kafka与RabbitMQ数据管道实战指南
摘要 本文全面对比Apache Kafka与RabbitMQ在数据管道中的设计哲学、核心差异及协同方案。结合性能指标、应用场景和企业级实战案例,揭示Kafka在高吞吐流式处理中的优势与RabbitMQ在复杂路由和低延迟传输方面的独特特点;介绍了使用Java生态成熟第三方库(如Apache Kafka Clie…...
在 Spring Boot 中实现分库分表的全面指南
分库分表(Database Sharding)是一种数据库架构优化技术,通过将数据分散到多个数据库或表中,以应对高并发、大数据量场景,提升系统性能和扩展性。在 Spring Boot 中,分库分表可以通过框架支持(如…...
关于大语言模型的困惑度(PPL)指标优势与劣势
1. 指标本身的局限性 与人类感知脱节: PPL衡量的是模型对词序列的预测概率(基于交叉熵损失),但低困惑度未必对应高质量的生成结果。例如: 模型可能生成语法正确但内容空洞的文本(PPL低但质量差)…...

如何使用WordPress SEO检查器进行实时内容分析
在这篇文章中,我们将带你从头开始了解如何在WordPress中使用SEO检查工具进行实时内容分析。这篇文章面向初学者,帮助你理解SEO的重要性以及如何通过工具提高文章的搜索引擎优化(SEO)效果。 一、什么是SEO内容分析? 内…...

C语言:深入理解指针(5)
目录 一、回调函数 二、qsort 使用举例 三、模拟qsort 一、回调函数 回调函数就是一个通过函数指针调用的函数。 举个例子: int Add(int x, int y) {return xy; }void test(int (*pf)(int, int)) {int r pf(10 ,20);printf("%d\n" ,r); }int main()…...
前端如何获取电脑唯一编码
在前端开发中,出于安全和隐私的考虑,浏览器不允许直接获取硬件的唯一标识(如 MAC 地址、CPU 序列号等)。但可以通过以下方法生成设备指纹(Device Fingerprint),近似实现设备唯一标识:…...

IEEE出版|连续多年稳定检索|第三届信号处理与智能计算国际学术会议(SPIC2025)
【重要信息】 会议官网: www.ic-spic.com 会议日期:2025年11月28-30日 会议地点:中国 广州 截稿日期:2025年11月10日 接受或拒绝通知日期:提交后7个工作日 【征稿主题】 人工智能和机器学习 计算机系统和架构 …...

“强强联手,智启未来”凯创未来与绿算技术共筑高端智能家居及智能照明领域新生态
近日,北京凯创未来科技有限公司总经理赵健凯先生莅临广东省绿算技术有限公司北京运营中心,双方正式签订战略合作协议,标志着绿算技术在高端智能家居及智能照明领域的技术实力与产业布局获得智能家居行业认可,同时也为凯创未来在高…...

MQ消息队列的深入研究
目录 1、Apache Kafka 1.1、 kafka架构设 1.2、最大特点 1.3、功能介绍 1.4、Broker数据共享 1.5、数据一致性 2、RabbitMQ 2.1、架构图 2.2、最大特点 2.3、工作原理 2.4、功能介绍 3、RocketMQ 3.1、 架构设计 3.2、工作原理 3.3、最大特点 3.4、功能介绍 3…...

【NLP 74、最强提示词工程 Prompt Engineering 从理论到实战案例】
一定要拼尽全力,才能看起来毫不费劲 —— 25.5.15 一、提示词工程 1.提示词工程介绍 Ⅰ、什么是提示词 所谓的提示词其实就是一个提供给模型的文本片段,用于指导模型生成特定的输出或回答。提示词的目的是为模型提供一个任务的上下文,以便模…...
安卓开饭-ScrollView内嵌套了多个RecyclerView,只想与其中一个RecyclerView有联动
在 Android 开发中,将 RecyclerView 嵌套在 ScrollView 内通常会导致性能问题和滚动冲突,应尽量避免这种设计。以下是原因和替代方案: 为什么不推荐 RecyclerView ScrollView? 性能损耗 RecyclerView 本身已自带高效回收复…...
Linux 系统中的文件系统层次结构和重要目录的用途。
Linux系统目录结构采用分层布局方式,通过根目录"/"组织管理各类文件。以下为核心目录说明: 一、主要目录结构 1. / : 根目录,所有文件和目录的起点 2. /bin : 存储基础用户命令(ls/cp/mv等) 3. /boot : 存放系统引导程序和…...