一、前置说明
在Appium自动化中,经常需要使用adb命令与设备进行交互,所以有必要把常用的adb操作封装成一个类
二、代码实现
import os
import platform
import re
import subprocessfrom common import path
from common.exception import AndroidSDKUninstalledError, AndroidDevicesNotFoundError
from common.logger import loggerclass ADBRunner:def __init__(self):self._check_adb_is_installed()@staticmethoddef run_adb(command):try:result = subprocess.run(command, capture_output=True, text=True, check=True, shell=True)logger.debug(f"Execute adb command: {command}")return result.stdout.strip()except subprocess.CalledProcessError as e:logger.error(f"Execute adb command failure: {e}")return Nonedef _check_adb_is_installed(self):result = self.run_adb("adb --version")if not result:raise AndroidSDKUninstalledError('Android SDK is not installed or configured.')return resultdef adb_connect_device(self, device):return self.run_adb(f'adb connect {device}')def get_connected_device_udids(self):"""获取所有连接设备的序列号udid"""res = self.run_adb('adb devices')pattern = r'\b((?!of\b)\S+)\s+device'devices = re.findall(pattern, res)if not devices:raise AndroidDevicesNotFoundError('No connected mobile devices found.')logger.info(f'Devices found: {devices}')return devices@propertydef _grep(self):if platform.system() == 'Windows':return 'findstr'else:return 'grep'def get_activities(self, udid=None):"""获取当前设备的所有top activities, 输出结果示例:['com.android.settings/.Settings c4e2e17 pid=3637','com.mumu.launcher/.Launcher 9392da7 pid=1434','com.android.browser/com.android.settings de68b73 pid=3722']"""if not udid:udid = self.get_connected_device_udids()[0]command = f'adb -s {udid} shell dumpsys activity top | {self._grep} ACTIVITY'tops = self.run_adb(command).split('ACTIVITY')tops = [top.strip(' ').strip('\n') for top in tops if top]logger.debug(f'The top activities on device {udid} are: {tops}')return topsdef get_app_package_and_activity(self, udid=None):"""从 com.android.settings/.Settings c4e2e17 pid=3637,获取包名和活动页面名称输出:['com.android.settings', '.Settings']用途:capabilities = {"platformName": "Android","automationName": "uiautomator2","deviceName": "9YS0220306003185","appPackage": "com.tencent.mm", # 包名"appActivity": ".ui.LauncherUI", # 活动页面名称}"""last_activity_info = self.get_activities(udid)[-1]pattern = r'(\S+) (\S+) pid=(\d+)'match = re.match(pattern, last_activity_info)if match:package, activity = match.group(1).split('/')return [package, activity]else:raisedef get_apk_path(self, package_name, udid=None):"""从设备中使用包名,获取应用程序的APK路径。"""if not udid:udid = self.get_connected_device_udids()[0]# 使用pm path命令获取应用程序的APK路径command = f'adb -s {udid} shell pm path {package_name}'result = self.run_adb(command)if result and result.startswith('package:'):apk_path = result.replace('package:', '').strip()return apk_pathelse:logger.error(f"APK path for package '{package_name}' not found on the device {udid}.")return Nonedef get_apk_version(self, package_name, udid=None):"""获取应用程序的版本号。"""if not udid:udid = self.get_connected_device_udids()[0]# 使用dumpsys package命令获取应用程序的版本号command = f'adb -s {udid} shell dumpsys package {package_name} | {self._grep} versionCode'result = self.run_adb(command)match = re.search(r'versionCode=(\d+)', result)if match:version_code = match.group(1)return version_codeelse:logger.error(f"Version code not found for package '{package_name}'.")return Nonedef pull_apk_from_device(self, package_name, output_dir=None, apk_name=None, udid=None):"""根据包名从当前设备中将应用程序的APK文件复制至本地。用于:capabilities = {"platformName": "Android","automationName": "uiautomator2","deviceName": "9YS0220306003185","app": apk_path, # 用在这里# "appPackage": "com.tencent.mm",# "appActivity": ".ui.LauncherUI",}"""if not udid:udid = self.get_connected_device_udids()[0]if not output_dir:output_dir = path.get_apk_resources_dir()# 获取应用程序的APK路径apk_path = self.get_apk_path(package_name)apk_name = apk_name if apk_name else package_nameapk_version = self.get_apk_version(package_name)if apk_path:# 构建本地输出路径local_output_path = os.path.join(output_dir, f"{apk_name}_{apk_version}.apk")# 使用adb pull命令将APK复制到本地command = f'adb -s {udid} pull {apk_path} {local_output_path}'result = self.run_adb(command)if result:logger.info(f"APK successfully pulled to: {local_output_path}")return local_output_pathelse:logger.error("Failed to pull APK from device.")if __name__ == '__main__':adb = ADBRunner()print(adb.get_connected_device_udids())print(adb.get_app_package_and_activity())print(adb.get_apk_path('cn.com.open.mooc'))print(adb.get_apk_version('cn.com.open.mooc'))print(adb.pull_apk_from_device('cn.com.open.mooc'))
欢迎技术交流: