Android應用流量統計——NetworkStatsManager使用

來源:互聯網
上載者:User

標籤:流量   .text   down   develop   arainfo   name   exce   function   應用   

在沒有Root的情況下,Android應用流量統計在6.0之前一直沒有太好的辦法,官方雖然提供了TrafficStats,但其主要功能是裝置啟動以來流量的統計資訊,和時間資訊無法很好的配合。最近再看TrafficStats類時,發現說明中提到,為擷取更具魯棒性的網路曆史資料,建議使用NetworkStatsManager。

本文首先簡單對比下TrafficStats和NetworkStatsManager各自的限制和優缺點,然後詳細說明NetworkStatsManager的用法,並給出主要代碼。

TrafficStats

Android API8提供了android.net.TrafficStats類。
通過此類能擷取裝置重啟以來網路資訊,部分函數如下所示:

static long  getMobileRxBytes()  //擷取通過移動資料網路收到的位元組總數static long  getMobileTxBytes()  //通過移動資料網發送的總位元組數  static long  getTotalRxBytes()  //擷取裝置總的接收位元組數 static long  getTotalTxBytes()  //擷取裝置總的發送位元組數static long  getUidRxBytes(int uid)  //擷取指定uid的接收位元組數  static long  getUidTxBytes(int uid) //擷取指定uid的發送位元組數 

通過文檔及上述函數可以知道,TrafficStats能夠擷取裝置的資料流量和總的網路流量消耗(一般情況下也就得到Wi-Fi下的流量資訊);可以查詢uid對應的流量資訊,而uid可以通過應用的包名查詢到,因此能夠查詢某個應用的流量統計資訊(不考慮shareuid)。非常方便的是,它的使用不需要特別的許可權。另一方面它也一些限制:
(1)無法擷取應用的資料流量消耗
從文檔中僅能擷取到指定uid的流量,但無法區分不同網路類型下的消耗
間接方法是通過監聽網路切換,做好流量記錄(但是要保證你的應用一直存活,且一定準確接收到網路切換資訊),基本不可用。
(2)無法擷取某個時間段內的流量消耗
從API文檔中看,函數參數沒有與時間相關的資訊。而且重要的一點是,TrafficStats類中記錄的是裝置重啟以來的流量統計資訊。因為TrafficStats 類,底層還是讀取/proc/net/xt_qtaguid/stats 對內容進行解析,將得到對應的結果返回上層。

NetworkStatsManager

在Android 6.0(API23)中新增加的類,提供網路使用曆史統計資訊,同時特彆強調了可查詢指定時間間隔內的統計資訊。看看部分函數(非靜態):

//查詢指定網路類型在某時間間隔內的總的流量統計資訊NetworkStats.Bucket querySummaryForDevice(int networkType, String subscriberId, long startTime, long endTime) //查詢某uid在指定網路類型和時間間隔內的流量統計資訊NetworkStats queryDetailsForUid(int networkType, String subscriberId, long startTime, long endTime, int uid)  //查詢指定網路類型在某時間間隔內的詳細的流量統計資訊(包括每個uid)NetworkStats queryDetails(int networkType, String subscriberId, long startTime, long endTime) 

從上述函數和文檔看,NetworkStatsManager類克服了TrafficStats的查詢限制,而且統計資訊也不再是裝置重啟以來的資料。但它也有自己的限制和缺點。
(1)許可權限制
NetworkStatsManager的使用需要額外的許可權,”android.permission.PACKAGE_USAGE_STATS”是系統許可權,需要主動引導使用者開啟應用的“有權查看使用方式的應用”(使用記錄存取權限)許可權,後面會有程式碼範例。
(2)文檔不完善
不好說是文檔不全,還是我沒找對。首先文檔中沒有給出類的執行個體對象的構造方法,一開始還是反射擷取的,後來才發現可以通過擷取系統服務方式得到。另外queryDetailsForUid函數中設定的時間間隔不太有用,沒能及時的擷取流量統計資訊,而是有兩個小時的時間間隔。還好可以在querySummary函數中獲得。

程式碼範例

下面說說具體的使用和代碼,使用前必須明確的是這裡的統計資訊都是在網路層以上的資料。
1.使用權限設定
(1)AndroidManifest中添加許可權聲明

<uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions"/>

(2)代碼中主動引導使用者開啟許可權
這裡沒有說明READ_PHONE_STATE的主動擷取,大家根據自己的targetSdkVersion設定

    private boolean hasPermissionToReadNetworkStats() {        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {            return true;        }        final 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;        }        requestReadNetworkStats();        return false;    }    // 開啟“有權查看使用方式的應用”頁面    private void requestReadNetworkStats() {        Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);        startActivity(intent);    }

