115 lines
3.6 KiB
Python
115 lines
3.6 KiB
Python
"""uiautomator2 通用辅助函数。
|
||
|
||
所有任务都可复用:应用启动保障、等待首页、安全点击、随机操作等。
|
||
具体业务逻辑(如抖音首页特征元素判断)不放这里,放各自 task 文件。
|
||
"""
|
||
import time
|
||
import random
|
||
|
||
from core.logger import get_logger
|
||
|
||
_log = get_logger("core.u2")
|
||
|
||
|
||
def ensure_app_running(d, package, max_restart=3, home_check=None):
|
||
"""确保 app 在前台运行,被人为退出/崩溃则自动重启。
|
||
|
||
d: u2.Device
|
||
package: 包名
|
||
max_restart: 最大重启次数
|
||
home_check: 可选回调 d->bool,判断是否进入主页。
|
||
默认只看包名(包名对就返回 True,和原抖音逻辑一致)。
|
||
返回 True 表示 app 在前台;连续重启失败返回 False。
|
||
"""
|
||
home_check = home_check or (lambda d: True)
|
||
for attempt in range(1, max_restart + 1):
|
||
try:
|
||
cur = d.app_current()
|
||
except Exception:
|
||
cur = {}
|
||
if cur.get("package") == package and home_check(d):
|
||
return True
|
||
_log.info(f"{package} 不在前台(当前:{cur}),第 {attempt}/{max_restart} 次重启...")
|
||
try:
|
||
d.app_start(package, wait=True)
|
||
time.sleep(3)
|
||
if home_check(d):
|
||
return True
|
||
except Exception as e:
|
||
_log.warning(f"重启异常: {e}")
|
||
time.sleep(3)
|
||
return False
|
||
|
||
|
||
def wait_for_app_home(d, package, home_check, timeout=40):
|
||
"""等待 app 进入主页。
|
||
|
||
home_check: d->bool 回调,返回 True 表示已进入主页。
|
||
抖音主页 Activity 名仍叫 SplashActivity,不能靠 Activity 名判断,
|
||
要用界面特征元素(由调用方通过 home_check 传入)。
|
||
"""
|
||
deadline = time.time() + timeout
|
||
while time.time() < deadline:
|
||
try:
|
||
cur = d.app_current()
|
||
except Exception:
|
||
cur = {}
|
||
if cur.get("package") == package and home_check(d):
|
||
return True
|
||
time.sleep(2)
|
||
return False
|
||
|
||
|
||
def random_sleep(min_s, max_s):
|
||
"""随机睡眠(模拟人类操作间隔)。"""
|
||
time.sleep(random.uniform(min_s, max_s))
|
||
|
||
|
||
def safe_click(el, timeout=1):
|
||
"""安全点击:元素存在才点,不抛异常。返回是否点击成功。"""
|
||
try:
|
||
if el.exists(timeout=timeout):
|
||
el.click()
|
||
return True
|
||
except Exception:
|
||
pass
|
||
return False
|
||
|
||
|
||
def safe_set_text(el, text, timeout=1):
|
||
"""安全输入文本:元素存在才输入,不抛异常。返回是否成功。"""
|
||
try:
|
||
if el.exists(timeout=timeout):
|
||
el.set_text(text)
|
||
return True
|
||
except Exception:
|
||
pass
|
||
return False
|
||
|
||
|
||
def find_and_click(d, timeout=1, **selectors):
|
||
"""按定位器找元素并点击,找不到不抛异常。返回是否成功。
|
||
|
||
用法:find_and_click(d, text="首页") 或 find_and_click(d, description="搜索")
|
||
"""
|
||
try:
|
||
el = d(**selectors)
|
||
if el.exists(timeout=timeout):
|
||
el.click()
|
||
return True
|
||
except Exception:
|
||
pass
|
||
return False
|
||
|
||
|
||
def random_swipe_up(d, width_ratio=0.5, height_start=0.8, height_end=0.2, duration=None):
|
||
"""随机上滑(刷视频常用)。坐标按屏幕比例算,适配不同分辨率。
|
||
|
||
duration: 滑动时长(秒),None 则随机 0.25~0.50s。
|
||
"""
|
||
info = d.info
|
||
w, h = info["displayWidth"], info["displayHeight"]
|
||
dur = duration or random.uniform(0.25, 0.50)
|
||
d.swipe(int(w * width_ratio), int(h * height_start),
|
||
int(w * width_ratio), int(h * height_end), dur)
|