標籤:流量 .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使用