基于Flask+echarts+socketio的前端动态展示android app的CPU、内存占用

如需转载请注明出处。

先来一张效果图:

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实现。

如需转载请注明出处。

猜你喜欢

转载自blog.csdn.net/weixin_38256474/article/details/81219937
今日推荐