# 截图直链(Android) 点一下按钮 = **自动退到桌面/背后的界面 → 截那一屏 → 上传到你的接口 → 直链复制进剪贴板**。 - 安装包:`截图直链.apk`(同目录 `shotup.apk` 是编译产物,同一个文件) - 包名:`com.shotup.app` 版本 1.0 最低 Android 6.0(API 23),targetSdk 34 - 源码:`src\com\shotup\app\`(MainActivity.java、ShotService.java)、`AndroidManifest.xml`、`res\` - 重新编译:运行 `编译.ps1`(脚本会把源码同步到 `C:\shotup_build` 再打包,因为 aapt2 不认中文路径) ## 一、截图逻辑(重要) 点「截图并上传」后,App **立刻把自己收起来**,回到你点它之前那个界面(桌面、游戏、网页都行),**再截那一屏**——所以截到的是里面的画面,不是这个 App 自己的界面。 实测:在桌面点按钮 → App 收起 → 截到的就是桌面图标那一屏(720×1280、506 KB 无损 PNG,App 界面完全不在图里)。 截完上传成功后: - 直链自动进剪贴板(可以直接粘贴) - 弹一条通知「截图已上传,直链已复制」,**点通知回到本应用**就能看到预览、直链和“最近上传”记录 - 相册「Pictures/截图直链/」也留一份原图 ## 二、界面:没有任何输入框 按你的要求,界面上**一个输入框都没有了**:只有「截图并上传」按钮、状态、预览、直链、复制直链、相册多选、最近上传。上传地址是**内置常量**,装好就按那条地址走。 ## 二点五、截图按 zip 打包上传 每次截图都先存成**无损 PNG 原图**,再**原封不动打进一个 zip**(zip 里就一张 PNG,打包不二次压缩,PNG 字节一模一样),上传的就是这个 `.zip`(例:`shot_20261113_221719.zip`)。 - 为什么要 zip:网盘/接口多数只收压缩包,直接传图片会被拦 - 已实测:服务端收到 `shot.zip`,解出来是 `shot_20261113_221719.png`,720×1280,505038 字节,PNG 无损 - 打包开关在源码常量 `PACK_AS_ZIP`(true=传 zip,false=传原图 PNG) ## 二点六、直链地址怎么工作(内置,不用填) - 地址是**可覆盖写的直链**(例:`https://你的域名/img/shot.zip`)→ 用 `PUT` 直接写上去,**同一条链接永远是最新那张截图** - 地址是**上传接口** → PUT 被拒(404/405)时自动改走 `POST multipart/form-data`,再从返回里取直链 - 请求头、字段名、文件名模板都是内部常量(`DEFAULT_URL` 等),改一行重新编译就换目标 ⚠️ 网盘“分享页”不行:`share.feijipan.com/s/xxx`、`qq1212.lanzout.com/b0zl9v3wh` 这类都是**下载分享页**, - 飞机盘分享页前端接口只有 `/ws/share/*`(列表、下载),**没有上传接口** - 蓝奏分享页 HTML 里也没有上传按钮,只有下载 要上传到网盘必须用**账号**(登录后才走 `/html5up.php` 那套上传),分享密码不是登录密码—— 用 `qq1212 / cqou` 试登录,服务器直接回「密码错误!」。 可选的三条路: 1. 给蓝奏**登录账号 + 登录密码**(不是分享密码),我在 App 里做一次自动登录,之后每次截图上传 zip 到你的文件夹 2. 给对象存储/图床的**可写直链**(OSS/COS/七牛/R2/S3 的 PUT 地址 + 签名) 3. 给**自己的上传接口**(URL + 鉴权 + 返回直链字段) ## 三、第一次使用(3 步) 1. 打开 App,直接点「截图并上传」。 2. 系统弹「要开始使用截图直链录制或投放内容吗?」→ 点**立即开始**(同一会话只弹一次)。 3. 之后每次点按钮都是「收起 → 截图 → 打包 zip → 上传 → 复制直链」,通知里能看到结果。 杀进程后第一次点按钮会再弹一次系统授权框,这是 Android 的规定。 ## 三、你的上传接口要满足什么 App 用标准 `POST multipart/form-data` 上传,参数都能在界面上改: | 设置项 | 默认值 | 说明 | |---|---|---| | 上传接口地址 | 空 | 例:`https://你的域名/api/upload`;http 也支持(已开 cleartext) | | 请求头 | 空 | 一行一个 `key: value`,例:`Authorization: Bearer 你的token` | | 文件字段名 | `file` | 服务端接收文件的字段名 | | 上传文件名 | `shot_{time}.png` | `{time}` 换成 `年月日_时分秒_毫秒`,**每张独立、不覆盖** | | 返回里直链的字段名 | `url` | 返回 JSON 时取哪个字段;嵌套写 `data.url`;纯文本接口留空 | | 附加表单字段 | 空 | 一行一个 `key=value`,例:`token=abc`、`bucket=shot` | 服务端返回什么都能识别: - JSON:`{"code":0,"data":{"url":"https://cdn.xx.com/shot.png"}}` → 字段名填 `data.url` - JSON:`{"url":"https://.../a.png"}` → 字段名填 `url` - 纯文本:直接返回 `https://.../a.png` → 字段名留空 - 兜底:JSON 里找不到配置字段时,自动抓返回内容里的第一个 http(s) 地址 如果服务端要求 **PUT / 二进制 body**,或返回的是 HTML 页面里的链接,这套 multipart 方案不适用,把实际格式发我再改。 ## 四、画质与多张 - **原图、不压画质**:以**无损 PNG**(`Bitmap.compress(PNG, 100)`)上传,分辨率就是屏幕真实分辨率,不缩放、不转 JPEG。实测 720×1280 的机器上传回来就是 720×1280,逐像素无损。 - **不覆盖**:文件名默认带毫秒时间戳,连续截多少张都是各自独立的直链。 - **批量**:点「从相册选图上传(可多选)」→ 长按第一张进入多选 → 再点其它图片 → 右上角「选择」→ 逐张上传,最后所有直链换行拼一起复制进剪贴板。(这条只上传相册里已有的图,不会退桌面。) - 想让某个固定地址永远显示最新一张(覆盖式热更新),把「上传文件名」改成 `latest.png` 之类固定名字即可。 ## 五、实测记录(Android 14 x86_64 模拟器 720×1280,全部通过) | 用例 | 结果 | |---|---| | 点按钮后是否退出本应用 | 是,直接退回桌面/背后界面 | | 截到的是不是那一屏 | 是,上传图里是桌面图标,App 界面不在图中 | | 文件规格 | 720×1280,无损 PNG,约 500 KB | | 覆盖写入同一条直链 | `PUT /cover.png` → HTTP 200,原地址内容被替换成新截图,GET 回来 `image/png`、字节数一致 | | 直链访问 | `HTTP 200`,`Content-Type: image/png` | | 连点两次 | 覆盖模式下永远一条链接、始终最新;普通上传模式下新文件独立命名,旧文件保留 | | 相册多选 2 张 | 两张各自上传成功,界面列出直链 | | 剪贴板 | 上传成功后提示「直链已复制」;多张时为多行链接 | ## 六、可能踩到的坑(已处理) 1. **Android 14 必须前台服务**:调 `MediaProjection` 前必须有 `foregroundServiceType=mediaProjection` 的前台服务,否则抛 `SecurityException`。已加 `ShotService`(通知栏“截屏通道已就绪”常驻,退出 App 自动停)。 2. **必须先注册回调**:`registerCallback` 要在 `createVirtualDisplay` 之前,否则抛 `Must register a callback before starting capture`。已注册。 3. **每次冷启动要重新授权**:授权 token 只在一次会话有效,杀进程后第一次点会再弹系统框。 4. **升级安装签名**:`编译.ps1` 复用目录里的 `shotup.keystore`,覆盖安装不会签名冲突;别删这个 keystore。 5. **截图时 App 在后台**:`VirtualDisplay` 由前台服务撑着,退到后台照样能抓到当前屏幕;抓帧前会先丢掉旧帧再等新帧,避免传上一张旧画面。 ## 七、失败时怎么排查(状态栏直接写原因) - `先填上传接口地址` —— 地址没填或没保存 - `上传失败:HTTP 401/403` —— 鉴权头不对,检查「请求头」 - `上传失败:HTTP 400 ...` —— 字段名不匹配,检查「文件字段名 / 附加表单字段」 - `没解析出直链,返回:...` —— 返回格式和「返回里直链的字段名」对不上,按引号里的原始返回改字段名 - `上传异常:...UnknownHostException / timeout` —— 网络或地址写错(手机访问电脑上的测试服务要用局域网 IP 或 `adb reverse`,不能用 `127.0.0.1`) - `截图失败:没取到画面` —— 再点一次;若系统停掉了截屏授权,App 会提示重新授权