很多App要做到极致的话,对网络状态的监听是很有必要的,比如在网络差的时候加载质量一般的小图,缩略图,在网络好的时候,加载高清大图,脸书的android 客户端就是这么做的,

当然伟大的脸书也把这部分代码开源出来,今天就来带着大家分析一下脸书的这个开源代码。

GitHub 地址https://github.com/facebook/network-connection-class

注意这个项目下载下来以后 会报很多错误,导致很多人运行不了,大家要根据各自电脑不同的情况修改gradle脚本,才能让他运行。

如果有人对gradle不熟悉的话 可以下载我编写好的gradle脚本 直接放在你项目的根目录下即可。

地址在

http://pan.baidu.com/s/1pJuxesz

注意下载以后替换完成 把项目里为数不多的注解代码给删除即可正常运行,另外请自己查毒,中毒不要来找我~

下面来看下源码,其实大部分的代码还是很简单的,

首先从MainActivity 代码来看

  /**
* AsyncTask for handling downloading and making calls to the timer.
*/
private class DownloadImage extends AsyncTask<String, Void, Void> { @Override
protected void onPreExecute() {
mDeviceBandwidthSampler.startSampling();
mRunningBar.setVisibility(View.VISIBLE);
} @Override
protected Void doInBackground(String... url) {
String imageURL = url[0];
try {
// Open a stream to download the image from our URL.
InputStream input = new URL(imageURL).openStream();
try {
byte[] buffer = new byte[1024]; // Do some busy waiting while the stream is open.
while (input.read(buffer) != -1) {
}
} finally {
input.close();
}
} catch (IOException e) {
Log.e(TAG, "Error while downloading image.");
}
return null;
} @Override
protected void onPostExecute(Void v) {
mDeviceBandwidthSampler.stopSampling();
// Retry for up to 10 times until we find a ConnectionClass.
if (mConnectionClass == ConnectionQuality.UNKNOWN && mTries < 10) {
mTries++;
new DownloadImage().execute(mURL);
}
if (!mDeviceBandwidthSampler.isSampling()) {
mRunningBar.setVisibility(View.GONE);
}
}
}

主要就是通过上面的 这个task 来访问一个网络上的图片,然后计算 “瞬时“流量。

然后到DeviceBandwidthSampler来看这个方法

  /**
* Method call to start sampling for download bandwidth.
*/
public void startSampling() {
if (mSamplingCounter.getAndIncrement() == 0) {
mHandler.sendEmptyMessage(SamplingHandler.MSG_START);
long lastTimeReading = SystemClock.elapsedRealtime();
mLastTimeReading = lastTimeReading;
}
}

这个lastTimeReading 实际上就代表这一次请求发起的时间,大家可以看到这里去了Samplinghandler,注意他是子线程的消息队列~

 private class SamplingHandler extends Handler {
static final int MSG_START = 1;
static final int MSG_STOP = 2; public SamplingHandler(Looper looper) {
super(looper);
} @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_START:
addSample();
sendEmptyMessageDelayed(MSG_START, SAMPLE_TIME);
break;
case MSG_STOP:
addFinalSample();
removeMessages(MSG_START);
break;
default:
throw new IllegalArgumentException("Unknown what=" + msg.what);
}
}

看11-15行,实际上就是不断的在调用add Sample这个函数,当我们那个DownloadTask 任务完成以后 就会发MSG_STOP消息。

然后addFinalSample 这个过程就完成了(即代表本次对网络状态的监听完成)。

好,我们就来看看这个add Sample做了什么

 /**
* Method for polling for the change in total bytes since last update and
* adding it to the BandwidthManager.
*/
private void addSample() {
long byteDiff = QTagParser.getInstance().parseDataUsageForUidAndTag(Process.myUid());
synchronized (this) {
long curTimeReading = SystemClock.elapsedRealtime();
if (byteDiff != -1) {
mConnectionClassManager.addBandwidth(byteDiff, curTimeReading - mLastTimeReading);
}
mLastTimeReading = curTimeReading;
}
}

这边代码也很好理解,实际上第六航,byteDiff 就是代表 获取了多少流量,单位是byte。

然后7-12行 就是用这个byteDiff 和你算出来的时间差,这2个值,来算你的瞬时网络状态。

第10行 就是算这个网络状态的,第10行的代码 我不准备深入分析,因为这个是FACEBOOK对网络状态的定义,

大家在使用的时候 并不一定要按照他的来,比如他认为100k/s是网络糟糕,但是我们4g网络这么烂,可能50kb 就认为网络很好了。所以这个地方代码

