You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何监控其他应用网络使用?非root下解决TrafficStats.getUidRxBytes返回-1问题

无Root权限下获取其他应用网络使用情况的解决方案

嘿,我之前也碰到过一模一样的问题——TrafficStats.getUidRxBytes()在自己应用里正常,但拿其他应用数据就返回-1,这其实是Android从API 29(Android 10)开始收紧了权限限制导致的。下面分版本给你说无Root情况下的可行方案:

1. Android 9(API 28)及以下版本

这个阶段系统限制还比较松,只要你申请了ACCESS_NETWORK_STATE权限,就能正常通过UID获取其他应用的流量数据:

  • 首先在AndroidManifest.xml里声明权限:
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
  • 然后动态申请(针对Android 6.0+),之后就可以调用方法:
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_NETWORK_STATE)
            == PackageManager.PERMISSION_GRANTED) {
        // 先通过包名获取目标应用的UID
        try {
            int targetUid = getPackageManager().getApplicationInfo("com.target.app", 0).uid;
            long rxBytes = TrafficStats.getUidRxBytes(targetUid);
            if (rxBytes != TrafficStats.UNSUPPORTED) {
                // 处理获取到的接收流量数据
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    注意:部分定制ROM可能在API 28以下也有额外限制,但原生系统基本没问题。

2. Android 10-12(API 29-32)

这几个版本系统收紧了权限,普通应用直接用TrafficStats拿其他应用数据会返回-1,但有两个可行路径:

路径一:申请PACKAGE_USAGE_STATS权限

这个权限需要用户手动在系统设置里授予(一般路径:设置 > 应用 > 你的应用 > 权限 > 使用情况访问权限)。拿到权限后,推荐用NetworkStatsManager替代TrafficStats,因为它能更稳定地获取其他应用的流量数据:

// 先检查是否拥有PACKAGE_USAGE_STATS权限
AppOpsManager appOps = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
        android.os.Process.myUid(), getPackageName());
boolean hasUsagePermission = mode == AppOpsManager.MODE_ALLOWED;

if (hasUsagePermission) {
    NetworkStatsManager networkStatsManager = 
        (NetworkStatsManager) getSystemService(Context.NETWORK_STATS_SERVICE);
    try {
        int targetUid = getPackageManager().getApplicationInfo("com.target.app", 0).uid;
        // 获取目标UID的移动网络接收流量(时间范围从0到当前时间)
        NetworkStats networkStats = networkStatsManager.queryDetailsForUid(
                ConnectivityManager.TYPE_MOBILE,
                null, // null表示所有移动网络
                0, System.currentTimeMillis(),
                targetUid);
        
        long totalRxBytes = 0;
        while (networkStats.hasNextBucket()) {
            NetworkStats.Bucket bucket = new NetworkStats.Bucket();
            networkStats.getNextBucket(bucket);
            totalRxBytes += bucket.getRxBytes();
        }
        // 这里totalRxBytes就是目标应用的移动网络总接收流量
    } catch (PackageManager.NameNotFoundException | RemoteException e) {
        e.printStackTrace();
    }
}

如果要获取WiFi流量,把ConnectivityManager.TYPE_MOBILE换成ConnectivityManager.TYPE_WIFI即可。

路径二:成为设备/配置文件所有者应用

这个方案主要面向企业级应用,需要通过MDM(移动设备管理)配置来设置,普通消费级APP基本用不上,就不多展开了。

3. Android 13+(API 33及以上)

这个版本进一步收紧了权限,哪怕你拿到了PACKAGE_USAGE_STATS,也无法获取其他应用的详细流量数据——普通消费级APP只能获取自己的流量统计。只有系统应用或者满足特殊企业级场景的应用才能拿到其他应用的数据。

额外注意事项

  • 部分定制ROM(比如小米、华为)可能有自己的权限管控逻辑,即使你按步骤申请了权限,可能还是需要引导用户在厂商专属的权限管理页面额外开启相关权限;
  • 获取目标应用的UID时,要处理PackageManager.NameNotFoundException,避免目标应用未安装导致崩溃。

内容的提问来源于stack exchange,提问作者amirreza sh

火山引擎 最新活动