最近逛github,突然发现facebook居然写了个检测网络质量的框架源码地址,好奇心驱使那就研究一下吧。分析源码之前,先看一看官方给我们提供的例子效果:
这个是faceBook官方为这个小框架提供的demo,点击Test按钮即可测出当前手机网络速度的质量,但是这里出现了一串英文EXCELLENT,什么鬼这表示的速度是什么,查询源码找到包含参数的这个枚举类
public enum ConnectionQuality {
/**
* 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
}
mConnectionClassManager = ConnectionClassManager.getInstance();
mDeviceBandwidthSampler = DeviceBandwidthSampler.getInstance();
.....
mListener = new ConnectionChangedListener();
@Override
protected void onPause() {
super.onPause();
mConnectionClassManager.remove(mListener);
}
@Override
protected void onResume() {
super.onResume();
mConnectionClassManager.register(mListener);
}
private DeviceBandwidthSampler(
ConnectionClassManager connectionClassManager) {
mConnectionClassManager = connectionClassManager;
mSamplingCounter = new AtomicInteger();
mThread = new HandlerThread("ParseThread");
mThread.start();
mHandler = new SamplingHandler(mThread.getLooper());
}
这个类的构造方法声明了一个HandlerThread,看到
HandlerThread,已经能看出来,它是想在在线程当中执行消息队列,start()方法创建一个新的消息队列管理,类Loop,然后进行轮询查看是否有消息,有消息就处理,接下来创建一个绑定这个
HandlerThread的Loop的Handler,用来发消息。按照猜测接下来就是利用Handler来循环传送消息了,在点击按钮下载图片的时候调用了 mDeviceBandwidthSampler.startSampling()方法发送了消息。
/**
* 开始检测网络速度
*/
public void startSampling() {
if (mSamplingCounter.getAndIncrement() == 0) {
//看这里
mHandler.startSamplingThread();
mLastTimeReading = SystemClock.elapsedRealtime();
}
}
public void startSamplingThread() {
sendEmptyMessage(SamplingHandler.MSG_START);
}
看这个方法链的调用,最终通过发消息的方式实现循环
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_START:
addSample();
sendEmptyMessageDelayed(MSG_START, SAMPLE_TIME);
protected void addSample() {
//得到手机自启动以来的总共消耗的流量
long newBytes = TrafficStats.getTotalRxBytes();
long byteDiff = newBytes - sPreviousBytes;
//有变化的时候才去计算
if (sPreviousBytes >= 0) {
synchronized (this) {
//得到当前的时间
long curTimeReading = SystemClock.elapsedRealtime();
//传入流量变化的差值和时间差值
mConnectionClassManager.addBandwidth(byteDiff, curTimeReading - mLastTimeReading);
mLastTimeReading = curTimeReading;
}
}
sPreviousBytes = newBytes;
}
public synchronized void addBandwidth(long bytes, long timeInMs) {
// Ignore garbage values.
// 变化量小于10计算终止重新记录值
if (timeInMs == 0
|| (bytes) * 1.0 / (timeInMs) * BYTES_TO_BITS < BANDWIDTH_LOWER_BOUND) {
return;
}
double bandwidth = (bytes) * 1.0 / (timeInMs) * BYTES_TO_BITS;
mDownloadBandwidth.addMeasurement(bandwidth);
// 如果它为true连续mSampleCounter+1
if (mInitiateStateChange) {
mSampleCounter += 1;
// 判断当前的值和期望的值是否处在同一个范围内,不处在同一个范围内,连续5次计算的值和波动值在同一个范围内的话,减少网络波动造成的误差
if (getCurrentBandwidthQuality() != mNextBandwidthConnectionQuality
.get()) {
// 计数重新开始,mInitiateStateChange
mInitiateStateChange = false;
mSampleCounter = 1;
}
/**
* 网络稳定到5次以上才通知计算结果,波动比较大的时候不通知,优化更准确
*/
if (mSampleCounter >= DEFAULT_SAMPLES_TO_QUALITY_CHANGE
&& significantlyOutsideCurrentBand()) {
mInitiateStateChange = false;
mSampleCounter = 1;
// 确定改变的时候设置通知
mCurrentBandwidthConnectionQuality
.set(mNextBandwidthConnectionQuality.get());
notifyListeners();
}
return;
}
// 如果上一个速率和现在的计算速率不一样,那么mInitiateStateChange=true,mNextBandwidthConnectionQuality储存当前的速录
if (mCurrentBandwidthConnectionQuality.get() != getCurrentBandwidthQuality()) {
mInitiateStateChange = true;
mNextBandwidthConnectionQuality = new AtomicReference(
getCurrentBandwidthQuality());
}
}
最后看一下指数移动平均值是怎么计算的
public void addMeasurement(double measurement) {
// 0.95
double keepConstant = 1 - mDecayConstant;
// 累加次数大于20次
if (mCount > mCutover) {
// 自然对数Math.log(mValue),Math.exp() 函数返回 ex
mValue = Math.exp(keepConstant * Math.log(mValue) + mDecayConstant
* Math.log(measurement));
}
// 0 0) {
// 如果mCount是4的话
double retained = keepConstant * mCount / (mCount + 1.0);
double newcomer = 1.0 - retained;
mValue = Math.exp(retained * Math.log(mValue) + newcomer
* Math.log(measurement));
}
// 第一次mCount是等于0的
else {
mValue = measurement;
}
mCount++;
}
指数平均值通过前后变化的两个数进行权重平均值计算,至于这样计算为什么准确,笔者暂时有点懵逼,但是这个算法是统计学当中的,不管怎么说阅读此代码让笔者了解到,想成为高手中的战斗机算法必须牛,那么接下来的目标就是拾起曾经遗忘的数学