2.查看裝置和某應用的流量統計
(1)擷取NetworkStatsManager樣本對象

NetworkStatsManager networkStatsManager = (NetworkStatsManager) getSystemService(NETWORK_STATS_SERVICE);

(2)查詢裝置總的流量統計資訊

NetworkStats.Bucket bucket = null;// 擷取到目前為止裝置的Wi-Fi流量統計bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_WIFI, "", 0, System.currentTimeMillis());Log.i("Info", "Total: " + (bucket.getRxBytes() + bucket.getTxBytes()));

(3)查詢某應用(uid)的資料流量統計資訊

// 擷取subscriberIdTelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);String subId = tm.getSubscriberId();NetworkStats summaryStats;long summaryRx = 0;long summaryTx = 0;NetworkStats.Bucket summaryBucket = new NetworkStats.Bucket();long summaryTotal = 0;summaryStats = networkStatsManager.querySummary(ConnectivityManager.TYPE_MOBILE, subId, getTimesMonthmorning(), System.currentTimeMillis());do {    summaryStats.getNextBucket(summaryBucket);    int summaryUid = summaryBucket.getUid();    if (uid == summaryUid) {        summaryRx += summaryBucket.getRxBytes();        summaryTx += summaryBucket.getTxBytes();    }    Log.i(MainActivity.class.getSimpleName(), "uid:" + summaryBucket.getUid() + " rx:" + summaryBucket.getRxBytes() +" tx:" + summaryBucket.getTxBytes());    summaryTotal += summaryBucket.getRxBytes() + summaryBucket.getTxBytes();} while (summaryStats.hasNextBucket());

3.附贈實用函數
(1)應用程式套件名查uid

NetworkStats summaryStats;long summaryRx = 0;long summaryTx = 0;NetworkStats.Bucket summaryBucket = new NetworkStats.Bucket();long summaryTotal = 0;summaryStats = networkStatsManager.querySummary(ConnectivityManager.TYPE_MOBILE, subId, getTimesMonthmorning(), System.currentTimeMillis());do {    summaryStats.getNextBucket(summaryBucket);    int summaryUid = summaryBucket.getUid();    if (uid == summaryUid) {        summaryRx += summaryBucket.getRxBytes();        summaryTx += summaryBucket.getTxBytes();    }    Log.i(MainActivity.class.getSimpleName(), "uid:" + summaryBucket.getUid() + " rx:" + summaryBucket.getRxBytes() +" tx:" + summaryBucket.getTxBytes());    summaryTotal += summaryBucket.getRxBytes() + summaryBucket.getTxBytes();} while (summaryStats.hasNextBucket());

(2)獲得本月第一天0點時間

public static long getTimesMonthMorning() {        Calendar cal = Calendar.getInstance();        cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));        return cal.getTimeInMillis();    }

4.提示無許可權資訊

15:39:06.531 5276-5276/cn.arainfo.test.android.testapp1 E/AndroidRuntime: FATAL EXCEPTION: mainProcess: cn.arainfo.test.android.testapp1, PID: 5276java.lang.SecurityException: Network stats history of uid 10145 is forbidden for caller 10144    at android.os.Parcel.readException(Parcel.java:1665)    at android.os.Parcel.readException(Parcel.java:1618)    at android.net.INetworkStatsSession$Stub$Proxy.getHistoryIntervalForUid(INetworkStatsSession.java:425)    at android.app.usage.NetworkStats.startHistoryEnumeration(NetworkStats.java:433)    at android.app.usage.NetworkStatsManager.queryDetailsForUidTag(NetworkStatsManager.java:254)    at android.app.usage.NetworkStatsManager.queryDetailsForUid(NetworkStatsManager.java:219)
統計測試

(1)測試裝置
小米5S Plus Android 6.0 和 華為Mate9 Android 7.0裝置上實際測試
(2)流量差距
實際測試流量有30M左右,和電訊廠商流量統計相差2M左右

轉載請註明出處:http://blog.csdn.net/w7849516230,歡迎關注公眾號“編程陽光”

參考連結:
1.android.net.TrafficStats
2.android.app.usage.NetworkStatsManager
3.stack overflow裡的兩個問題
http://stackoverflow.com/questions/38783836/how-to-fetch-data-usage-report-for-all-uids-using-the-new-networkstatsmanager-c

http://stackoverflow.com/questions/36702621/getting-mobile-data-usage-history-using-networkstatsmanager

Android應用流量統計——NetworkStatsManager使用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.