1. 程式人生 > >Android 流量優化(一):模組化流量統計

Android 流量優化(一):模組化流量統計

有個使用者說一夜間跑了幾百兆的移動流量,流量是哪裡產生的?是不是哪裡有程式碼缺陷?怎樣定位到問題? 於是想著把流量劃分模組的統計下來,以便優化App的整體健康;
    如果僅統計應用總流量,類提供的介面就足以實現了;
    現在我們需要分析流量的成分,一直沒有發現比較好的方法。TrafficStats類的方法setThreadStatsTag(int)可以標記執行緒,那麼這個標記什麼時候使用呢?Android monitor裡面獲取的流量資料是包含有tag的,系統肯定將tag資訊儲存在了某個地方。後來發現Android系統將放流量資料資訊儲存在檔案/proc/net/xt_qtaguid/stats 中(見原始碼),於是我們可以通過下面的方式,分析流量來源!


模組劃分


     首先要對應用進行模組劃分,模組值Tag區間[0,0xFFFFFF00)。使用位表示模組與子模組之間的關係,例如0xGGPPCC00,GG一級模組、PP一級模組下的子模組,類推;
千牛是按照API介面劃分的,當然也可以按照業務場景劃分模組;


標記流量源

     使用TrafficStats.setThreadTag(int tag)方法標記的產生流量的執行緒,引數tag是上面模組相關的tag;@See TrafficStats
     NOTE:1、一個執行緒同時只會有一個有效的tag,並且不會傳遞,依賴包啟動了新執行緒訪問網路則需要依賴方提供標記支援;2、
native層socket、webview產生的流量無法使用此方法標記;
     標記流量後,可以在monitor網路介面檢視流量變化,不同的tag會繪製為不同的顏色,可以先在本地分析下,使用的時候網路請求是否有冗餘;<此處應有配圖>

獲取統計值


通過adb命令可以檢視流量內容 adb shell cat /proc/net/xt_qtaguid/stats :

android流量資料截圖

iface:網路性質[vlan-wifi流量 lo-本地流量 rmnet0-3g/2g流量 ...]

acct_tag_hex: 執行緒標記,也就是上面我們做的標記

uid_tag_int: 應用UID,據此判斷是否是某應用統計的流量資料

cnt_set: 應用前後臺標誌位:1-前臺,0-後臺

rx_bytes:

read 總接受位元組

tx_bytes:  transfer總髮送位元組

其中acct_tag_hex 為0x0的一行為某應用開機以來產生的總流量;
搞明白欄位意思瞭解析起來也就比較容易了,直接貼程式碼
NOTE:這裡統計的流量是開機之後所有的流量統計

try {
            FileReader stream = new FileReader(DEV_FILE);
            BufferedReader in = new BufferedReader(stream, 500);
            String line;
            String[] dataStrings;
            while ((line = in.readLine()) != null) {
                dataStrings = line.split(" ");
                if (StringUtils.equals(uid, dataStrings[3])) {
                    TrafficData dataTotal = new TrafficData();
                    TrafficData dataDiff = new TrafficData();

                    dataTotal.iface = dataDiff.iface = dataStrings[1];
                    dataTotal.moduleTag = dataDiff.moduleTag = dataStrings[2];
                    dataTotal.cnt_set = dataDiff.cnt_set = dataStrings[4];

                    dataTotal.rx_bytes = Integer.parseInt(dataStrings[5]);
                    dataTotal.tx_bytes = Integer.parseInt(dataStrings[6]);

                    //全量
                    if (dataTotal.rx_bytes + dataTotal.tx_bytes > 0) {
                        trafficDataList.add(dataTotal);

                        //需要根據儲存值計算增量
                        
                    }
                }
            }
            FileStoreProxy.setListValue(trafficDataList);
            in.close();
            stream.close();
        } catch (Exception e) {
        }