fix(client): wait for PDD foreground after intent
This commit is contained in:
@@ -7,9 +7,11 @@ from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
from hashlib import sha256
|
||||
import json
|
||||
from math import isfinite
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from time import monotonic, sleep
|
||||
from typing import Any, Protocol
|
||||
from uuid import uuid4
|
||||
|
||||
@@ -40,7 +42,7 @@ class ProductVersionMismatchError(ProductOpenError):
|
||||
|
||||
|
||||
class ProductPackageMismatchError(ProductOpenError):
|
||||
"""Intent 后当前前台包不是拼多多。"""
|
||||
"""Intent 后在有限时间内未观察到拼多多前台包。"""
|
||||
|
||||
|
||||
class ProductOpenTimeoutError(ProductOpenError):
|
||||
@@ -90,12 +92,20 @@ class ProductOpenCapturer:
|
||||
adb_client: AdbClient,
|
||||
connector: Callable[[str], ProductOpenUiDevice],
|
||||
timeout_seconds: float,
|
||||
foreground_poll_interval_seconds: float = 0.2,
|
||||
monotonic_clock: Callable[[], float] = monotonic,
|
||||
sleep_function: Callable[[float], None] = sleep,
|
||||
) -> None:
|
||||
if timeout_seconds <= 0:
|
||||
raise ValueError("timeout_seconds 必须大于 0")
|
||||
if not _is_positive_finite(timeout_seconds):
|
||||
raise ValueError("timeout_seconds 必须是大于 0 的有限数值")
|
||||
if not _is_positive_finite(foreground_poll_interval_seconds):
|
||||
raise ValueError("foreground_poll_interval_seconds 必须是大于 0 的有限数值")
|
||||
self._adb_client = adb_client
|
||||
self._connector = connector
|
||||
self._timeout_seconds = timeout_seconds
|
||||
self._foreground_poll_interval_seconds = foreground_poll_interval_seconds
|
||||
self._monotonic_clock = monotonic_clock
|
||||
self._sleep_function = sleep_function
|
||||
|
||||
def open_and_capture(self, serial: str, product_url: str, output_directory: Path) -> ProductOpenResult:
|
||||
"""完成唯一允许的 Intent 打开及其后的只读取证。"""
|
||||
@@ -114,7 +124,7 @@ class ProductOpenCapturer:
|
||||
|
||||
# 版本精确匹配是 Intent 的前置条件,失败时绝不调用 start_pdd_view_intent。
|
||||
intent = self._adb_client.start_pdd_view_intent(serial, link.goods_id)
|
||||
_require_pdd_foreground(device.app_current())
|
||||
self._wait_for_pdd_foreground(device)
|
||||
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
staging = target.parent / f".{target.name}.staging-{uuid4().hex}"
|
||||
@@ -174,6 +184,21 @@ class ProductOpenCapturer:
|
||||
hierarchy_path=target / "hierarchy.xml",
|
||||
)
|
||||
|
||||
def _wait_for_pdd_foreground(self, device: ProductOpenUiDevice) -> None:
|
||||
"""只轮询当前 package,直到 deadline;Activity 和节点树均不参与本判据。"""
|
||||
|
||||
deadline = self._monotonic_clock() + self._timeout_seconds
|
||||
while True:
|
||||
if _is_pdd_foreground(device.app_current()):
|
||||
return
|
||||
remaining = deadline - self._monotonic_clock()
|
||||
if remaining <= 0:
|
||||
raise ProductPackageMismatchError(
|
||||
"商品链接打开后未在限定时间内进入拼多多,已停止后续取证。"
|
||||
)
|
||||
# 每个失败观察后都等待正的、受 deadline 约束的时长,避免 busy-loop。
|
||||
self._sleep_function(min(self._foreground_poll_interval_seconds, remaining))
|
||||
|
||||
|
||||
def _validate_new_target(target: Path) -> None:
|
||||
if target.exists():
|
||||
@@ -197,9 +222,12 @@ def _require_expected_version(app_info: dict[str, Any]) -> str:
|
||||
return version
|
||||
|
||||
|
||||
def _require_pdd_foreground(current: dict[str, Any]) -> None:
|
||||
if not isinstance(current, dict) or current.get("package") != PDD_PACKAGE:
|
||||
raise ProductPackageMismatchError("商品链接打开后前台应用不是拼多多,已停止后续取证。")
|
||||
def _is_positive_finite(value: object) -> bool:
|
||||
return isinstance(value, (int, float)) and not isinstance(value, bool) and value > 0 and isfinite(value)
|
||||
|
||||
|
||||
def _is_pdd_foreground(current: object) -> bool:
|
||||
return isinstance(current, dict) and current.get("package") == PDD_PACKAGE
|
||||
|
||||
|
||||
def _manifest(
|
||||
|
||||
Reference in New Issue
Block a user