以后有时间大家要根据自己的公司业务来调整。我们主要要看byteDiff 这个值是怎么算出来的,实际上这个才是代码的精髓。

 /**
* Reads the qtaguid file and returns a difference from the previous read.
*
* @param uid The target uid to read bytes downloaded for.
* @return The difference between the current number of bytes downloaded and
*/
public long parseDataUsageForUidAndTag(int uid) {
// The format of each line is
// idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes
// (There are many more fields but we are not interested in them)
// For us parts: 1, 2, 3 are to see if the line is relevant
// and part 5 is the received bytes
// (part numbers start from 0) // Permit disk reads here, as /proc/net/xt_qtaguid/stats isn't really "on
// disk" and should be fast.
StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
try {
long tagRxBytes = 0; FileInputStream fis = new FileInputStream(mPath);
//sStatsReader.setFileStream(fis);
// writeFileToSD(sStatsReader);
sStatsReader.setFileStream(fis);
byte[] buffer = sLineBuffer.get();
try {
int length;
sStatsReader.readLine(buffer);
sStatsReader.skipLine(); // skip first line (headers) int line = 2;
while ((length = sStatsReader.readLine(buffer)) != -1) {
try { // Content is arranged in terms of:
// idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes
// rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets
// tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets // The ones we're interested in are:
// idx - ignore
// interface, filter out local interface ("lo")
// tag - ignore
// uid_tag_int, match it with the UID of interest
// cnt_set - ignore
// rx_bytes
//Log.v("burning", "buffer==" + new String(buffer));
sScanner.reset(buffer, length);
sScanner.useDelimiter(' '); sScanner.skip();
if (sScanner.nextStringEquals("lo")) {
continue;
}
sScanner.skip();
if (sScanner.nextInt() != uid) {
continue;
}
sScanner.skip();
int rxBytes = sScanner.nextInt();
tagRxBytes += rxBytes;
line++; // If the line is incorrectly formatted, ignore the line.
} catch (NumberFormatException e) {
Log.e(TAG, "Cannot parse byte count at line" + line + ".");
continue;
} catch (NoSuchElementException e) {
Log.e(TAG, "Invalid number of tokens on line " + line + ".");
continue;
}
}
} finally {
fis.close();
} if (sPreviousBytes == -1) {
sPreviousBytes = tagRxBytes;
return -1;
}
long diff = tagRxBytes - sPreviousBytes;
sPreviousBytes = tagRxBytes;
return diff; } catch (IOException e) {
Log.e(TAG, "Error reading from /proc/net/xt_qtaguid/stats. Please check if this file exists.");
} finally {
StrictMode.setThreadPolicy(savedPolicy);
} // Return -1 upon error.
return -1;
}

17行-21行,实际上就是对/proc/net/xt_qtaguid/stats 这个文件进行读取,然后分析他的内容 来算出来我们的bytediff的,

熟悉linux的同学 尤其是运维的同学 肯定对/proc/net 不陌生,后面的xt_qtaguid/stats这个路径实际上就是谷歌android

给我们特殊的一个监听网络状态的接口文件。我这里可以给出一部分日志的结构 供大家参考。因为这个函数的内容就是对

这个日志的解析,你能读懂这个日志 就读懂了 这个开源项目最核心的部分。

idx哪一行就代表着文件头,下面对应的就是数据。

可以看到iface就代表着 那个网络接口,因为我是wifi下运行的这个代码 所以肯定是wlan,acct_tag_hex 这个标记就代表socket  当然这些参数值都不重要。

uid_tag_int 这个值 是比较重要的,很多人不明白为什么这个函数一定要先判断下uid的值,大家在这里一定要注意,对于linux系统来说 uid 就代表用户。

而android 是单用户系统,谷歌把这个地方改造成了uid代表你的app!!!!!!!!!!!!!!所以我们在监听app网络状态的时候 你一定要判断uid

你不能把别的app的流量也算在你自己的头上!

然后看cnt_set 实际上着就是一个标志位 0代表前台流量 1代表后台流量罢了。然后看rx_bytes r就代表是receive tx_bytes就代表transmit所以

就代表着 一个是收到的byte 一个是发送的byte,对于手机来说 发送的byte一般较少,我们主要关心的就是收到的byte。

好,分析完毕以后 我们再接着看那个函数 就比较容易了。

第29行 就是跳过文件头,因为我们的目标是获取rx_bytes

