12.虚拟显示与多任务
虚拟显示是 scrcpy 中一个相当实用的功能,它允许我们在设备上创建一个全新的、独立的显示空间,而不是简单地镜像主屏幕。这个特性为多任务处理和应用隔离提供了全新的可能性。想象一下,可以在电脑上同时运行多个 Android 应用,每个应用都在自己的虚拟显示中,互不干扰,这种工作方式对于开发者、测试人员或者需要多开应用的用户来说,价值不言而喻。
虚拟显示创建与管理
从主屏幕到虚拟显示
默认情况下,scrcpy 做的是镜像工作——把手机的物理屏幕原封不动地搬到电脑上。但有时候,我们并不想要主屏幕,而是需要一个干净的、全新的画布。这时候,--new-display 参数就派上用场了。
最基本的用法是指定分辨率:
scrcpy --new-display=1920x1080
这行命令会在设备上创建一个 1920x1080 分辨率的虚拟显示,并将其内容传输到电脑。注意,这个显示是独立于手机主屏幕的,手机上不会显示任何变化,所有内容只出现在 scrcpy 窗口里。
创建虚拟显示时,系统会自动处理大部分底层细节。Android 系统从 10 版本开始支持虚拟显示,scrcpy 通过 Android 的 DisplayManager API 来创建和管理这些显示。这个过程对开发者透明,但理解其原理有助于排查问题:虚拟显示本质上是一个没有物理硬件对应的逻辑显示,它渲染的内容会被捕获并编码成视频流。
分辨率与DPI的灵活配置
分辨率只是基础,DPI(每英寸点数)同样重要。不同的 DPI 设置会影响应用的 UI 缩放,同一个应用在不同 DPI 下看起来可能完全不同。scrcpy 允许我们同时指定分辨率和 DPI:
scrcpy --new-display=1920x1080/420
这里的 /420 就是强制设置 DPI 为 420。为什么需要这个?因为很多应用会根据 DPI 来决定布局。比如,在 1920x1080 分辨率下,420 DPI 会让 UI 元素保持合理大小,避免过大或过小。
如果省略分辨率,只写 DPI,scrcpy 会自动使用主屏幕的分辨率:
scrcpy --new-display=/240
这行命令创建了一个与主屏幕分辨率相同但 DPI 为 240 的虚拟显示。这种写法在需要保持布局一致但调整 UI 大小时特别有用。
最简洁的形式是什么都不指定:
scrcpy --new-display
这会创建一个与主屏幕完全相同分辨率和 DPI 的虚拟显示。虽然看起来和镜像差不多,但本质区别在于:这是一个全新的显示实例,主屏幕上正在运行的应用不会自动出现在这里。
默认参数的巧妙用法
在实际使用中,我发现 --new-display 的默认行为很有讲究。当不指定任何参数时,scrcpy 会读取当前主屏幕的 DisplayMetrics,包括宽度、高度和密度,然后原样复制。这意味着虚拟显示和物理显示在系统层面被视为同等配置,应用不需要重新适配。
但这里有个细节:虚拟显示的刷新率默认会设置为 60Hz,即使主屏幕是 120Hz。这是因为视频编码和传输的瓶颈通常不在刷新率,而是带宽和处理能力。强制 60Hz 可以在大多数情况下保证流畅性,避免不必要的资源浪费。
管理虚拟显示的生命周期也很简单。当 scrcpy 进程结束时,虚拟显示会自动销毁,所有在其中运行的应用也会被清理。这种设计避免了资源泄漏,但有时候我们可能希望保留应用,后面会讲到如何处理。
应用独立显示配置
空显示器的挑战
创建虚拟显示后,可能会遇到一个尴尬的情况:屏幕是黑的,什么都没有。这是因为虚拟显示默认没有内容,它不像主屏幕那样有桌面启动器。很多 Android 设备在虚拟显示上不会自动显示系统 UI,这就形成了一个"空显示器"。
如何验证虚拟显示确实创建了?可以加上 -V 参数查看详细日志:
scrcpy --new-display=1920x1080 -V debug
在日志中会看到类似 DisplayManager: Creating virtual display: 1920x1080, dpi=420 的信息,确认创建成功。但如果没有任何应用启动,视频流会是空的,scrcpy 窗口可能显示为纯黑或最后一帧画面。
指定应用启动
解决空显示器问题的标准方案是手动启动应用。scrcpy 提供了 --start-app 参数,可以在创建虚拟显示的同时启动指定应用:
scrcpy --new-display=1920x1080 --start-app=org.videolan.vlc
这行命令会创建虚拟显示,然后立即启动 VLC 播放器。VLC 会在新的虚拟显示上打开,而不是主屏幕。这种方式非常适合单一应用的场景,比如只想在电脑上跑一个视频播放器,或者测试某个应用的特定功能。
--start-app 接受的是应用的包名。如何找到包名?可以在手机上打开应用,然后执行 adb shell dumpsys window | grep mCurrentFocus,输出中会包含当前前台应用的包名。对于常用应用,网上也有包名对照表。
启动应用后,虚拟显示就有了内容,视频流开始正常传输。这个应用会独占整个虚拟显示,就像在手机全屏运行一样。如果想切换应用,目前 scrcpy 没有提供直接的切换机制,需要依赖应用自身的功能或者通过 adb 发送 intent。
自定义启动器方案
有些设备在虚拟显示上支持系统启动器,但很多时候并不支持。这时候可以安装一个第三方的轻量级启动器,比如 Fossify Launcher。这是一个开源项目,专门为简洁和高效设计。
使用自定义启动器的命令组合如下:
scrcpy --new-display=1920x1080 --no-vd-system-decorations --start-app=org.fossify.home
这里出现了两个新参数。--no-vd-system-decorations 会禁用系统装饰,避免可能出现的 UI 问题。--start-app 启动 Fossify Launcher 作为桌面环境。这样,虚拟显示就有了一个完整的启动器,可以像普通手机一样点击图标启动应用。
这种方式构建了一个"虚拟手机"环境,所有操作都在虚拟显示中完成,完全隔离于主屏幕。对于需要同时运行多个独立应用的场景,这是理想方案。比如,可以在主屏幕正常使用手机,同时在电脑上通过虚拟显示运行另一个应用,两者互不干扰。
系统装饰与IME策略
系统装饰的作用与禁用
系统装饰指的是虚拟显示上的系统 UI 元素,比如状态栏、导航栏等。默认情况下,scrcpy 会启用这些装饰(--vd-system-decorations),让虚拟显示看起来更像真实设备。
但在某些场景下,系统装饰可能带来问题。比如,有些设备的虚拟显示实现不完善,状态栏可能显示异常,或者导航栏无法点击。这时候就需要禁用系统装饰:
scrcpy --new-display --no-vd-system-decorations
禁用后,虚拟显示会变成一个纯粹的"裸"显示,没有任何系统 UI。这在使用自定义启动器或者全屏应用时特别有用,可以避免视觉干扰。
需要注意的是,如果既没有系统装饰,也没有启动任何应用,虚拟显示将完全空白,不会产生任何视频帧。scrcpy 可能会显示黑屏或者提示无内容。这种情况下,必须确保有应用正在运行,否则虚拟显示就失去意义。
输入法显示策略
输入法(IME)在虚拟显示中的行为需要特别处理。默认情况下,虚拟显示上调出的输入法会出现在设备的默认显示上(通常是主屏幕)。这听起来很奇怪,但确实是 Android 系统的设计:输入法服务通常绑定到默认显示。
scrcpy 提供了 --display-ime-policy 参数来控制这个行为。有两个选项:local 和 remote。local 表示输入法显示在本地(即虚拟显示所在的 scrcpy 窗口),remote 表示显示在远程(设备主屏幕)。
让输入法出现在 scrcpy 窗口中的命令是:
scrcpy --new-display --display-ime-policy=local
或者,如果镜像的是已有显示(比如显示 ID 为 1 的屏幕):
scrcpy --display-id=1 --display-ime-policy=local
这个参数对于需要频繁输入的场景至关重要。想象一下,在虚拟显示中打开一个笔记应用,点击输入框,输入法却出现在手机屏幕上,这种体验非常割裂。设置为 local 后,输入法会以悬浮窗形式出现在 scrcpy 窗口中,和正常使用无异。
实现这个功能的原理是 scrcpy 在服务端拦截了输入法窗口的显示请求,并将其重定向到虚拟显示上。这需要 Android 10 以上的版本支持,因为系统才开放了足够的 API 来控制输入法显示策略。
无内容时的处理
当虚拟显示没有内容时,scrcpy 的行为值得注意。如果视频编码器收不到任何帧,它可能会停止发送数据,导致客户端冻结在最后一帧或显示黑屏。这不是 bug,而是性能优化:不渲染静态内容可以节省带宽和 CPU。
但在实际使用中,我们可能希望即使没有应用运行,也保持显示活跃。目前 scrcpy 没有直接参数控制这个行为,但可以通过在虚拟显示上运行一个保活应用来解决。比如,一个简单的时钟应用或者静态图片查看器,确保始终有内容在渲染。
另一个技巧是使用 --stay-awake 参数(虽然这是第 9 章的内容,但这里可以简述),防止设备休眠导致虚拟显示关闭。组合使用如下:
scrcpy --new-display --stay-awake --start-app=com.example.keepalive
这样就能构建一个稳定的虚拟显示环境,适合长期运行的场景。
关闭时自动销毁
默认的销毁行为
虚拟显示的生命周期与 scrcpy 进程绑定。当关闭 scrcpy 窗口或终止进程时,虚拟显示会被自动销毁,这是默认行为。销毁意味着释放系统资源,包括显示缓冲区和相关服务。
这个设计很合理,避免了用户忘记关闭虚拟显示导致资源泄漏。在 Android 系统中,每个虚拟显示都会占用一定的内存和 GPU 资源,长期不用的虚拟显示会成为负担。
默认销毁的命令很简单,不需要额外参数:
scrcpy --new-display=1920x1080
关闭窗口后,虚拟显示和其中的应用都会消失,就像关闭了一个真实的显示器。应用会被系统强制停止,不会留在后台。
保留应用到主屏幕
但有时候,我们希望关闭虚拟显示时,应用不要被杀掉,而是转移到主屏幕继续运行。scrcpy 提供了 --no-vd-destroy-content 参数来实现这个需求:
scrcpy --new-display --no-vd-destroy-content
这个参数的作用是改变销毁策略。当虚拟显示关闭时,系统不会杀死应用,而是尝试将它们移动到默认显示(主屏幕)。不过,这个行为依赖于应用本身是否支持显示切换。很多应用在设计时没有考虑这种场景,可能会崩溃或者行为异常。
实际测试发现,视频播放器、浏览器这类应用通常能较好地处理显示切换,它们会在转移后保持播放状态。但游戏或者全屏应用可能会出问题,因为它们通常假设显示配置是静态的。
使用这个参数时需要注意,应用转移到主屏幕后,可能会覆盖当前主屏幕的内容,造成干扰。所以最好在主屏幕处于桌面状态时关闭虚拟显示,避免打断正在进行的操作。
实际应用场景
这个特性在长时间任务场景中很有用。比如,在虚拟显示中下载大文件或者渲染视频,中途需要关闭 scrcpy 窗口,但希望任务继续。设置 --no-vd-destroy-content 后,关闭窗口,下载任务会转移到后台继续执行。
另一个场景是应用调试。开发者在虚拟显示中测试应用,发现问题需要关闭 scrcpy,但希望保留应用状态以便后续分析。这个参数可以让应用存活下来,通过 adb 继续获取日志或者状态信息。
不过,我建议谨慎使用这个功能。因为应用转移后,用户可能忘记它们还在运行,导致资源占用和电量消耗。最好配合 --stay-awake 使用时要格外注意,避免设备长时间不休眠。
多任务协作场景
同时运行多个应用
虚拟显示的真正威力在于多任务。虽然一个虚拟显示同一时间只能显示一个应用(没有启动器的情况下),但我们可以创建多个虚拟显示,每个显示运行不同的应用。
scrcpy 本身不支持同时创建多个虚拟显示,但可以通过多次运行 scrcpy 来实现。每个实例创建自己的虚拟显示:
# 终端1:运行浏览器
scrcpy --new-display=1920x1080 --start-app=com.android.chrome -s device1
# 终端2:运行音乐播放器
scrcpy --new-display=1280x720 --start-app=com.spotify.music -s device1
这里 -s device1 确保两个实例连接到同一设备。每个实例独立工作,互不影响。电脑上会出现两个 scrcpy 窗口,一个显示浏览器,一个显示音乐播放器。
这种方式的局限是资源消耗。每个虚拟显示都需要独立的视频编码和传输,对设备 CPU 和网络带宽要求较高。在实际使用中,建议最多同时运行 2-3 个虚拟显示,再多可能会导致卡顿。
虚拟显示与主显示的协作
更高级的场景是虚拟显示与主显示的协作。比如,在主屏幕上运行一个应用,在虚拟显示中运行另一个应用,两者通过剪贴板或者文件共享数据。
scrcpy 的剪贴板同步功能(第 2 章内容)在虚拟显示中同样有效。可以在虚拟显示中复制文本,粘贴到主屏幕的应用中,反之亦然。这种跨显示的协作扩展了多任务的可能性。
一个实际的工作流示例:在主屏幕上运行微信,虚拟显示中运行文档编辑器。收到消息时,复制内容,切换到虚拟显示窗口粘贴回复。整个过程不需要在手机上操作,全部在电脑上完成。
文件拖拽功能(同样第 2 章)也支持虚拟显示。可以把电脑上的图片拖到虚拟显示窗口,直接发送给虚拟显示中的应用。这在使用社交应用或者文件管理器时非常方便。
生产力工作流构建
结合这些特性,可以构建完整的生产力工作流。比如,开发者可以在虚拟显示中运行待测试的应用,主屏幕保持代码编辑器或者文档;内容创作者可以在虚拟显示中运行视频剪辑应用,主屏幕查看素材。
对于需要监控多个应用的场景,可以编写脚本自动创建虚拟显示并启动应用。比如,一个监控脚本:
#!/bin/bash
# 创建三个虚拟显示,分别运行监控应用
scrcpy --new-display=800x600 --start-app=com.app.monitor1 --stay-awake &
sleep 2
scrcpy --new-display=800x600 --start-app=com.app.monitor2 --stay-awake &
sleep 2
scrcpy --new-display=800x600 --start-app=com.app.monitor3 --stay-awake &
这个脚本创建了三个小尺寸虚拟显示,每个运行一个监控应用。& 让进程后台运行,sleep 2 避免同时创建造成资源争抢。--stay-awake 确保设备不会休眠。
在自动化测试场景中,虚拟显示提供了隔离环境。测试脚本可以在虚拟显示中运行被测应用,不影响主屏幕,测试结束后自动销毁,环境干净。这比在主屏幕上测试可靠得多,避免了通知、来电等干扰。
虚拟显示还可以用于演示。在会议中,可以在虚拟显示中运行演示应用,主屏幕保持私人信息不被投影。关闭 scrcpy 窗口即结束演示,无需担心隐私泄露。
虚拟显示功能将 scrcpy 从简单的屏幕镜像工具升级为多任务平台。通过创建独立的显示空间,应用可以在隔离环境中运行,互不干扰。结合应用启动、输入法策略和生命周期管理,虚拟显示为开发、测试和日常使用提供了强大支持。
下一章将探讨另一个特殊场景:摄像头捕捉。scrcpy 不仅能处理屏幕内容,还能直接捕获设备摄像头的视频流,这在视频会议、直播等场景中有着独特价值。我们将深入了解摄像头列表获取、分辨率配置以及如何将 Android 设备变成电脑的高清摄像头。