import subprocess
import re
import time
def run_adb_command(command):
"""执行ADB命令并返回输出"""
try:
result = subprocess.run(
command,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
if result.returncode != 0:
return None
return result.stdout.strip()
except:
return None
def get_foreground_activity():
"""获取前台应用Activity名称"""
# 根据Android版本选择不同的命令(引用[2])
android_version = run_adb_command("adb shell getprop ro.build.version.release")
if android_version and float(android_version[:3]) >= 9.0:
command = "adb shell dumpsys activity activities | grep mResumedActivity"
else:
command = "adb shell dumpsys activity activities | grep mFocusedActivity"
output = run_adb_command(command)
if not output:
return None
# 解析Activity名称
match = re.search(r'(\S+)/', output)
return match.group(1) if match else None
def get_app_fps(package_name):
"""获取应用帧率(FPS)"""
# 方法1: 使用gfxinfo命令(需要开发者选项中开启GPU渲染分析)
gfx_output = run_adb_command(f"adb shell dumpsys gfxinfo {package_name}")
if gfx_output:
# 解析帧时间数据
frame_times = re.findall(r'(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)', gfx_output)
if frame_times:
# 计算最近帧的平均FPS
total_frames = len(frame_times)
total_time = sum(float(frame[2]) for frame in frame_times) / 1000 # 转换为秒
return round(total_frames / total_time, 1) if total_time > 0 else 0
# 方法2: 使用SurfaceFlinger(引用[1])
surface_output = run_adb_command(f"adb shell dumpsys SurfaceFlinger | grep {package_name}")
if surface_output:
# 解析帧率数据
match = re.search(r'(\d+) fps', surface_output)
if match:
return int(match.group(1))
return None
def get_surface_fps():
"""直接获取SurfaceFlinger的全局帧率"""
output = run_adb_command("adb shell dumpsys SurfaceFlinger --latency")
if output:
lines = output.split('\n')
if len(lines) > 2:
timestamps = [int(line.split()[0]) for line in lines[1:] if line.strip()]
if len(timestamps) > 1:
# 计算帧间隔时间
time_diff = (timestamps[-1] - timestamps[0]) / 1e9 # 纳秒转秒
return round((len(timestamps) - 1) / time_diff, 1)
return None
def get_refresh_rate():
"""获取屏幕刷新率(Hz)"""
# 方法1: 通过display服务
display_output = run_adb_command("adb shell dumpsys display | grep mRefreshRate")
if display_output:
match = re.search(r'mRefreshRate=([\d.]+)', display_output)
if match:
return float(match.group(1))
# 方法2: 通过SurfaceFlinger
surface_output = run_adb_command("adb shell dumpsys SurfaceFlinger | grep refresh-rate")
if surface_output:
match = re.search(r'refresh-rate: ([\d.]+)', surface_output)
if match:
return float(match.group(1))
# 方法3: 通过wm命令
wm_output = run_adb_command("adb shell wm density")
if wm_output:
match = re.search(r'Physical size: \d+x\d+ @ ([\d.]+)', wm_output)
if match:
return float(match.group(1))
return 60.0 # 默认值
def get_display_info():
"""获取完整的显示信息"""
fps = get_surface_fps() or get_app_fps(get_foreground_activity())
refresh_rate = get_refresh_rate()
return {
"app_fps": fps,
"refresh_rate": refresh_rate,
"vsync_missed": fps and refresh_rate and (fps < refresh_rate * 0.9)
}
if __name__ == "__main__":
# 示例使用
package = "com.android.settings" # 替换为目标包名
print("正在获取性能数据...")
# 获取前台应用
foreground_app = get_foreground_activity()
print(f"前台应用: {foreground_app or '未知'}")
# 获取帧率
app_fps = get_app_fps(package)
surface_fps = get_surface_fps()
print(f"应用帧率(FPS): {app_fps or 'N/A'}")
print(f"Surface帧率(FPS): {surface_fps or 'N/A'}")
# 获取刷新率
refresh_rate = get_refresh_rate()
print(f"屏幕刷新率: {refresh_rate} Hz")
# 综合信息
display_info = get_display_info()
print(f"显示同步状态: {'正常' if not display_info['vsync_missed'] else 'VSync丢失'}")
#!/usr/bin/env python3
# -*- coding: gbk -*- # 声明GBK编码
import subprocess
import re
import time
import csv
from datetime import datetime
class DeviceMonitor:
def __init__(self, interval=5, output_file="device_performance.csv"):
self.interval = interval # 监控间隔(秒)
self.output_file = output_file
self.header_written = False
def check_adb_connection(self):
"""验证ADB设备连接状态"""
try:
result = subprocess.run(
"adb devices",
shell=True,
capture_output=True,
text=True,
encoding='gbk'
)
if "device" not in result.stdout:
print("设备未连接或未授权")
return False
return True
except Exception as e:
print(f"ADB连接检查失败: {str(e)}")
return False
def get_foreground_package(self):
"""获取当前前台应用包名"""
try:
# 方法1: 使用dumpsys window命令
result = subprocess.run(
"adb shell dumpsys window windows",
shell=True,
capture_output=True,
text=True,
encoding='gbk',
errors='ignore'
)
output = result.stdout
# 尝试多种匹配模式
patterns = [
r'mCurrentFocus=Window\{.*?\s([a-zA-Z0-9._]+)/',
r'mFocusedApp=AppWindowToken\{.*?\s([a-zA-Z0-9._]+)/',
r'ResumedActivity: ActivityRecord\{.*?\s([a-zA-Z0-9._]+)/'
]
for pattern in patterns:
match = re.search(pattern, output)
if match:
return match.group(1)
# 方法2: 使用dumpsys activity命令(备用)
result = subprocess.run(
"adb shell dumpsys activity activities",
shell=True,
capture_output=True,
text=True,
encoding='gbk',
errors='ignore'
)
match = re.search(r'ResumedActivity: ActivityRecord\{.*?\s([a-zA-Z0-9._]+)/', result.stdout)
return match.group(1) if match else "unknown"
except Exception as e:
print(f"获取前台应用失败: {str(e)}")
return "unknown"
def get_cpu_usage(self):
"""获取CPU使用率(%)"""
try:
# 获取总CPU时间和空闲时间
result = subprocess.run(
"adb shell top -n 1 -d 0.5 | grep {match.group(1)}",
shell=True,
capture_output=True,
text=True
)
cpu_stats = result.stdout.split()[1:]
total_time = sum(map(int, cpu_stats))
idle_time = int(cpu_stats[3])
# 计算使用率
usage = 100 * (1 - idle_time / total_time)
return round(usage, 1)
except:
return 0.0
def get_memory_usage(self):
"""获取内存使用情况(MB)"""
try:
result = subprocess.run(
"adb shell cat /proc/meminfo",
shell=True,
capture_output=True,
text=True
)
meminfo = result.stdout.splitlines()
total = int(meminfo[0].split()[1]) // 1024
free = int(meminfo[1].split()[1]) // 1024
used = total - free
return total, used, free
except:
return 0, 0, 0
def get_battery_info(self):
"""获取电池信息和温度(℃)"""
try:
result = subprocess.run(
"adb shell dumpsys battery",
shell=True,
capture_output=True,
text=True
)
output = result.stdout
level = re.search(r'level: (\d+)', output)
status = re.search(r'status: (\d+)', output)
temp = re.search(r'temperature: (\d+)', output)
return (
int(level.group(1)) if level else 0,
int(status.group(1)) if status else 0,
int(temp.group(1)) / 10.0 if temp else 0.0 # 转换为摄氏度
)
except:
return 0, 0, 0.0
def get_cpu_temperature(self):
"""获取CPU温度(℃) - 需要root权限"""
try:
result = subprocess.run(
"adb shell cat /sys/class/thermal/thermal_zone0/temp",
shell=True,
capture_output=True,
text=True
)
return float(result.stdout.strip()) / 1000.0
except:
return 0.0
def monitor_performance(self):
"""主监控循环"""
if not self.check_adb_connection():
print("设备连接失败,请检查ADB连接")
return
print(f"开始设备监控,间隔: {self.interval}秒")
print("按Ctrl+C停止监控...")
try:
while True:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
package = self.get_foreground_package()
cpu_usage = self.get_cpu_usage()
mem_total, mem_used, mem_free = self.get_memory_usage()
batt_level, batt_status, batt_temp = self.get_battery_info()
cpu_temp = self.get_cpu_temperature()
# 输出到控制台
print(f"[{timestamp}] 应用: {package[:20]:<20} | "
f"CPU: {cpu_usage}% | "
f"内存: {mem_used}/{mem_total}MB | "
f"电池: {batt_level}% | "
f"温度: CPU:{cpu_temp:.1f}℃ 电池:{batt_temp:.1f}℃")
# 保存到CSV文件
self.save_to_csv(timestamp, package, cpu_usage, mem_total,
mem_used, mem_free, batt_level, batt_status,
batt_temp, cpu_temp)
time.sleep(self.interval)
except KeyboardInterrupt:
print("\n监控已停止")
def save_to_csv(self, timestamp, package, cpu_usage, mem_total,
mem_used, mem_free, batt_level, batt_status,
batt_temp, cpu_temp):
"""保存数据到CSV文件"""
data = [
timestamp, package, cpu_usage, mem_total, mem_used, mem_free,
batt_level, batt_status, batt_temp, cpu_temp
]
with open(self.output_file, 'a', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
if not self.header_written:
writer.writerow([
'时间戳', '前台应用', 'CPU使用率(%)', '内存总量(MB)',
'已用内存(MB)', '空闲内存(MB)', '电池电量(%)', '电池状态',
'电池温度(℃)', 'CPU温度(℃)'
])
self.header_written = True
writer.writerow(data)
if __name__ == "__main__":
# 创建监控实例并启动(每10秒监控一次)
monitor = DeviceMonitor(interval=10)
monitor.monitor_performance()
将此两个脚本结合起来