1. 程式人生 > >Android流量統計

Android流量統計

進程 整體 logs 接受 發送 api alt net code

項目中需要對Android設備進行流量統計來進行資費結算,所以對Android設備流量統計進行了一些調研。發現流量統計主流上有兩種方式

  • 使用系統統計類TrafficStats獲取
  • 通過系統文件解析讀取

TrafficStats

  • static long getMobileRxBytes() //獲取通過Mobile連接收到的字節總數,不包含WiFi
  • static long getMobileRxPackets() //獲取Mobile連接收到的數據包總數
  • static long getMobileTxBytes() //Mobile發送的總字節數
  • static long getMobileTxPackets() //Mobile發送的總數據包數
  • static long getTotalRxBytes() //獲取總的接受字節數,包含Mobile和WiFi等
  • static long getTotalRxPackets() //總的接受數據包數,包含Mobile和WiFi等
  • static long getTotalTxBytes() //總的發送字節數,包含Mobile和WiFi等
  • static long getTotalTxPackets() //發送的總數據包數,包含Mobile和WiFi等
  • static long getUidRxBytes(int uid) //獲取某個網絡UID的接受字節數
  • static long getUidTxBytes(int uid) //獲取某個網絡UID的發送字節數

這些方法 獲取到的流量數據都是從手機上次開機到當前的流量使用情況,這個沒關系,我們可以監聽手機的開機和關機。關機的時候(或者每間隔一段時間) 將數據都存儲起來,下次開機後通過以上方法獲取到的數據在疊加上.

但是這些方法裏面,只能我發現在這些方法裏,缺少我最需要的那個方法,我想獲取某個app接受和發送的2G/3G流量,不包含wifi,這些API無法滿足該需求。

於是乎,便從系統文件的解析上著手吧

/proc/net/xt_qtaguid/stats格式

/proc/net/xt_qtaguid/stats內包含著各個app使用流量的情況,先來簡單看一下這個文件的內容

技術分享圖片

這輸出內容整體是一個表結構,首先我們需要看一下表中各列字段代表什麽

  • idx : 序號
  • iface : 代表流量類型(rmnet表示2G/3G, wlan表示Wifi流量,lo表示本地流量)
  • acct_tag_hex :線程標記(用於區分單個應用內不同模塊/線程的流量)
  • uid_tag_int : 應用uid,據此判斷是否是某應用統計的流量數據
  • cnt_set : 應用前後標誌位:1:前臺, 0:後臺
  • rx_btyes : receive bytes 接受到的字節數
  • rx_packets : 接收到的任務包數
  • tx_bytes : transmit bytes 發送的總字節數
  • tx_packets : 發送的總包數
  • rx_tcp_types : 接收到的tcp字節數
  • rx_tcp_packets : 接收到的tcp包數
  • rx_udp_bytes : 接收到的udp字節數
  • rx_udp_packets : 接收到的udp包數
  • rx_other_bytes : 接收到的其他類型字節數
  • rx_other_packets : 接收到的其他類型包數
  • tx_tcp_bytes : 發送的tcp字節數
  • tx_tcp_packets : 發送的tcp包數
  • tx_udp_bytes : 發送的udp字節數
  • tx_udp_packets : 發送的udp包數
  • tx_other_bytes : 發送的其他類型字節數
  • tx_other_packets : 發送的其他類型包數

可以看到,用文件解析的方式獲取流量可以獲得比直接使用acct_tag_hex要豐富的多的信息,包括區分WIFI和移動網絡,區分應用內不同模塊功能流量,前後臺流量等。

使用shell命令來查看進程流量

接下來說明下uid_tag_int這個字段

pid:Processid(進程id)
獲取pid的shell命令:

grep com.car.tencent

技術分享圖片

  • uid:每個應用程序一個uid(如果一個app擁有兩個進程,則有兩個不同pid,但兩個pid對應的uid是相同的)
    根據pid獲取uid

    adb shell cat /proc/1643/status
  • 根據uid獲取流量

    adb shell cat /proc/net/xt_qtaguid/stats | grep uid

如何使用代碼解析/proc/net/xt_qtaguid/stats文件

static const char *QTAGUID_UID_STATS = "/proc/net/xt_qtaguid/stats";

struct Stats {
    uint64_t rxBytes;
    uint64_t rxPackets;
    uint64_t txBytes;
    uint64_t txPackets;
    uint64_t tcpRxPackets;
    uint64_t tcpTxPackets;
};

static int parseUidStats(const uint32_t uid, struct Stats *stats) {
    FILE *fp = fopen(QTAGUID_UID_STATS, "r");
    if (fp == NULL) {
        return -1;
    }

    char buffer[384];
    char iface[32];
    uint32_t idx, cur_uid, set;
    uint64_t tag, rxBytes, rxPackets, txBytes, txPackets;

    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        if (sscanf(buffer,
                   "%" SCNu32 " %31s 0x%" SCNx64 " %u %u %" SCNu64 " %" SCNu64
                           " %" SCNu64 " %" SCNu64 "",
                   &idx, iface, &tag, &cur_uid, &set, &rxBytes, &rxPackets,
                   &txBytes, &txPackets) == 9) {
            //繞過本地回環地址
            if (strcmp(iface, "lo") && uid == cur_uid && tag == 0L) {
                stats->rxBytes += rxBytes;
                stats->rxPackets += rxPackets;
                stats->txBytes += txBytes;
                stats->txPackets += txPackets;
            }
        }
    }

    if (fclose(fp) != 0) {
        return -1;
    }
    return 0;
}

static jlong getUidStat(JNIEnv *env, jclass clazz, jint uid, jint type) {
    struct Stats stats;
    memset(&stats, 0, sizeof(Stats));
    if (parseUidStats(uid, &stats) == 0) {
        return getStatsType(&stats, (StatsType) type);
    } else {
        return UNKNOWN;
    }
}

這段代碼裏面,需要註意的點是如何忽略本地回環流量

if (strcmp(iface, "lo") && uid == cur_uid && tag == 0L) {

因為iface是網絡類型,一共只有三個類型,rmnet表示2G/3G, wlan表示Wifi流量,lo表示本地流量。所以這裏直接使用了字符串比對的方式。

擴展

  • /proc/net/xt_qtaguid/iface_stat_fmt:該文件中已經按照類型統計出了不同類型網絡數據的總數,因此獲取當前設備的總接受字節數等設備級統計信息,直接分析該文件,能最快獲取結果
  • /proc/net/xt_qtaguid/stats:該文件中則是按照uid進行了分類,適合於做更細力度(應用級/線程級)的分析

附錄:

  • 博客原地址:https://chchaooo.github.io/2018/06/13/Android%E6%B5%81%E9%87%8F%E7%BB%9F%E8%AE%A1/
  • 如果有問題交流請移步:https://www.cnblogs.com/weilf/p/9180373.html

參考資料:

  • Android流量優化(一):模塊化流量統計
  • Android性能測試之網絡流量

Android流量統計