56行 就是看如果不是自己的app 的流量 自然跳出 不会计算。

77-83行,就是算bytediff的,注意那个sPreviousBytes 这个就是存储你上一次的流量的,初始化的时候是-1

他是一个静态变量,

因为我们的日志里面 rx_bytes是存储的总流量!,所以这边计算的时候要用流量差 来表示bytediff。

到这 这个框架就分析完毕了,希望能带给大家一点启发,

最后有的人可能要问 为什么 那个while循环 要把rx_bytes 给加起来,因为大家要注意啊 一个app

可能有多个进程啊,我们要计算自己的app 或者某个app的 网络状态,肯定是要把他所有进程的

流量全算进去的!所以这个地方要不断遍历!此外就是如果你真的能读懂我的这篇博客的话,又恰好会python的

话,就可以用python和adb 来完成对你手机上app的流量测试了!很方便,再也不用装什么360来测试你app

的流量了!

人生苦短,何不用python!

Android 网络流量监听开源项目-ConnectionClass源码分析的更多相关文章

  1. Android 开源项目PhotoView源码分析

    https://github.com/chrisbanes/PhotoView/tree/master/library 这个就是项目地址,相信很多人都用过,我依然不去讲怎么使用.只讲他的原理和具体实现 ...

  2. go开源项目influxdb-relay源码分析(一)

    influxdb-relay项目地址: https://github.com/influxdata/influxdb-relay,主要作为负载均衡节点,写入多个influxdb节点,起到高可用效果. ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  5. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  6. 开源项目Telegram源码 Telegram for Android Source

    背景介绍 Telegram 是一款跨平台的即时通信软件,它的客户端是自由及开放源代码软件.用户可以相互交换加密与自毁消息,发送照片.影片等所有类型文件.官方提供手机版.桌面版和网页版等多种平台客户端. ...

  7. Python优秀开源项目Rich源码解析

    这篇文章对优秀的开源项目Rich的源码进行解析,OMG,盘他.为什么建议阅读源码,有两个原因,第一,单纯学语言很难在实践中灵活应用,通过阅读源码可以看到每个知识点的运用场景,印象会更深,以后写代码的时 ...

  8. Android事件传递机制详解及最新源码分析——ViewGroup篇

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...

  9. 开源MyBatisGenerator组件源码分析

    开源MyBatisGenerator组件源码分析 看源码前,先了解Generator能做什么? MyBatisGenerator是用来生成mybatis的Mapper接口和xml文件的工具,提供多种启 ...

随机推荐

  1. UVA 10341 二分搜索

    Solve the equation:p ∗ e−x + q ∗ sin(x) + r ∗ cos(x) + s ∗ tan(x) + t ∗ x2 + u = 0where 0 ≤ x ≤ 1.In ...

  2. JS加载时间线

    1.创建Document对象,开始解析web页面.解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中.这个阶段document.readyState = 'loading' ...

  3. redis命令参考

    http://doc.redisfans.com/ 进入redis命令行模式方式: 1.进入redis安装目录 2.运行redis-cli

  4. jqGrid中实现radiobutton的两种做法

    http://blog.sina.com.cn/s/blog_4f925fc30102e27j.html   jqGrid中实现radiobutton的两种做法 ------------------- ...

  5. jq的bind用法

    type,[data],function(eventObject)String,Object,Function type: 含有一个或多个事件类型的字符串,由空格分隔多个事件.比如"clic ...

  6. Bootstrap全屏

    1.由于bootstrap中的.containter是根据媒体查询定死了width,所以页面不会占满全屏,若要全屏,则最外面的div的class不能用container(或改用.container-f ...

  7. HTML Meta标签详解

    HTML Meta中添加X-UA-Compatible和IE=Edge,chrome=1有什么作用?主题 HTML X-UA-Compatible是自从IE8新加的一个设置,对于IE8以下的浏览器是不 ...

  8. WebSphere常用设置

    WebSphere常用设置 1.查看环境配置信息D:\Program Files\IBM\WebSphere\AppServer\profiles\AppSrv01\logs\AboutThisPro ...

  9. 如何在github上展示作品——为你的项目生成一个快速访问的网址如(DaisyWang88.github.io)

      (这里值针对Windos系统的,因为本人用的是Window系统,暂时没有条件在其他平台上测试)   1.创建命名为 <userName>.github.io的仓库.      这里的u ...

  10. vb.net 写入文件同步锁

    <SoapHeader("oHeader")> _ <WebMethod()> _ <ScriptMethod(ResponseFormat:=Res ...