fix: clean failed UIAutomator2 startups before retrying
This commit is contained in:
1 parent
4d6560dbd4
commit
3fbcdfefea
4 files changed
+304
-5
No files matched your search
@@ -247,6 +247,48 @@ def _ensure_maestro_not_installed(device_id: str) -> None:
|
||||
_uninstall_package(device_id, MAESTRO_PACKAGE)
|
||||
|
||||
|
||||
class UIAutomatorConnectionError(RuntimeError):
|
||||
"""Terminal connection failure with device context and the original cause."""
|
||||
|
||||
def __init__(self, device_id: str, attempts: int, cause: Exception) -> None:
|
||||
# The library appends its whole output buffer on every readiness poll.
|
||||
# Keep the original exception as __cause__, but do not repeat those
|
||||
# identical startup lines dozens of times in the user-facing failure.
|
||||
lines = [line for arg in cause.args for line in str(arg).splitlines()]
|
||||
detail = "\n".join(dict.fromkeys(lines)) or str(cause)
|
||||
super().__init__(
|
||||
f"UIAutomator2 连接失败(设备:{device_id};已尝试 {attempts} 次)。"
|
||||
f"\n底层原因:{type(cause).__name__}: {detail}"
|
||||
)
|
||||
|
||||
|
||||
class _ManagedUIAutomatorDevice(u2.Device):
|
||||
"""Release a failed launch before the constructor can lose its handle.
|
||||
|
||||
UIAutomator2 registers exit cleanup only after startup succeeds. Its
|
||||
partially constructed Device is not returned when readiness fails, so
|
||||
the caller cannot stop that launch. Intercept the startup boundary here
|
||||
instead of modifying the installed library or killing device processes.
|
||||
"""
|
||||
|
||||
def start_uiautomator(self) -> None:
|
||||
try:
|
||||
super().start_uiautomator()
|
||||
except BaseException:
|
||||
# _process is set only for a server launched by this instance;
|
||||
# attaching to a server already on the device leaves it None.
|
||||
if self._process is not None:
|
||||
try:
|
||||
super().stop_uiautomator(wait=False)
|
||||
except Exception as cleanup_error:
|
||||
# Cleanup must never hide the startup error or interrupt.
|
||||
logger.warning(
|
||||
f"Failed to stop this UIAutomator2 launch on {self._dev.serial}: "
|
||||
f"{cleanup_error}"
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
class UIAutomatorClient:
|
||||
"""UIAutomator2 client for Android screen data retrieval.
|
||||
|
||||
@@ -297,7 +339,7 @@ class UIAutomatorClient:
|
||||
if attempt:
|
||||
time.sleep(1.0 * attempt)
|
||||
try:
|
||||
self._device = u2.connect(self._device_id)
|
||||
self._device = _ManagedUIAutomatorDevice(self._device_id)
|
||||
break
|
||||
except Exception as exc:
|
||||
last_error = exc
|
||||
@@ -307,7 +349,9 @@ class UIAutomatorClient:
|
||||
)
|
||||
else:
|
||||
self._awake_strategy = None
|
||||
raise last_error
|
||||
raise UIAutomatorConnectionError(
|
||||
self._device_id, attempt + 1, last_error
|
||||
) from last_error
|
||||
logger.info("UIAutomator2 connected successfully")
|
||||
return self._device
|
||||
|
||||
|
||||
Reference in new issue
Block a user