背景
网络质量是移动端开发很关注的指标,典型场景宝贝详情展示,在网速好的时候出宝贝高清大图片,网络不好的时候出小图片,保证用户的体验和流量付出的平衡。
Network Connection Class是一个由Facebook公司出品的Android类库,用于监听App当前网络流量以及将其按照网络质量进行分类,其是如何做到呢?
原理
android是基于linux的,搞过服务端监控都知道,tsar等统计工具,也都是读取下内核记录的相关各种stat数据,那其实这个库也是一样的,其读取了/proc/net/xt_qtaguid/stats记录的信息
如上图例子,rx_bytes是接受到的数据字节数,tx_bytes则是发送出去的数量,其他参数可以自己看看就能明白,下面每一行代表一个用户下的流量情况。既然有这数据,那要做app对网络质量的监控,就有米了。
功能原理
1、用android的loop,hander,message机制,每1秒异步的去捞取数据分析,此库还是自己去读取文件,逐行过滤用户,选取自己Process.myUid()的数据进行统计,其实Android API8 以后提供了getUidRxBytes以及getUidTxBytes,也是读取这文件。在android.net.TrafficStats里面提供了完善的数据
这样可以直接利用,也可以忽略不同平台的差异性,比如有些版本是要读取/proc/uid_stat/***(uid)/tcp_rcv和tcp_snd文件等,可以等这系统接口取不到数据的时候,再自己想办法去捞取数据。不需先自己进行复杂的文件解析,轻松可以捞取tcp、udb、总量等各个维度数据。可以自己app,和总数据等进行比对分析
2、同时可添加个实现ConnectionClassManager.ConnectionClassStateChangeListener接口的listener,当网络变化的时候去进行回调。默认规则是,连续5次以上,新采样的网络质量一直恒定新值,与当前网络质量不同,才会触发listener回调。这里其实是对网络波动的灵敏度控制。
算法
网络质量定义:
/**
* Bandwidth under 150 kbps.
*/
POOR,
/**
* Bandwidth between 150 and 550 kbps.
*/
MODERATE,
/**
* Bandwidth between 550 and 2000 kbps.
*/
GOOD,
/**
* EXCELLENT - Bandwidth over 2000 kbps.
*/
EXCELLENT,
/**
* Placeholder for unknown bandwidth. This is the initial value and will stay at this value
* if a bandwidth cannot be accurately found.
*/
UNKNOWN
那怎么确定网络质量,因为从文件捞取的数据是字节数,因此都需要*8转换为每秒的比特位,如果每秒小于10的数据会被忽略,然后再去运算。
当app刚刚启动的时候,怎么样才能更精确反应当前的网速呢,其定义了个重要的常量
/**
* The factor used to calculate the current bandwidth
* depending upon the previous calculated value for bandwidth.
*
* The smaller this value is, the less responsive to new samples the moving average becomes.
*/
private static final double DEFAULT_DECAY_CONSTANT = 0.05;
对确定了另外两个常量
double mDecayConstant=
DEFAULT_DECAY_CONSTANT;
int mCutover=
decayConstant == 0.0? Integer.MAX_VALUE: (int) Math.ceil(1 / decayConstant);
核心算法:
/* * Adds a new measurement to the moving average.
* @param measurement - Bandwidth measurement in bits/ms to add to the moving average.
*/
public void addMeasurement(double measurement) {
double keepConstant = 1 - mDecayConstant;
if (mCount > mCutover) {
mValue = Math.exp(keepConstant * Math.log(mValue) + mDecayConstant * Math.log(measurement));
} else if (mCount > 0) {
double retained = keepConstant * mCount / (mCount + 1.0);
double newcomer = 1.0 - retained;
mValue = Math.exp(retained * Math.log(mValue) + newcomer * Math.log(measurement));
} else {
mValue = measurement;
}
mCount++;
}
看上去是否很高大上啊,还用math.exp和log,自然指数、欧拉数,还记得吗?我已经还给学校的体育老师了。自己补脑了
而计算的公式换个矮穷挫的类似写法就是
网速=之前平均值网速*旧权重比例 + 新采样的速度*新采样的权重值
那之前定义的两常量干啥呢,其实就是mDecayConstant=0.05 mCutover=20
再看就很明了了,在统计前二十次,第一次取采样值,后续旧权重比例逐步的增加,当超过20次后,就权重比例就恒定为0.95,新采样的权重为0.05了。
其实之前DEFAULT_DECAY_CONSTANT注释上说的很明白了,这个值越小,刚启动时候,旧权重比例会更慢的累计,当超过阀值后,新采样的权重值越小,对网络波动可能就不敏感了。因此这个值,是需要好好权衡。
同时这个库只统计了下行,在一些特定业务需要,也可以对上行进行监控,甚至可以区分tcp ,udp等数据统计分析