2024年安卓最全Android 应用流量监控实践_监测调用接口占用多少流量(1),阿里面试 笔试题

结尾

最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。

首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)

泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

接下来是资料清单:(敲黑板!!!


1.数据结构和算法

2.设计模式

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

背景

得物Apm系统本身包含网络接口性能监控的能力,但接口监控主要关注的是接口的耗时、异常率等信息,没有流量消耗相关维度的统计信息,并且一部分流量消耗可能来自于音视频等其他特殊场景,在接口监控的盲区外。 为了了解用户目前使用App时的流量消耗情况,并支持分析用户是存在异常的流量使用情况,还需要从流量消耗的角度,进行相关监控功能的建设。本文主要介绍流量监控实现过程中的一些技术实现及平台能力。

流量消耗信息获取

xt_qtaguid模块

xt_qtaguid 模块 是android 内核3.0 引入的流量统计模块,在Android系统9以下的版本,可以通过直接读取 /proc/net/xt_qtaguid/stats 文件获取对应应用的 流量消耗信息。其内容如下

nimdanoob:/ $ cat proc/net/xt_qtaguid/stats | grep 10027
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
10 rmnet_data0 0x0 10027 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
11 rmnet_data0 0x0 10027 1 451662 2320 1193948 3888 451662 2320 0 0 0 0 1193948 3888 0 0 0 0
40 wlan0 0x0 10027 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
41 wlan0 0x0 10027 1 38829 166 77083 208 38829 166 0 0 0 0 77083 208 0 0 0 0

在结果中,根据 uid_tag_int 可以区分不同的应用程序,这个值对应代码中 android.os.Process.myUid()�获取的值。 第二列的 iface 表示所使用的网络接口,比如 wlan 表示Wi-Fi网络适配器的物理接口, rmnet_data 表示手机数据网络接口, l0表示本地回环接口 。 通过读取 rx_bytes、tx_bytes 就可以获取不同网络接口累计使用的上下行流量大小。

除了通过直接读取 xt_qtaguid/stats文件的方式,也可以直接通过调用系统封装TrafficStats类提供的相关Api获取流量消耗信息,该类在 Android API8 版本开始就被引入了。 [图片上传失败…(image-69eb39-1698138290833)]

相关Api 如下

  • getUidTxBytes(int uid) :返回指定uid的上行流量消耗
  • getUidRxBytes(int uid): 返回指定uid的下行流量
  • getMobileTxBytes(): 返回整机的上行流量消耗
  • getMobileRxBytes(): 返回整机下行流量消耗
TrafficStats的限制

在Android 9 版本开始,google 逐步取消了对 xt_qtaguid模块的支持,开始采用基于 eBPF的网络流量监控实现 ,所以在高版本设备中无法再通过读取xt_qtaguid文件获取流量使用信息。 而直接使用TrafficStats读取流量消耗信息又由于其提供的Api有限,存在一些限制,比如TrafficStats 虽然支持查看指定程序的整体流量消耗信息,但不支持根据iface端口进行区分,因而无法明确wifi和移动分别消耗了多少流量。 在一些开源项目的实现中 通过ConnectivityManager 获取当前网络连接状态,判断采样间隔内流量消耗来自于wifi还是移动。但是实际上在采样间隔内,设备可能同时在使用wifi和移动进行进行网络通信,因此这种方式采集的数据精准度有限。

另外采用基于TrafficStats Api的方案在接入得物后,还发现TrafficStats统计的流量消耗比实际的流量消耗大了非常多(实际流量消耗的5倍),最后定位发现是由于得物的音视频场景 使用了本地Socket提供媒体数据,而TrafficStats.getUidTxBytes接口包含了所有iface的数据,本地Socket的通信数据也会被包含在内。

NetworkStatsManager

在Android6.0 (API 23)版中,系统新增了NetworkStatsManager类查询网络历史使用信息,其提供的Api 能力相比TrafficStats 强大很多,比如其可以获取指定时间内的流量消耗,并且 通过指定networkType 可以区分流量消耗的类型。

NetworkStatsManager statsManager = (NetworkStatsManager) getSystemService(Context.NETWORK_STATS_SERVICE);
//查询自身进程 蜂窝网络消耗
statsManager.querySummary(ConnectivityManager.TYPE_MOBILE, "", startTime, endTime);
//查询整机 蜂窝网络消耗
statsManager.querySummaryForDevice(ConnectivityManager.TYPE_MOBILE, null, startTime, endTime);

然而,在实际的测试过程中,以10s为采样间隔为例,经常会发现采样间隔内统计的数值未发生变化,在测试TrafficStats相关Api时发现没有类似的限制,其返回的数值是是实时的。

通过跟踪 NetworkStatsService 相关的实现后发现,原因可能是2个方面的问题导致的。 当应用层通过本地NetworkStatsManager调用相应Api获取消耗大小时,会创建一个NetworkStats对象,而NetworkStats对象构造时,会创建一个本地的 INetworkStatsSession对象

首先需要了解,NetworkStatsService 主要提供的是历史流量消耗记录的能力,它的计算方式是周期性的读取ebpf相应的虚拟文件中,从中获得最新的流量消耗大小。 NetworkStatsService为了尽量保证应用层获取的是最新的数据在每次openSession时,会尝试进行一次poll,不过这里的poll动作触发存在一定限制,15秒内只能触发一次。

另一个原因是,其底层实际负责流量记录的 NetworkStatsRecorder 会先将变化的流量信息统计在一个mPending对象中,当pending 记录超出一定阈值后,才会将这个数据落盘,而这个阈值默认情况下为2MB。

为了验证这个问题,保证每次采样时获取到的wifi及移动消耗数据是最新的,我们可以创建一组LocalSocket进行本地的端对端通信,在每次采样之前,进行一次数据数据通信,从而触发系统底层的数据合并操作,保证我们获取的数值是最新的。 当然这种方案会消耗不必要的系统资源,包括Socket资源的浪费并且会导致触发系统流量统计服务的数据合并及持久化操作,从节省系统资源的角度并不是一个好的方式,如果对数据采集结果的失效及准确性要求不高的话,不建议进行此类行为。

跟踪流量消耗明细

当发现用户流量消耗不合理时,我们还需要提供流量消耗的明细。比如接口的流量消耗、图片、音视频的流量消耗、直连Socket的流量消耗。

网络库Hook

大部分的网络请求发起依赖于现成的网络框架如 OkHttp、HttpURLConnection,因此需要Hook这些网络库追踪具体的流量消耗信息。

OkHttp

针对OkHttp库,OkHttp本身提供了EventListener机制,通过注册EventListner可以监听获取每个请求的阶段过程,以及请求响应信息。 因此这里不做过多阐述。

HttpURLConnection

针对HttpURLConnection进行的网络请求,这里参考的是AndroidStudio中网络监控的方案,具体代码详见 aosp ,这里简单做下原理阐述。 首先需要字节码插桩代理 URLConnection的 openConnection函数,代理原始URL.openConnection返回的URLConnection对象。这里需要注意由于Https 和Http协议会返回不同的URLConnection类型实例,因此需要做下判断。

以HttpURLConnectionWrapper实现为例,具体跟踪代码

其内部会再创建一个专门负责流量统计的TrackHttpURLConnection实例,这里又包装了一层是因为 该类的实现 可以在HttpsURLConnection的代理中进行复用。 继续跟踪TrackedHttpURLConnection� 实现。 针对请求体、响应体流量的跟踪,其主要实现是返回一个代理包装类型的InputStream或OutputStream

以OutputStream为例,继续代理内部的write函数,当write函数被调用时记录相应的流量消耗。

在整个对HttpURLConnection Hook实现的过程中有一些需要额外注意的点。 拿到URLConnection对象后,业务层会调用connect触发实际的网络请求调用,我们在connect函数中可以进行请求头和请求体流量的记录,但实际上业务层不主动调用connect函数,比如直接调用getInputStream()或getOutputStream时 底层也会触发connect操作,但此时它直接触发的是被代理的原始对象的connect函数,而我们自身的connect函数是不会被触发的,因此如果只在connect函数中记录信息,就可能出现遗漏的情况。 因此AS在实现这块逻辑时,会包装一个trackPreConnect�()函数,并同时在这3处进行调度。

类似的直接调用获取响应内容的一些函数,比如获取响应头信息也会触发底层的conenct操作,因此其也封装了一个trackResponse函数,内部会调用conenct函数(如果判断没调用过的话),并记录响应头的一些信息

基于Socket Api Hook

在部分业务场景中,可能会存在使用Socket进行网络通信的情况,比如自研长连协议,此时需要能够实现直接对原生Java Socket层数据通信的监控。 这里介绍2中针对Java Socket通信的监控方式。

基于字节码替换

和URLConnection监控的方案类似,Socket的数据读写是通过其InputStream和OutputStream对象实现的,因此通过ASM全局替换Socket的getInputStream和getOutPutStream对象,对其数据读写进行监控即可。

自定义SocketImplFactory

在Java层, 创建Socket的方式是直接构造 java.net.Socket对象,我们先跟踪其构造函数的实现。

在构造函数中,会调用setImpl()函数,继续跟踪setImple()实现

setImpl()函数先判断 factory是否为空,如果不为空则 使用factory创建SocketImpl实例,否则直接通过 SocksSocketImple()构造函数创建。 观察整个 java.net.Socket类的代码可以发现,其有关Socket的实际操作其实都是转发到 内部的SocksSocketImple对象中,以getInputStream()为例,在java.net.Socket实现中只是简单做了下状态的判断,最后直接调用impl的getInputStream函数。

java.net.Socket 类的factory是一个静态常量,SocketImpleFactory接口的实现如下

而 SocketImple抽象类实现如下 [图片上传失败…(image-ec6ff0-1698138290833)]# Android 应用流量监控实践

因此可以通过设置自定义的SocketImpleFactory 返回一个包装过的SokcetImpl实例,从而对Socket进行监控。



# 最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

上面分享的**百度、腾讯、网易、字节跳动、阿里等公司2021年的高频面试题**,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含**知识脉络 + 诸多细节**,由于篇幅有限,上面只是以图片的形式给大家展示一部分。

**【Android思维脑图(技能树)】**

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

![](https://img-blog.csdnimg.cn/img_convert/fb464ecceee182524060d552b6e94bc3.webp?x-oss-process=image/format,png)

**【Android高级架构视频学习资源】**

**Android部分精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 26
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值