如需转载请注明出处。
先来一张效果图:
win10+Python3.6.3
一、项目结构
autoTestDev
---->app 存放程序相关的所有文件
----|---->templates 存放html文件
----|----|---->cpumem.html
----|---->app.py 主文件.py
---->virtualenv Python虚拟环境
---->requirements.txt 需求文件
二、具体实现
1、app.py代码
'''
Function:a Monitor of Android APP for cpu and memory usage.
Once the back-end background thread generates data, it will be pushed to the front-end.
Advanatage:
1.No front-end ajax timing query required;
2.Save server resources.
'''
import time, os, re
from threading import Lock
import threading as thd
from flask import Flask, render_template, session, request
from flask_socketio import SocketIO, emit
import logging;logging.basicConfig(level=logging.INFO)
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'I\'m a secret!'
socketio = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()
def connect_adb():
cmd = ['adb connect 10.2.0.151','adb devices']
#con_out = os.popen(cmd[0]).read()
#logging.info(con_out)
devices_status = os.popen(cmd[1]).read()
logging.info(devices_status)
def cpu_mem():
device_status = True
if device_status == False:#You can write a judgement in case the ADB is disconnection.
connect_adb()
cmd = 'adb shell top -n 1 | findstr APP包名'
cpu_mem_list = []
#create_time = time.strftime('%H:%M:%S',time.localtime())
#cpu_mem_list.append(create_time)
cpumem_out = os.popen(cmd).read().strip()
cpumem_first = cpumem_out.split('\n')[0]
logging.info(cpumem_first)
pattern = r'\d*%|\d*K'
match = re.findall(pattern, cpumem_first)
#CPU usage
cpu_occuRate = match[0][0:-1]#Also remove the trailing '%' with '.strip('%')'
cpu_mem_list.append(int(cpu_occuRate))
#Memory usage
mem_occu_d = match[2][0:-1]
mem_occu = int(mem_occu_d) // 1024#The unit of mem_occu_d is Kb,converted to Mb by 1024. // floor division.
cpu_mem_list.append(mem_occu)
'''
return a list: [cpu usage, memory usage],E.g ['15', '247'].
'''
return cpu_mem_list
def intervalGet_cpumem():
thd.Timer(5,intervalGet_cpumem).start()
while True:
cpu_mem_data = cpu_mem()
time.sleep(5)
#Background thread generates data and instantly push to the front-end
def background_thread():
count = 0
while True:
socketio.sleep(5)
count += 1
t = time.strftime('%H:%M:%S', time.localtime())#Get system time.(time:minute:second)
cpumems = cpu_mem()
socketio.emit('server_response',{'data': [t, *cpumems], 'count': count},namespace='/test')
@app.route('/cpumem')
def cpumem():
return render_template('cpumem.html', async_mode=socketio.async_mode)
#Start back-end thread after establishing a socket connection with the front-end.
@socketio.on('connect', namespace='/test')
def test_connect():
global thread
with thread_lock:
if thread is None:
thread = socketio.start_background_task(target=background_thread)
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', port=8080, debug=True)
2、cpumem.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>性能测试数据展示</title>
<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.bootcss.com/socket.io/1.5.1/socket.io.min.js"></script>
<script src="http://echarts.baidu.com/dist/echarts.min.js"></script>
</head>
<body>
<div id="main" style="height:500px;border:1px solid #ccc;padding:10px;"></div>
<script type="text/javascript">
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption({
title: {
text: 'CPU占用率+内存占用'
},
tooltip: {
trigger:'axis'
},
legend: {
data:['cpu','mem']
},
xAxis: {
type:'category',
boundaryGap:false,
data: []
},
yAxis: {
type:'value'
},
series: [{
name: 'cpu',
type: 'line',
data: [],
lineStyle:{
normal:{
width:2
}
},
areaStyle:{
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#A9A9A9'
}, {
offset: 1,
color: '#ADD8E6'
}])
}
}
},{
name: 'mem',
type: 'line',
data: [],
lineStyle:{
normal:{
width:2
}
},
areaStyle:{
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#A9A9A9'
}, {
offset: 1,
color: '#ADD8E6'
}])
}
}
}]
});
//three Global variable:time、cpu、mem
var time = ["","","","","","","","","","","","","","",""],
cpu = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
mem = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
//prepare a unified callback function.
var update_mychart = function (res) { //res is a json response object
myChart.hideLoading();
//prepare data
time.push(res.data[0]);
cpu.push(parseFloat(res.data[1]));
mem.push(parseFloat(res.data[2]));
if (time.length >= 10){
time.shift();
cpu.shift();
mem.shift();
}
//fill data
myChart.setOption({
xAxis: {
data: time
},
//Corresponding to the corresponding series according to the name
series: [{
name: 'cpu',
data: cpu
},{
name: 'mem',
data: mem
}]
});
};
//First display loading animation
myChart.showLoading();
//Create a socket connection, wait for the server to "push" the data, and update the chart with a callback function.
$(document).ready(function() {
namespace = '/test';
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);
socket.on('server_response', function(res) {
update_mychart(res);
});
});
</script>
</body>
</html>
PS:前端js库如echarts.min.js、jquery.min.js、socket.io.min.js均可下载到static目录下(新增目录app/static)。上述js文件是通过在线的方式。
3、生成requirements.txt文件
(virtualenv) D:\autoTestDev>pip freeze > requirements.txt
click==6.7
Flask==1.0.2
Flask-SocketIO==3.0.1
itsdangerous==0.24
python-engineio==2.2.0
python-socketio==2.0.0
Werkzeug==0.14.1
在其他python环境下安装上述同样的项目环境命令:pip install -r requirements.txt
pip install flask(会附带安装click、itsdangerous、werkzeug);
pip install flask_socketio(附带安装python_engineio、socketio)。
三、效果展示
cmd或编辑器运行app.py文件。在此以cmd下运行为例:
(virtualenv) D:\autoTestDev\app>python app.py
上述http://0.0.0.0:8080表示局域网内均可访问,即此该程序所在机器为主机(如IP为 10.1.0.72),在局域网内设备下浏览器均可通过10.1.0.72:8080/cpumem来进行访问该前端网页。
四、展望
adb连接android设备的方式欠妥,因为存在以下问题:
a、很多android设备不执行adb连接,特别是TV设备;
b、adb连接很容易被断开。
据于此,可通过Java编写一个无界面的APK安装到android设备上,经它发送CPU、内存占用数据到服务器,再经前端处理。
如:改写emmagee或GT实现。
如需转载请注明出处。