【问题标题】:How to get current network usage of app (or in total), even on Android N?即使在 Android N 上,如何获取应用程序(或全部)的当前网络使用情况?
【发布时间】:2016-09-04 06:50:46
【问题描述】:

正如标题所说。我想知道特定应用程序在特定时间每秒使用多少字节。

也许我可以使用“netstat”命令?但如果是这样,我如何将其过滤到特定的应用程序/进程?

我是否还需要获得一些权限才能这样做?

目前人们说要使用 TrafficStats.getUidRxBytes(packageInfo.uid) ,但是,从这里:https://developer.android.com/reference/android/net/TrafficStats.html#getUidRxBytes(int) ,它说 N 不支持它,我应该改用 NetworkStatsManager。有什么例子可以使用吗?

也许是一个合并的解决方案?

编辑:我尝试在 Android N 上使用 NetworkStatsManager,但失败了。我找不到任何关于如何使用它的示例,并且所有关于它的 stackOverflow 问题在不能很好地使用方面都是相似的。请,如果有人知道如何使用它,请写下来。

【问题讨论】:

标签: android networking


【解决方案1】:

NetworkStats的用法并不复杂。

设备 API 级别必须至少为 23。以下是开始使用 NetworkStatsManager 之前需要执行的一些步骤

  1. 在AndroidManifest.xml中声明所需的权限:

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission
        android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions"/>
    
  2. 在Activity请求许可

    android.permission.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());
    if (mode == AppOpsManager.MODE_ALLOWED) {
        return true;
    }
    

    要请求此权限,只需致电Intent:

    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
    startActivity(intent);
    

    还需要另一个权限:Manifest.permission.READ_PHONE_STATE。但是,这是正常的权限,所以可以是requested as any other permission

  3. 使用NetworkStatsManager:

    要获取它的参考,请调用:

    NetworkStatsManager networkStatsManager = (NetworkStatsManager) getApplicationContext().getSystemService(Context.NETWORK_STATS_SERVICE);
    

    从NetworkStatsManager 检索到的所有内容都打包到Buckets 中。这只是一个简单的 POJO,用于保存数据。

    全球:

    要获得 WiFi 的总体使用情况:

    NetworkStats.Bucket bucket;
    try {
        bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_WIFI,
                "",
                0,
                System.currentTimeMillis());
    } catch (RemoteException e) {
        return -1;
    }
    

    来自NetworkStats.Bucket,可以调用两个方法来获取使用情况(以Bps为单位):

    bucket.getRxBytes();
    bucket.getTxBytes();
    

    获取移动网络的数据比较困难。为了获得Bucket调用:

    public long getAllRxBytesMobile(Context context) {
        NetworkStats.Bucket bucket;
        try {
            bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_MOBILE,
                getSubscriberId(context, ConnectivityManager.TYPE_MOBILE),
                0,
                System.currentTimeMillis());
        } catch (RemoteException e) {
            return -1;
        }
        return bucket.getRxBytes();
    }
    
    //Here Manifest.permission.READ_PHONE_STATS is needed
    private String getSubscriberId(Context context, int networkType) {
        if (ConnectivityManager.TYPE_MOBILE == networkType) {
        TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            return tm.getSubscriberId();
        }
        return "";
    }
    

    应用:

    要获取特定应用的数据,请阅读queryDetailsForUID的文档。

    获取WiFi的包使用情况:

    NetworkStats networkStats = null;
    try {
        networkStats = networkStatsManager.queryDetailsForUid(
                ConnectivityManager.TYPE_WIFI,
                "",
                0,
                System.currentTimeMillis(),
                packageUid);
    } catch (RemoteException e) {
        return -1;
    }
    NetworkStats.Bucket bucket = new NetworkStats.Bucket();
    networkStats.getNextBucket(bucket);
    

    要获取 Mobile 的包使用情况:

    NetworkStats networkStats = null;
    try {
        networkStats = networkStatsManager.queryDetailsForUid(
                ConnectivityManager.TYPE_MOBILE,
                getSubscriberId(context, ConnectivityManager.TYPE_MOBILE),
                0,
                System.currentTimeMillis(),
                packageUid);
    } catch (RemoteException e) {
        return -1;
    }
    NetworkStats.Bucket bucket = new NetworkStats.Bucket();
    networkStats.getNextBucket(bucket);
    

    不幸的是,根据this piece of code,只有ConnectivityManager.TYPE_MOBILE和ConnectivityManager.TYPE_WIFI才能获得统计信息。

制作了一个示例Github repo 演示用法。

【讨论】:

  • 如果我想从所有类型的网络中获取网络使用情况怎么办? wifi和手机都可以吗?它在一面旗帜上,不是吗?另外,我认为你有一个错误:它不是“Bps”,它只是字节。你得到了自它开始以来的字节数......
  • 另外,您编写的代码不是关于应用程序的,而是全局的。我问的是一个应用的网络使用情况。
  • 我试图获得“以太网”,但每次我这样做时,它都会抱怨类似“java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Cannot create template for network type 9 ,subscriberId '425010...'。”
  • 要获得 Wifi 和 Mobile,只需将两个来源的数据相加即可。更新了答案以显示如何获取一个应用程序的数据以及为什么 Ethernet 无法获取数据。
  • 关于其他类型,我明白了。但是,如果我不能使用它们,为什么还存在这些常量呢?
