关于Android获取流量计数TrafficStats.getUidRxBytes(uid)和TrafficStats.getUidTxBytes(uid)返回-1解决方案

在Android7.0设备上,一个流量监测项目遇到TrafficStats.getUidRxBytes()和getUidTxBytes()返回-1的难题。通过研究源码和咨询专家,了解到可以从'/proc/uid_stat/'目录读取流量信息来解决此问题。


最近一个关于流量的项目在Android7.0系统的手机上运行,一直获取不到流量的使用数据,查看源码然后发现TrafficStats.getUidRxBytes(uid)和TrafficStats.getUidTxBytes(uid)一直都是返回的-1,

// 获取某个网络UID接收和发送字节的总和
long total = TrafficStats.getUidRxBytes(uid)
        + TrafficStats.getUidTxBytes(uid);

这就纳闷了!!!然后多方查找资料,问了公司的大神,给我说可以在“/proc/uid_stat/"文件夹中读取数据。后来总算是出来了。。。 不说了 贴代码

if (total == 0 || (TrafficStats.getUidRxBytes(uid) == -1) && (TrafficStats.getUidTxBytes(uid) == -1)) {
    total = getTotalBytesManual(uid);
}

/**
     * 通过u
<think>我们正在讨论Android中流量统计的两种方式:`TrafficStats.getTotalTxBytes()``NetworkStatsManager`。用户的问题集中在它们之间的区别上。根据提供的引用之前的讨论,我们可以总结如下:1.**TrafficStats类**:- `TrafficStats.getTotalTxBytes()`:这是一个静态方法,用于获取设备自启动以来所有网络接口发送的总字节(即总上传流量)。它返回的是整个设备的流量,不区分应用或用户。-另外,`TrafficStats`也提供了按UID(用户ID,每个应用有一个唯一的UID)统计的方法,如`TrafficStats.getUidRxBytes(uid)``TrafficStats.getUidTxBytes(uid)`。但是,根据引用[1]的注释,这些方法只能获取调用者自身应用的流量(即使用自己的UID),而不能获取其他应用的流量。这是因为权限限制。-引用[3]提到,`TrafficStats`的标签机制依赖于网络库对套接字的处理,并非所有网络库都支持,因此可能存在统计不全的情况。2.**NetworkStatsManager类**:-这是Android6.0(API23)引入的新API,提供了更细粒度的网络流量统计功能。-它允许查询历史网络统计据,包括按设备、按用户(所有UID)、按应用(指定UID)以及按时间范围查询。-引用[2]列举了`NetworkStatsManager`的一些重要方法,如:-`querySummaryForDevice`:查询指定网络模板在某个时间段内设备的总流量(不分应用)。- `querySummaryForUser`:查询指定网络模板在某个时间段内所有UID的总流量(即某个用户的所有应用)。- `queryDetails`:查询指定网络模板时间段内的详细网络使用情况,可以按UID过滤(但只能查询调用者所属用户的UID,即自己应用或同一用户的多个应用,需要权限)。-此外,`NetworkStatsManager`还支持注册回调来监听流量使用情况(当流量达到某个阈值时触发)。**主要区别总结**: |特性|TrafficStats |NetworkStatsManager ||------|--------------|---------------------|| **引入版本** |较早版本(API8) |Android6.0(API23)| |**统计范围** |设备总量(或当前应用)|支持设备、用户、应用级别,且可指定时间范围| |**历史据**|不支持,只能获取自设备启动以来的累计值|支持查询任意时间范围(需要系统存储支持) || **权限要求** |需要`READ_NETWORK_USAGE_HISTORY`(已废弃)|需要`PACKAGE_USAGE_STATS`(系统权限,用户需手动授权)或`READ_NETWORK_USAGE_HISTORY`(已废弃)| |**实时性**|实时性较差(有延迟)|可以查询实时据(取决于系统)| |**据持久化** |设备重启后据重置|可以查询重启前的据(系统会持久化存储) || **跨应用查询**|不能查询其他应用的据 |可以查询其他应用的据(需要权限)| **使用建议**:-如果需要快速获取设备总流量(上传或下载)或当前应用自身的流量,且不需要历史据,可以使用`TrafficStats`。 -如果需要更详细的统计,如按时间范围查询、查询其他应用流量(如流量监控应用)等,应使用`NetworkStatsManager`。**注意**:`NetworkStatsManager`的使用相对复杂,需要构造`NetworkTemplate`(指定网络类型,如移动网络、WiFi等)时间范围,并且需要处理权限问题。关于用户提到的引用[1]中的“坑”:注释指出要获取其他UID的统计据应使用`NetworkStatsManager`,而`TrafficStats`的按UID方法只能获取自身应用的流量。因此,如果应用需要获取其他应用的流量,必须使用`NetworkStatsManager`,并且需要用户授予`PACKAGE_USAGE_STATS`权限(这是一个系统权限,需要在设置中手动开启)。最后,关于设备重启后据丢失的问题:`TrafficStats`的据在重启后会重置,而`NetworkStatsManager`可以查询到重启前的据,因为系统会将网络使用情况持久化到据库中。**示例代码**:使用`NetworkStatsManager`查询设备总发送流量(需要权限处理):```java//需要Android6.0及以上NetworkStatsManager networkStatsManager= (NetworkStatsManager)context.getSystemService(Context.NETWORK_STATS_SERVICE);NetworkTemplate template= NetworkTemplate.buildTemplateMobileAll(subscriberId); //或buildTemplateWifi()longnow= System.currentTimeMillis();long start=now -30*24 *60*60 *1000;//过去30天NetworkStats.Bucketbucket =networkStatsManager.querySummaryForDevice(template,start,now);long totalTxBytes= bucket.getTxBytes();//发送字节``` 注意:`subscriberId`对于移动网络是必须的(如IMSI),可以通过`TelephonyManager`获取(需要权限)。对于WiFi,可以使用`NetworkTemplate.buildTemplateWifi()`。 以上内容综合了引用[1][2][3]的信息。</think>### Android `TrafficStats.getTotalTxBytes()` 与 `NetworkStatsManager` 区别详解 #### 一、核心区别对比 | 特性 | `TrafficStats.getTotalTxBytes()` | `NetworkStatsManager` | |------|---------------------------------|----------------------| | **API 级别** | API 8+ (Android 2.2+) | API 23+ (Android 6.0+) | | **统计范围** | 设备启动后**总发送流量** | 支持按**时间段/网络类型/UID**查询 | | **据精度** | 粗略统计(约5-10秒延迟) | 精确到毫秒级时间窗口 | | **权限要求** | `READ_NETWORK_USAGE_HISTORY` | `PACKAGE_USAGE_STATS`(系统权限) | | **历史据** | 不支持历史查询 | 支持任意时间段查询 | | **跨应用统计** | 无法获取其他应用据 | 可查询其他应用据(需权限) | | **设备重启影响** | 据重置归零 | 据持久化存储 | | **适用场景** | 快速获取当前总量 | 精细化流量分析监控 | #### 二、工作机制差异 1. **`TrafficStats` 实现原理** - 直接读取内核文件 `/proc/net/xt_qtaguid/stats`[^3] - 仅提供**瞬时快照值**(设备启动后累计值) - 示例代码: ```java // 获取总发送字节 long totalTx = TrafficStats.getTotalTxBytes(); ``` 2. **`NetworkStatsManager` 工作机制** - 通过系统服务访问持久化流量据库 - 支持复杂查询条件: ```java // 查询过去30天WiFi发送流量 NetworkTemplate template = NetworkTemplate.buildTemplateWifi(); long end = System.currentTimeMillis(); long start = end - TimeUnit.DAYS.toMillis(30); Bucket bucket = networkStatsManager.querySummaryForDevice(template, start, end); long wifiTxBytes = bucket.getTxBytes(); ``` - 支持回调监听(流量阈值告警): ```java networkStatsManager.registerUsageCallback( NetworkTemplate.buildTemplateMobileAll(subscriberId), 1024 * 1024, // 1MB阈值 new UsageCallback() { @Override public void onThresholdReached(int networkType, String subscriberId) { // 流量超限处理 } }); ``` #### 三、选择建议 1. **使用 `TrafficStats` 当:** - 需要快速获取当前应用自身流量 - 兼容低版本 Android 系统(<6.0) - 仅需粗略总量统计 2. **使用 `NetworkStatsManager` 当:** - 需要历史流量分析(如月度统计) - 监控其他应用流量(如家长控制应用) - 实现实时流量阈值告警 - 区分网络类型(WiFi/移动据)统计 #### 四、权限处理关键点 ```xml <!-- AndroidManifest.xml 权限声明 --> <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" /> ``` ```java // 运行时检查权限 if (!hasUsageStatsPermission()) { Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); startActivity(intent); // 引导用户开启权限 } private boolean hasUsageStatsPermission() { AppOpsManager appOps = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); int mode = appOps.checkOpNoThrow( AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), getPackageName() ); return mode == AppOpsManager.MODE_ALLOWED; } ``` > **注意**:跨应用流量统计需要用户手动在**设置→特殊应用权限→使用情况访问**中授权[^1]。 ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值