手机远程手机,基于Scrcpy的安卓投屏工具EasyControl源码级改造全记录
Android 15投屏适配实战SurfaceControl迁移 Scrcpy v4.x同步 40Bug修复一个基于 Scrcpy 协议的安卓投屏工具原项目停更多年我在此基础上完成了 Android 15 适配、Scrcpy v4.x 协议同步、UI 全面现代化以及 40 项 Bug 修复并移除了所有捐赠/激活逻辑永久免费开放。项目地址https://github.com/yutils/Easycontrol为什么 Fork 一个停更项目EasyControl易控是一个安卓端控制安卓端的远程投屏工具基于 Scrcpy 协议实现。原项目由 mingzhixian 开发功能设计得很完整——支持音频传输、多设备连接、有线连接、剪切板同步、物理键盘共享在同类工具里算是思路清晰的一个。问题在于原项目已经多年没有更新了。我在实际使用中遇到了几个绕不过去的坎安卓 15 设备连不上、投屏偶发崩溃、端口占用导致重连失败、UI 风格停留在旧版 Material Design。这些问题每一个单独看都不致命但叠加在一起体验就已经不可用了。与其找一个替代品重新适应不如直接把源码拉下来改。原项目开源协议允许修改和分发代码结构也还算清晰改造成本可控。于是就有了这个 Fork。改造的核心内容这次改造不是小修小补而是从底层协议到上层 UI 的全面现代化。概括来说做了六件事移除捐赠/激活逻辑永久免费适配 Android 15 系统变更Scrcpy 协议同步至 v4.xUI 全面现代化重做修复 40 项 Bug调整若干功能细节下面逐项展开。Android 15 适配这次改造里最难的部分Android 15 对几个底层 API 做了破坏性变更而 Scrcpy 的实现深度依赖这些 API。原项目用的是旧接口在新系统上直接跑不通。这部分是整个改造中技术含量最高、也最耗时的环节。SurfaceControl API 迁移SurfaceControl是安卓图形系统的核心类Scrcpy 通过它创建虚拟显示器来渲染远端画面。Android 15 把createDisplay和destroyDisplay这两个方法从SurfaceControl移到了一个新的DisplayControl类下变成了DisplayControl.createVirtualDisplay和destroyVirtualDisplay。这不仅仅是换个调用路径的问题。DisplayControl是个隐藏 API访问方式、参数校验都和原来不同直接迁移会触发系统拦截。我的处理方式是做版本判断Android 15 及以上走DisplayControl新路径Android 14 及以下保留原来的静态方法调用。这样两条路径并存兼容性最好代价是代码里多了一层分支。SurfaceControl 静态方法移除除了创建/销毁显示器SurfaceControl还有几个静态方法在 Android 15 里被移除了setDisplaySurface、setDisplayProjection、setDisplayLayerStack。这三个方法的作用是把渲染内容绑定到指定的 Display 上。新系统要求改用SurfaceControl.Transaction对象来批量操作。Transaction 机制本身不新但原来代码是松散的静态调用现在要改成事务模式调用顺序和提交时机都得重新梳理。同样我只在 Android 15 启用 Transaction 路径低版本保持原样。AudioRecord 签名变更AudioRecord的native_setup是个 native 方法安卓通过 JNI 调用它来初始化音频录制。Android 14 QPR3 和 15 改了这个方法的签名——参数列表变了。原项目通过反射调用native_setup签名一变反射直接失败音频传输就断了。这部分需要对照新系统的源码确认新签名然后做版本适配。WindowManager 的 caller 参数WindowManager相关调用在 Android 15 里多了一个caller参数用于标识调用来源。原项目的调用方式在新系统上会触发安全校验或直接被拒。这个改动相对简单补上参数即可但定位问题花了不少时间——因为报错信息并不直接指向这个原因。禁用 Server 模块 R8 混淆这是最后一个坑。Server 模块运行在被控端的那部分代码开启了 R8 混淆后在 Android 15 的 ART 运行时上会抛ClassNotFoundException。原因是 R8 混淆会重命名类和方法但 Server 模块通过反射和 JNI 调用的部分类不在 R8 的 keep 规则覆盖范围内。Android 15 的 ART 对类加载做了更严格的校验混淆后的类名找不到对应的原始定义就直接崩了。解决方案是禁用 Server 模块的 R8 混淆。Server 模块本身不大禁用混淆对 APK 体积的影响可以忽略但彻底解决了运行时崩溃问题。Scrcpy 协议同步至 v4.x原项目基于的 Scrcpy 协议版本较旧很多近年新增的低延迟优化和编码参数都没用上。我把协议同步到了 v4.x主要带来两方面的提升。一是编码参数。v4.x 新增了KEY_PRIORITY和KEY_LATENCY两个低延迟编码参数可以让被控端在编码时优先保证实时性而非画质。对于投屏这种交互场景延迟比清晰度更重要。二是防息屏机制。原来的实现通过修改系统的screen_off_timeout来防止被控端息屏这是一种侵入式做法——改完之后系统的息屏设置就被污染了退出投屏也不一定恢复。v4.x 改用PowerManager.userActivity来模拟用户活动不修改任何系统设置退出投屏后系统行为完全正常。UI 全面现代化原项目的 UI 是旧版 Material Design 风格控件粗糙、配色不统一、深色模式缺失。这次我把所有核心页面都重做了一遍。整体设计语言采用绿色主调色彩系统完整支持深色模式。引入了一套精细化的尺寸系统——字号、间距、圆角、阴影层次都有统一的规范不再是各页面各自为政。布局上全面采用卡片式设计圆角卡片配细描边视觉上比原来的纯列表清爽很多。所有可点击元素都加了 Ripple 涟漪反馈触摸交互的感知更明确。重做的页面包括首页、设备详情、设置、投屏界面、悬浮窗、加载弹窗、对话框。其中投屏界面统一改为半透明深色浮层加白色图标关闭键红色高亮操作焦点更清晰。开关组件也做了主题色适配——开启时绿色、关闭时灰色不再像原来那样关闭状态和背景融为一体分不清。引入 appcompat 主题是这次 UI 改造的基础代价是 APK 体积变大但对于现代安卓开发来说这是必要的 trade-off。Bug 修复40 项原项目积累了不少 Bug我逐个排查修复了 40 多项按类型分为三类。崩溃与资源泄漏类是最严重的。Client.close()在连接失败时会抛 NPE导致资源无法释放ClientStream没有关闭 ADB Socket 和 Shell 进程端口一直被占用重连必然失败MyBroadcastReceiver.updateUSB里有个低级错误——return被误写成continue导致后续 USB 设备被直接跳过。这些都是影响核心功能的稳定性问题。逻辑错误类影响功能正确性。Device.java里 appStackId 的条件判断写反了ClientController的 switch 语句 default 穿透DbHelper删除设备后默认名称重复DeviceListAdapter随机颜色导致列表闪烁。这些 Bug 不一定崩溃但会让用户觉得这个 app 怪怪的。连接稳定性类是体验层面的。启动 Server 前不清理残留进程端口冲突导致连接失败连接断开时没有正确释放所有资源影响下一次连接。修复后连接的可靠性明显提升。功能调整除了修 Bug还做了一些功能层面的优化。启动时主动申请所需权限——悬浮窗、文件读取、前台服务、通知不再让用户自己去设置里翻。设备名称默认按序号命名设备1、设备2、设备3比原来的随机命名更直观。首页设备列表同时显示设备名称和 IP 地址方便多设备场景下区分。加载弹窗重构为正方形布局显示加载中比原来的满屏 loading 更克制。功能特色一览改造后的 EasyControl 具备以下能力使用简单开箱即用支持音频传输多设备同时连接支持有线连接多设备剪切板同步多设备共享主控端物理键盘启动迅速低延迟支持分辨率自适应良好的旋转支持支持小窗显示与全屏显示Android 14 / 15 完整适配如何使用如果你只是想用直接去 Release 页面下载 APK 安装即可https://github.com/yutils/Easycontrol/releases当前版本 1.5.8。如果你想自行编译项目使用 Android Studio Gradle 构建已升级到 Java 17# Windows.\easycontrol\gradlew.bat assembleDebug-peasycontrol# Linux / macOS./easycontrol/gradlew assembleDebug-peasycontrol编译产物在easycontrol/app/build/outputs/apk/debug/app-debug.apk。写在最后Fork 一个停更项目然后把它救活这个过程本身挺有意思的。最难的不是改代码而是定位问题——Android 15 的那几个 API 变更官方文档写得含糊报错信息也语焉不详最后是对照 AOSP 源码一点点对照出来的。如果你也在找一个能用的安卓投屏工具或者对 Scrcpy 协议的底层实现感兴趣欢迎来仓库看看GitHubhttps://github.com/yutils/Easycontrolapk下载地址https://github.com/yutils/Easycontrol/releases有问题或建议直接在 GitHub 提 Issue 就行。项目会持续维护。作者细雨若静雨季Android/Java 开发者11 年移动端开发经验。个人站点 kotlinx.comGitHub yutils。