经过一段时间的优化,PerfDog4.0 全新来袭,先来看看都更新了神马
【新增】新增图表常见操作提示功能
【新增】新增高阶功能,子进程帧率精准测试
【优化】彻底解决部分 Android 手机无法获取电量和内存等问题
【优化】解决 iOS 低版本无法获取 FPS 等问题
【修复】修复一些已知问题
我们发现新版增加了一些常用 tip
优化了一些安卓以及 ios 部分机型或者系统版本无法获取部分数据的问题,如果还有一些获取不到数据或者连接的问题,请查看
PerfDog 使用连接相关说明
这个子进程帧率获取对于目前来说使用多进程的应用来说非常有用哦,可能有些同学对于这个概念不是很清晰,我们来一步步探索;
多进程简介:
Android 平台,一般大型 APP,比如游戏有时候是多进程协作运行(微信小游戏,微视等 APP 及王者荣耀等游戏多子进程),可选择目标子进程进行针对性测试。默认是主进程。如图王者荣耀
弄清出子进程帧率之前先需要搞清楚 Android 窗口是是啥。下面简要的概述下,具体还不理解的请度娘SurfaceFliger,或者查看这篇文章扒一扒安卓渲染原理
简单来说 surfaceflinger 是安卓系统的一个服务,作用是接受多个来源的图形显示数据,将他们合成,然后发送到显示设备;
我们来看看官方的描述
大类去看,一般 Android 主窗口 Activity 和与之对应的 View,有一种特殊的 View,如 SurfaceView,他会独享一个 Surface,不与主窗口共享 Surface,独立渲染非常高效,支持 OpenglES 渲染。也就是说可能会出现两类窗口 FPS。一个是 Activety 窗口帧率和 SurfaceView 窗口帧率。
在什么情况下会出现两种类型窗口都有的情况呢?一般是游戏类型、直播类、视屏流、小游戏等类型应用会出现有两种窗口。特殊情况下,可能会出现多个 Activity 和多个 SurfaceView,对统计帧率造成很大困难,需要一定策略获取帧率。
游戏、直播、视频、小游戏等类型应用,PerfDog 默认获取的是 SurfaceView 的 FPS。其他传统应或 Web 用获取的是 Activity 帧率。
同时 PerfDog 高阶功能,支持用户自己选择窗口类型帧率,特别针对小程序,小游戏等,能更精准测试目标窗口帧率,如下图 (微信小游戏 - 欢乐斗地主)
小游戏、直播、视频、游戏等类型应用,请选择 SurfaceView
我们来总结一下:
如果我们子进程的 View 是 SurfaceView,那么子进程会独享一个 Surface,不与主窗口共享 Surface;
这里拿一个视频页举例,主线程和子线程实际上可以理解为叠加的状态,二者互不干扰;也就是说我们获取主线程的帧率是下面主进程的数据,而子进程帧率是中间的一小块轮播图的帧率数据,二者是独立的
在更新 4.0 后,我们在 setting 界面可以发现可以自定义上传的云平台了
这里要怎么使用呢,如果我们有自己的云平台,那我们自定义一个链接请求就可以了;
下面我用 Tornado 框架做个简单的例子
首先定义路由:
(r"/", GetPerfDogData),
(r"/icon", GetPerfDogIcon),
(r"/screenshots", GetPerfDogScreenShots),
(r"/done", GetPerfDogDone),
接下来处理方法
class GetPerfDogData(tornado.web.RequestHandler):
"""
:fuc:获取PerfDog性能数据
"""
def post(self):
data = self.request.body.decode('utf8')
data = parse.unquote(data)
print("性能数据:",data)
self.finish(
{
"errCode": 0,
"errStr": "",
"reportId": ""
})
完整代码:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web, tornado.ioloop
from urllib import parse
def Set_Ima(data):
with open("img.jpg", "wb") as f:
f.write(data["body"]) # 生成一张img.jpg的图片
class GetPerfDogData(tornado.web.RequestHandler):
"""
:fuc:获取PerfDog性能数据
"""
def post(self):
data = self.request.body.decode('utf8')
data = parse.unquote(data)
print("性能数据:",data)
self.finish(
{
"errCode": 0,
"errStr": "",
"reportId": ""
})
class GetPerfDogIcon(tornado.web.RequestHandler):
"""
:fuc:获取PerfDog应用图标
"""
def put(self):
data = self.request.files['icon']
print("Icon图标:", data)
for imadata in data:
Set_Ima(imadata)
self.finish(
{
"errCode": 0,
"errStr": "",
"reportId": ""
})
class GetPerfDogScreenShots(tornado.web.RequestHandler):
"""
:fuc:获取PerfDog测试过程中生成的应用截图
"""
def post(self):
pass
class GetPerfDogDone(tornado.web.RequestHandler):
"""
:fuc:完成上传测试数据
"""
def put(self):
pass
if __name__ == '__main__':
CONTENTS_LIST = []
settings = {
'template_path': 'template',
'static_path': 'static',
}
application = tornado.web.Application([
(r"/", GetPerfDogData),
(r"/icon", GetPerfDogIcon),
(r"/screenshots", GetPerfDogScreenShots),
(r"/done", GetPerfDogDone),
], **settings)
application.listen(8868)
tornado.ioloop.IOLoop.instance().start()
运行脚本,把 ip 地址 + 端口号填进 server 里,如果有域名就填写域名,
点击上传数据,就可以看到数据信息啦
这里贴出一下目前的接口文档:
接口说明
baseURL
需要配置,例如:http://abc.com/report
开始上传测试数据
• path:
• method: post
• header:
– Content-Type: 'multipart/form-data'
• req
– file_format: json/pb
– data: file
• resp
{
errCode: 0,
errStr: "",
reportId: ""
}
上传测试过程中生成性能数据,如fps、卡顿等等
设置测试应用图标
• path: /icon
• method: put
• header:
– Content-Type: 'multipart/form-data'
• req
– reportId
– icon: file
• resp
{
errCode: 0,
errStr: ""
}
上传测试过程中生成的应用截图
• path: /screenshots
• method: post
• header:
– Content-Type: 'multipart/form-data'
• req
– reportId
– file1: file
– file2: file
– file3: file
– file4: file
– file5: file
– ...
• resp
{
errCode: 0,
errStr: ""
}
一次可以上传多张 或者使用zip压缩多张图片上传
完成上传测试数据
• path: /done
• method: put
• header:
– Content-Type: 'multipart/form-data'
• req
– reportId
• resp
{
errCode: 0,
errStr: ""
}
注意这是 4.0 版本的接口文档哦。以后没准会变得哟 !
再贴一份 flask 框架完整的 demo 吧
report 文件
# coding: utf-8
import os
import uuid
from werkzeug.utils import secure_filename
class Report(object):
def __init__(self, base_dir):
self.base_dir = base_dir
def set_data_by_pb(self, file):
filename = self.get_filename('data.pb')
file.save(filename)
def set_data_by_json(self, file):
filename = self.get_filename('data.json')
file.save(filename)
def set_icon(self, file):
filename = self.get_filename('icon.png')
file.save(filename)
def add_screenshots(self, files):
for file in files:
filename = self.get_filename(secure_filename(file.filename))
file.save(filename)
def done(self):
filename = self.get_filename('done')
with open(filename, 'wb'):
pass
def get_filename(self, filename):
return '%s%s%s' % (self.base_dir, os.sep, filename)
class ReportManager(object):
def __init__(self):
self.base_dir = os.path.split(os.path.realpath(__file__))[0] + os.sep + 'data'
if not os.path.exists(self.base_dir):
os.mkdir(self.base_dir)
def create_report(self):
report_id = str(uuid.uuid4())
report_dir = self.get_report_dir(report_id)
os.mkdir(report_dir)
return report_id, Report(report_dir)
def get_report(self, report_id):
report_dir = self.get_report_dir(report_id)
return Report(report_dir)
def get_report_dir(self, report_id):
return '%s%s%s' % (self.base_dir, os.sep, report_id)
reportManager = ReportManager()
def create_by_pb(file):
(report_id, report) = reportManager.create_report()
report.set_data_by_pb(file)
return report_id
def create_by_json(file):
(report_id, report) = reportManager.create_report()
report.set_data_by_json(file)
return report_id
def set_icon(report_id, value):
report = reportManager.get_report(report_id)
report.set_icon(value)
def add_screenshots(report_id, screenshots):
report = reportManager.get_report(report_id)
report.add_screenshots(screenshots)
def done(report_id):
report = reportManager.get_report(report_id)
report.done()
demo 文件
#! /usr/bin/python
# coding: utf-8
from flask import Flask, request, jsonify
import report
app = Flask(__name__)
@app.route('/report', methods=['POST'])
def create_report():
file_format = request.form['file_format']
if file_format == 'pb':
report_id = report.create_by_pb(request.files['data'])
elif file_format == 'json':
report_id = report.create_by_json(request.files['data'])
if report_id is None:
res = {'errCode': -1, 'errStr': 'invalid arguments'}
else:
res = {'errCode': 0, 'errStr': 'success', 'reportId': report_id}
return jsonify(res)
@app.route('/report/icon', methods=['PUT'])
def set_icon():
report_id = request.form['reportId']
report.set_icon(report_id, request.files['icon'])
return jsonify({'errCode': 0, 'errStr': 'success'})
@app.route('/report/screenshots', methods=['POST'])
def add_screenshots():
report_id = request.form['reportId']
files = []
for name in request.files:
files.append(request.files[name])
report.add_screenshots(report_id, files)
return jsonify({'errCode': 0, 'errStr': 'success'})
@app.route('/report/done', methods=['PUT'])
def done():
report_id = request.form['reportId']
report.done(report_id)
return jsonify({'errCode': 0, 'errStr': 'success'})
好啦,可以开始愉快的玩耍啦!