移动端线上问题定位的几个场景:
场景一: 云端用户反馈某功能不可用,RD猜测几种可能触发原因,线上收集的客户端打点日志信息不全,无法完全确认问题。陷入死循环,线上用户持续暴露问题,线下无法稳定复现,不能及时定位问题。如何破?
场景二:通过客户端预埋方式打点用户行为,往往会出现打点不全的问题,而往往线上问题的定位需要这些行为日志为问题定位提供良好的复现步骤。 如何无需编码,通过技术手段获取全量用户与客户端交互日志?
场景三:线下很多好工具可以SDK化,给线上问题发现和定位带来大量正向收益,但因为测试能力本身会影响集成app的性能,或开发团队排期等原因,无法集成,大量线上问题无法充分暴露。如何优美的解决这种问题?
场景四:百度的线上客户端小流量实验表明,线上问题实际是一种正态分布的随机事件,TOP问题,往往只需抽样很少量用户即可召回,而不需要影响全量用户
移动端线下测试的几个场景:
场景一: 客户端测试简单却又复杂,一个客户端测试人员的简单技能树可能包括这些问题的分析能力: ANR、crash、卡顿、内存泄露、内存、CPU占用、电量分析、启动速度分析等等一系列的技能。而往往,部分QA人员并不是全栈。并且这些工具的使用本身,就是一个工具大集合。如何让客户端测试人员,或非专业测试人员,无需任何背景,只需要点一点就可以具备全部客户端问题分析能力
场景二:同刚才说的线上问题定位,线下大量的优秀功能,并不能适用于全量用户,因为他们本身就是伤敌八百,自伤一千的能力。如何在尽量小用户体验损耗的前提下,让问题尽量的全部召回?
基于插件系统,做一个测试插件,把我们所有觉得有用的线下测试能力打进去,同时集成业内知名的好框架,譬如dexposed、 leakcanary等,还有一些系统开放了但是主版使用会影响性能的好东西traceviewer、Choreographer、ActivityLifecycleCallback等。统统打到云插件中,并通过云端已经构建好的动态模块小流量系统下发到特定目标用户手机中,持续暴露问题
NICE JOB!!
云插件工作原理:
云插件本身就是宿主的一个插件而已,真正能够发挥它的测试能力的是,线下构建的大量测试场景,以及插件本身的动态加载机制,这样我们的测试场景就可以在线上发挥它的效力了。提到这个就不得不新锅炒旧饭:
双亲委托机制:java的类加载机制下,子classloader可以向上查找父classloader的加载内容,从而给云插件动态查找宿主各种类信息提供了先决条件(多进程化的插件系统。。请忽略我把)
dexclassloader:可以加载文件系统的任意JAR(包含dex文件)、zip、apk文件
patchclassloader: 只可以加载data/app/的apk文件,常用于多dex拆分项目
dexfile: 可以加载动态文件,同时提取文件内部的类信息,这个是dexclassloader不具备的
破壳: 云插件自己的场景需要集成信息上传类,但在编译时,不能将对应类编译进插件包。这样场景插件在被加载起来以后,就可以回调宿主的日志系统进行信息采集了
经过如上,下图中的JAR或APK中包裹的场景插件就可以被云插件动态加载进来,同时对宿主的各种类、本地空间、以及系统中与宿主相关的信息进行读取、采集。至于是hook、反射、代码注入、异常捕获、插桩等这些只是一种手段了
注意: 下面的case,虽然说的是云插件的问题定位场景,但是不止云插件,我们后面会以SDK形式开源这部分技术,所以,集成了这个SDK的app也可以这么干,但是脱离了插件系统,本身的安全性,需要集成的开发者关注自己的安全性。 当然也可以不关注,root的手机上,你的app本身就已经全部暴露给了黑客(BLESS…)
正文: 以流畅度为例,我们看如何非常快速的构建云调试插件的case吧
流畅度:可以理解为android系统绘制UI的速度,理论上,人眼在1s内接收60帧图像才会感觉程序流畅。 android系统之初,流畅度一直是人们诟病的目标,直到android 4.1系统的时候,有了注明的project Buffer,并引入了三大元素,VSYNC(垂直同步)、Triple Buffer和Choreographer。其中Choreographer这个东西是我们今天讨论的目标。它是整套机制中的协调者,并且所有looper都共用一个Choreographer对象
Choreographer对外开放了一个FrameCallback的东西,在每次系统绘制时,都会通过这个回调doFrame函数,通过这个函数可以计算出在1s内,当前页面的绘制次数。但是问题来了:
看上图,含义就是,此货虽然好,但是建议是你们开发者还是不要用了。。。这如何玩,本来还想拿它上个流畅度监控的。。此时一定会想到,我们有云调试插件。
构建很简单,如下,只需要把这段代码copy到任意android工程,然后打包,注意NutXError只作为编译依赖即可, 如果已有插件系统, 这段代码就可以直接被加载并运行了
public class
NutFrameMonitor extends BaseCase {
private static final String TAG = "NutFrameMonitor";
@Override
public void invoke(Context context) {
int sdkVersion = 0;
try {
sdkVersion = Build.VERSION.SDK_INT;
} catch (Exception e) {
// TODO
}
if (sdkVersion >= 16) {
try {
Choreographer.getInstance().postFrameCallback(NutFrameCallback.callback);
} catch (Exception e) {
// TODO
}
}
}
private static class NutFrameCallback implements Choreographer.FrameCallback {
static final NutFrameCallback callback = new NutFrameCallback();
private long mLastFrameTimeNanos = 0;
private long mFrameIntervalNanos = (long)(500000000) - 1;
@Override
public void doFrame(long frameTimeNanos) {
if (mLastFrameTimeNanos != 0) {
final long jitterNanos = frameTimeNanos - mLastFrameTimeNanos;
if (jitterNanos > mFrameIntervalNanos) {
NutXError error = new NutXError(TAG);
error.setDetailMessage("frame Choreographer waste more than 500ms");
error.dump();
}
}
mLastFrameTimeNanos = frameTimeNanos;
Choreographer.getInstance().postFrameCallback(NutFrameCallback.callback);
}
}
}
下面这个截图是在操作客户端运行的时候(注意监控case是通过云插件动态加载的哟),发现的一个流畅度问题。同时也可以看到,在监控到绘制问题后,用户停留的每个界面也被画了出来。 然后线下就可以沿着这条trace去复现结果了~
是不是很酷~~~
如上,便结束了对百度在移动端测试的技术探讨。然而,其实有很多没提到,譬如现在已经构建好的自动化case有哪些、这套机制兼容性如何等,再譬如有眼尖的同学也会发现这套系统本身可能就会有兼容性问题,如何做好问题卡控,保证云调试插件(其实我们叫坚果云)有效及时回收。 其实,我们有充足的自信在线上尝试这个东西。在厂子的线上,我们一整套动态模块小流量系统,云插件本身其实就被作为了一个动态模块,当线上出现问题时,我们的云插件就会发挥它的价值了
百度MTC是业界领先的移动应用测试服务平台,为广大开发者在移动应用测试中面临的成本、技术和效率问题提供解决方案。同时分享行业领先的百度技术,作者来自百度员工和业界领袖等。
本文作者:hyxbiao && tony xin
更多干货分享请关注“百度MTC学院”http://mtc.baidu.com/academy/article