【解决方案2】:

还有一种获取统计信息的复杂方法,但这应该适用于所有适配器:(但它很昂贵,因为您必须连续读取文件),

  1. 您可以在此处查看所有适配器以及发送/接收的数据:
cat /proc/net/dev
  1. 您可以从这些文件中获取每个适配器的统计信息:
cat /sys/class/net/**wlan0**/statistics/**rx**_bytes

wlan0 可以是你所有的适配器,但你需要在你的应用程序中保持这个值,因为如果你关闭/打开适配器,值将再次为 0。 (您可以有 RX 和 TX 值)

  1. 您可以获得活动的、当前正在发送/接收的应用列表
cat /proc/net/**tcp6**

这也可以通过 tcp、tcp6、upd、upd6

来完成

这里有“uid”列,它是已安装应用程序的 UID。 (所以该应用现在正在使用网络)

从这个文件中,您还可以看到连接端点。

  1. 检查每个应用程序发送/接收的字节数(如 TrafficStats API 调用)
cat /proc/uid_stat/**10348**/tcp_snd

这里的 id 是应用程序 UID。 你也有发送的:tcp_snd 和接收的:tcp_rcv 文件。

  1. 您还可以通过以下方式进行一些进程和 UID 匹配:
cat /proc/**17621**/status

这里的 id 是您可以从中获得的进程 id,例如top 命令。 在 status 文件中,您将拥有 UID,即已安装/系统应用的 UID。

+1。你也可以看看这个文件,TrafficStats API 使用这个:

cat /proc/net/xt_qtaguid/stats

标题:

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

价值观:

6 eth0 0x0 10005 0 0 0 80 2 0 0 0 0 0 0 80 2 0 0 0 0
7 eth0 0x0 10005 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
8 eth0 0x0 10007 0 11133 25 4751 24 11133 25 0 0 0 0 4751 24 0 0 0 0
9 eth0 0x0 10007 1 5965514 4358 102028 2327 5965514 4358 0 0 0 0 102028 2327 0 0 0 0
10 eth0 0x0 10014 0 95 1 40 1 95 1 0 0 0 0 40 1 0 0 0 0

因此,也许将第一个答案与这些知识相结合,您可以获得更多更好的统计数据。

【讨论】:

  • 我过去使用过这个解决方案(Android 2.1 - 2.3 - 4.4 次)。如果我没记错的话,不需要特定的权限。但也许在 Android 6-7 上发生了一些变化。
  • 我现在在 Android 6 设备上试了一下。所有提到的文件在没有任何许可的情况下都是可读的。例如FileInputStream fileInputStream = new FileInputStream("/proc/19534/status");
  • 在 Android 7 上问题与此类似:bugs.chromium.org/p/chromium/issues/detail?id=586021 /proc/**17621**/status 调用正常。我没有在 Android 5 上尝试过(但如果它在 2.1 到 6.0 上运行,我认为 5.0 没有问题...)试试吧;)
  • 你要解析哪个文件?
  • 我在评论中添加了一个新的 4. 段落。您必须阅读该文件,它只包含一个数字。 number = Long.parseLong(file.readLine());
【解决方案3】:

添加我的简化函数以通过 /proc/net/dev 获取整体网络使用情况。

不需要唯一的权限。

public static long[] getNetworkUsageKb() {
    BufferedReader reader;
    String line;
    String[] values;
    long[] totalBytes = new long[2];//rx,tx
    try {
        reader = new BufferedReader(new FileReader("/proc/net/dev"));

        while ((line = reader.readLine()) != null) {
            if (line.contains("eth") || line.contains("wlan")){
                values = line.trim().split("\\s+");
                totalBytes[0] +=Long.parseLong(values[1]);//rx
                totalBytes[1] +=Long.parseLong(values[9]);//tx
            }
        }
        reader.close();
    }
    catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    catch (IOException e) {
        e.printStackTrace();
    }

    //transfer to kb
    totalBytes[0] =  totalBytes[0] / 1024;
    totalBytes[1] =  totalBytes[1] / 1024;

    return totalBytes;
}

【讨论】:

  • 你究竟从中得到了什么?一段时间内下载和上传的总字节数?当前的下载/上传速度?特定应用也可以吗?
  • 其实我已经使用了 danesz 的建议,看起来这是自启动以来的总数。你可以在这里挖掘一些解释stackoverflow.com/questions/3521678/…。
  • 我明白了。但是是否有可能获得到目前为止(或从特定时间)以及特定应用程序的总数?
  • 我明白了。谢谢。
猜你喜欢
  • 2019-10-29
  • 1970-01-01
  • 1970-01-01
  • 2012-06-15
  • 2011-03-11
  • 2022-12-10
  • 1970-01-01
  • 2016-04-12
  • 2019-01-07
相关资源
最近更新 更多