Merge branch 'dev'
This commit is contained in:
+40
-6
@@ -539,18 +539,46 @@ class ApkManager:
|
||||
_log.warning(f"[{serial}] exec-in 推送到一半失败(rc={rc}),退回 adb push")
|
||||
return self._push_fallback(serial, apk_path, remote)
|
||||
|
||||
# 校验设备上文件大小一致(exec-in 中途断流可能"成功"但少字节)
|
||||
# 校验设备上文件大小。**必须轮询等它长齐**——见 _wait_remote_size 的说明:
|
||||
# exec-in 返回后设备侧可能仍在落盘,只量一次会把成功的传输误判成截断。
|
||||
got = self._wait_remote_size(serial, remote, total)
|
||||
if got is not None and got != total:
|
||||
_log.warning(f"[{serial}] exec-in 推送不完整(设备 {got} / 本地 {total} 字节),"
|
||||
f"改用 adb push 重传")
|
||||
return self._push_fallback(serial, apk_path, remote)
|
||||
|
||||
return True, "推送完成 %.1fMB / %.0fs" % (total / 1048576.0, time.time() - t0)
|
||||
|
||||
@staticmethod
|
||||
def _remote_size(serial, remote):
|
||||
"""设备上文件字节数;取不到返回 None(stat 参数各机型有差异,不据此判失败)。"""
|
||||
try:
|
||||
rs = subprocess.run(
|
||||
[ADB_PATH, "-s", serial, "shell", "stat", "-c", "%s", remote],
|
||||
capture_output=True, timeout=30)
|
||||
got = int((rs.stdout or b"").decode("utf-8", "replace").strip().splitlines()[-1])
|
||||
if got != total:
|
||||
return False, f"推送不完整(设备 {got} / 本地 {total} 字节)"
|
||||
lines = (rs.stdout or b"").decode("utf-8", "replace").strip().splitlines()
|
||||
return int(lines[-1]) if lines else None
|
||||
except Exception:
|
||||
pass # stat 参数各机型有差异,取不到就跳过校验
|
||||
return None
|
||||
|
||||
return True, "推送完成 %.1fMB / %.0fs" % (total / 1048576.0, time.time() - t0)
|
||||
def _wait_remote_size(self, serial, remote, want, timeout=20.0):
|
||||
"""轮询设备上的文件大小,直到等于 want / 超时 / 取不到(None)。
|
||||
|
||||
为什么不能只量一次:**adb 进程退出 ≠ 设备上文件写完**。exec-in 把数据交给
|
||||
设备侧(adbd → shell → `cat >`)后,缓冲区里的数据还会继续落盘。实测同一份
|
||||
5.9MB 的 APK:adb 刚返回时 stat 读到 3710967,紧接着 4264448,几秒后才是完整的
|
||||
5933150。旧实现量一次就判"推送不完整",把**成功的传输误报成失败**
|
||||
(2026-09-14 用户报的就是这个;同一份文件在多台设备上"失败",断点还各不相同)。
|
||||
"""
|
||||
deadline = time.time() + timeout
|
||||
last = None
|
||||
while True:
|
||||
last = self._remote_size(serial, remote)
|
||||
if last == want or last is None:
|
||||
return last
|
||||
if time.time() >= deadline:
|
||||
return last
|
||||
time.sleep(0.3)
|
||||
|
||||
def _push_fallback(self, serial, apk_path, remote):
|
||||
"""老 adb 或 exec-in 失败时的退路:原来的 `adb push`(无进度)。"""
|
||||
@@ -565,6 +593,12 @@ class ApkManager:
|
||||
if r.returncode != 0 or "1 file pushed" not in out:
|
||||
return False, (out.strip().replace("\n", " ")[:150]
|
||||
or f"推送失败(returncode={r.returncode})")
|
||||
# adb push 是同步的(不会出现 exec-in 那种"退出后才落盘"),但仍量一次大小:
|
||||
# 设备空间不足/文件被并发覆盖时,"1 file pushed" 也可能是残缺的
|
||||
total = os.path.getsize(apk_path)
|
||||
got = self._wait_remote_size(serial, remote, total, timeout=10)
|
||||
if got is not None and got != total:
|
||||
return False, f"推送不完整(设备 {got} / 本地 {total} 字节)"
|
||||
return True, "推送完成(adb push)"
|
||||
|
||||
def _set_item(self, serial, status, msg=""):
|
||||
|
||||
@@ -577,6 +577,12 @@
|
||||
> 1. **推送到设备**:`推送中 62%(211.0/336.1 MB · 2.6 MB/s · 剩约 48s)` —— 用分块写
|
||||
> (`adb exec-in`)拿字节级进度;不用 `adb push` 是因为它只在结束时吐一行汇总,
|
||||
> 大包(抖音 336MB≈2 分钟)整个传输过程界面上只能干等
|
||||
> ⚠️ **`adb exec-in` 返回 ≠ 设备上写完**:数据还在设备侧(adbd → shell → `cat >`)缓冲里
|
||||
> 继续落盘。所以收尾必须**轮询文件大小直到长齐**(`_wait_remote_size`,最长 20s),
|
||||
> 量一次就判"推送不完整"会把成功的传输误报成失败(2026-09-14 的真实案例:同一份
|
||||
> 5.9MB 的 APK 在多台设备上报了 8 次假失败,实际文件最终都是完整的)。真不齐才退回
|
||||
> `adb push` 重传。
|
||||
|
||||
> 2. **设备上安装**:`正在设备上安装...(大包要 1-2 分钟,无进度可读)` —— `pm install`
|
||||
> 在设备端解包,系统没给进度接口
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user