如何获取Android系统各应用月度等时段的移动及WiFi数据用量?
Hey there! I’ve tackled this exact problem before, so let’s walk through how to retrieve per-app mobile and WiFi data usage for a custom time window (like a full month) on Android. The key here is using Android’s NetworkStatsManager API (available from API 23 onwards), which lets you filter usage by time, network type, and app.
1. Required Permissions
You’ll need two critical permissions to make this work:
android.permission.INTERNET(a basic permission, often already included in most app manifests)android.permission.PACKAGE_USAGE_STATS(a special system permission that users have to enable manually via settings)
Add these to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" android:permissionGroup="android.permission-group.SYSTEM_TOOLS" android:protectionLevel="signature|preinstalled" />
Note on the Usage Stats Permission
This permission can’t be requested via the standard runtime permission prompt. You’ll need to redirect users to the system settings to enable it:
private fun checkUsageStatsPermission(): Boolean { val appOps = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager val mode = appOps.checkOpNoThrow( AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), packageName ) return mode == AppOpsManager.MODE_ALLOWED } private fun requestUsageStatsPermission() { startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)) }
2. Query Data Usage with NetworkStatsManager
Once you have the necessary permissions, you can use NetworkStatsManager to fetch data for specific network types and time ranges. Let’s break this down with code examples.
First, Define Your Time Range
For a monthly report, calculate the start and end timestamps of the current month:
// Get start of current month (midnight on the 1st) val calendar = Calendar.getInstance() calendar.set(Calendar.DAY_OF_MONTH, 1) calendar.set(Calendar.HOUR_OF_DAY, 0) calendar.set(Calendar.MINUTE, 0) calendar.set(Calendar.SECOND, 0) val startTimestamp = calendar.timeInMillis // Get end of current month (11:59 PM on the last day) calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH)) calendar.set(Calendar.HOUR_OF_DAY, 23) calendar.set(Calendar.MINUTE, 59) calendar.set(Calendar.SECOND, 59) val endTimestamp = calendar.timeInMillis
Fetch Data for All Apps
Create a function to retrieve usage data for a specific network type (mobile or WiFi):
@SuppressLint("MissingPermission") private fun getAppDataUsage(networkType: Int, start: Long, end: Long): Map<String, Pair<Long, Long>> { // Package name -> (Received bytes, Transmitted bytes) val usageMap = mutableMapOf<String, Pair<Long, Long>>() val networkStatsManager = getSystemService(Context.NETWORK_STATS_SERVICE) as NetworkStatsManager val packageManager = packageManager try { // Query all app usage for the specified network and time range val subscriberId = if (networkType == ConnectivityManager.TYPE_MOBILE_DATA) getSubscriberId() else "" val statsIterator = networkStatsManager.queryDetails( networkType, subscriberId, start, end ) while (statsIterator.hasNextBucket()) { val bucket = NetworkStats.Bucket() statsIterator.getNextBucket(bucket) val uid = bucket.uid val packageNames = packageManager.getPackagesForUid(uid) ?: continue val rxBytes = bucket.rxBytes val txBytes = bucket.txBytes // Aggregate usage for each package linked to the UID packageNames.forEach { packageName -> val currentUsage = usageMap[packageName] ?: Pair(0L, 0L) usageMap[packageName] = Pair(currentUsage.first + rxBytes, currentUsage.second + txBytes) } } } catch (e: SecurityException) { e.printStackTrace() // Handle permission error (remind user to enable usage stats access) } return usageMap } // Helper to get subscriber ID for mobile network (required for mobile data queries) private fun getSubscriberId(): String { val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { telephonyManager.subscriptionId.toString() } else { @Suppress("DEPRECATION") telephonyManager.subscriberId ?: "" } }
Network Type References
- Mobile Data: Use
ConnectivityManager.TYPE_MOBILE_DATA(API 29+; for older versions, useConnectivityManager.TYPE_MOBILE) - WiFi: Use
ConnectivityManager.TYPE_WIFI
3. Put It All Together
Here’s how to use the above functions to get monthly mobile and WiFi usage:
if (checkUsageStatsPermission()) { // Handle network type compatibility for mobile data val mobileNetworkType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ConnectivityManager.TYPE_MOBILE_DATA } else { ConnectivityManager.TYPE_MOBILE } // Fetch usage data val mobileUsage = getAppDataUsage(mobileNetworkType, startTimestamp, endTimestamp) val wifiUsage = getAppDataUsage(ConnectivityManager.TYPE_WIFI, startTimestamp, endTimestamp) // Example: Print top 5 apps by total mobile data usage mobileUsage.entries.sortedByDescending { it.value.first + it.value.second } .take(5) .forEach { entry -> val totalBytes = entry.value.first + entry.value.second val totalMB = totalBytes / (1024 * 1024) Log.d("DataUsage", "${entry.key}: ${totalMB}MB (Rx: ${entry.value.first/(1024*1024)}MB, Tx: ${entry.value.second/(1024*1024)}MB)") } } else { // Prompt user to enable usage stats access requestUsageStatsPermission() }
Important Notes
- API Compatibility:
NetworkStatsManageronly works on API 23+. For older Android versions, you’ll have to useTrafficStats, but it doesn’t support time-range filtering (only total usage since device boot). - Multi-SIM Devices: If the device has multiple SIM cards, you’ll need to iterate over active subscriptions to get subscriber IDs for each SIM.
- Unit Conversion: The API returns data in bytes—convert to KB/MB/GB by dividing by
1024(KB),1024*1024(MB), or1024*1024*1024(GB).
内容的提问来源于stack exchange,提问作者Haresh Solanki

