# trafficDemo **Repository Path**: ddchub/traffic-demo ## Basic Information - **Project Name**: trafficDemo - **Description**: 移动流量监控 demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2024-01-06 - **Last Updated**: 2024-01-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # trafficDemo ## 介绍 移动流量监控 demo ## 先上完成效果 ![流量监控.gif](https://upload-images.jianshu.io/upload_images/12221638-ed627cb176dafe93.gif?imageMogr2/auto-orient/strip) ## 编译 环境 及工具 AS4.0 jdk1.8 测试的android系统 6.0 以上 在7.1安卓板 和 手机 android13 上测试了可以使用 。只写了移动流量的展示 ## NetworkStatsManager 的api 说明 ### 1. NetworkStatsManager类 网络数据管理类 Android 6.0后推出的 提供对网络使用历史和统计数据的访问,分为Summary queries(摘要查询)和 History queries(历史查询) 根据实际场景 个人建议尽量使用Summary queries ### 2.我使用的几个api如下 #### 2.1.API方法 ``` 查询网络使用统计摘要。(查询多个应用流量统计) 会返回多个桶 需要循环遍历 在根据uid 的到每一个的实时流量 querySummary(int networkType, String subscriberId, long startTime, long endTime) 查询网络使用统计摘要。结果是整个设备的汇总数据使用情况。结果是随着时间、状态、uid、标签、计量和漫游聚合的单个存储桶 querySummaryForDevice(int networkType, String subscriberId, long startTime, long endTime) ``` #### 2.2.API方法中的参数说明 ``` 1.networkType 查询网络类型 (ConnectivityManager.TYPE_WIFI,ConnectivityManager.TYPE_MOBILE) 2.subscriberId 设备唯一id(android 10及以后设备 获取不了,可不传) 3.startTime 查询指定时间段 开始时间戳 4.endTime 查询指定时间段 结束时间戳 5.uid 查询设备的Uid 这个我用的是在方法里判断了 ``` ### 3. api方法的使用 #### 3.1.querySummaryForDevice的使用 整个设备的汇总数据使用情况 ``` /** * 获取 所有移动使用流量信息 * * @param context 上下文 * @param isDayAndMonth 是否是当天还是当月 * @return 返回 当天 还是当月的流量信息 */ public TrafficBean getAllDay_MonthMobileInfo(Context context, boolean isDayAndMonth) { TrafficBean trafficBean = new TrafficBean(); NetworkStats.Bucket bucket; try { bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_MOBILE, getSubscriberId(context, ConnectivityManager.TYPE_MOBILE), isDayAndMonth ? getTimesmorning() : getTimesMonthmorning(), System.currentTimeMillis()); } catch (RemoteException e) { return trafficBean; } trafficBean.setRxBytes(bucket.getRxBytes()); trafficBean.setTxBytes(bucket.getTxBytes()); trafficBean.setTotalData(bucket.getTxBytes() + bucket.getRxBytes()); return trafficBean; } ``` #### 3.2.querySummary 的使用 (查询多个应用流量统计) 我使用这个是查询每一个应用的的实时流量情况 **记得使用过后 记得释放 网络统计信息 networkStats.close() 记得判空 我开始没有释放这个,报系统system 异常 但是不影响使用** ``` /** * 获取今日 或者今月的实时流量使用情况 * * @param context 上下文 * @param isDayAndMonth 是否是今天还是今月 * @return 获取今日 或者今月的流量使用情况 */ public TrafficBean getSummaryTrafficMobile(Context context, boolean isDayAndMonth) { TrafficBean trafficBean = new TrafficBean(); trafficBean.setUid(packageUid + ""); NetworkStats networkStats = null; try { networkStats = networkStatsManager.querySummary( ConnectivityManager.TYPE_MOBILE, getSubscriberId(context, ConnectivityManager.TYPE_MOBILE), isDayAndMonth ? getTimesmorning() : getTimesMonthmorning(), System.currentTimeMillis()); long mobileTraffic = 0;// long mobileRx = 0; long mobileTx = 0; NetworkStats.Bucket bucket = new NetworkStats.Bucket(); do { networkStats.getNextBucket(bucket); int summaryUid = bucket.getUid(); if (packageUid == summaryUid) { mobileTx += bucket.getTxBytes(); mobileRx += bucket.getRxBytes(); } } while (networkStats.hasNextBucket()); mobileTraffic = mobileRx + mobileTx; trafficBean.setTxBytes(mobileTx); trafficBean.setRxBytes(mobileRx); trafficBean.setTotalData(mobileTraffic); } catch (RemoteException e) { e.printStackTrace(); } finally { if (networkStats != null) { networkStats.close(); } } return trafficBean; } ``` #### 3.3完整代码NetworkStatsHelper ``` import android.annotation.SuppressLint; import android.app.usage.NetworkStats; import android.app.usage.NetworkStatsManager; import android.content.Context; import android.net.ConnectivityManager; import android.os.Build; import android.os.RemoteException; import android.telephony.TelephonyManager; import androidx.annotation.RequiresApi; import com.tjf.traffic.bean.TrafficBean; /** * @author: tjf * @date: 2022-12-10 * @desc: NetworkStatsHelper 流量查询辅助类 * *

使用方式 * * NetworkStatsManager networkStatsManager = (NetworkStatsManager) getSystemService(NETWORK_STATS_SERVICE); * * NetworkStatsHelper helper = new NetworkStatsHelper(networkStatsManager); *

*/ @RequiresApi(api = Build.VERSION_CODES.M) public class NetworkStatsHelper { NetworkStatsManager networkStatsManager; int packageUid; public void setPackageUid(int packageUid) { this.packageUid = packageUid; } public NetworkStatsHelper(NetworkStatsManager networkStatsManager) { this.networkStatsManager = networkStatsManager; } public NetworkStatsHelper(NetworkStatsManager networkStatsManager, int packageUid) { this.networkStatsManager = networkStatsManager; this.packageUid = packageUid; } /** * 获取 所有移动使用流量信息 * * @param context 上下文 * @param isDayAndMonth 是否是当天还是当月 * @return 返回 当天 还是当月的流量信息 */ public TrafficBean getAllDay_MonthMobileInfo(Context context, boolean isDayAndMonth) { TrafficBean trafficBean = new TrafficBean(); NetworkStats.Bucket bucket; try { bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_MOBILE, getSubscriberId(context, ConnectivityManager.TYPE_MOBILE), isDayAndMonth ? DateUtil.getTimesmorning() :DateUtil. getTimesMonthmorning(), System.currentTimeMillis()); } catch (RemoteException e) { return trafficBean; } trafficBean.setRxBytes(bucket.getRxBytes()); trafficBean.setTxBytes(bucket.getTxBytes()); trafficBean.setTotalData(bucket.getTxBytes() + bucket.getRxBytes()); return trafficBean; } /** * 获取所有应用一天使用的移动流量信息 * * @param context 上下文 * @param startTime 开始时间 * @return 流量信息 */ public TrafficBean getOneDayMobileInfo(Context context, long startTime) { TrafficBean trafficBean = new TrafficBean(); NetworkStats.Bucket bucket; try { bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_MOBILE, getSubscriberId(context, ConnectivityManager.TYPE_MOBILE), startTime, startTime + 86400000 ); trafficBean.setRxBytes(bucket.getRxBytes()); trafficBean.setTxBytes(bucket.getTxBytes()); trafficBean.setTotalData(bucket.getTxBytes() + bucket.getRxBytes()); } catch (RemoteException e) { return trafficBean; } return trafficBean; } /** * 获取今日 或者今月的 应用 的实时流量使用情况 * * @param context 上下文 * @param isDayAndMonth 是否是今天还是今月 * @return 获取今日 或者今月的流量使用情况 */ public TrafficBean getSummaryTrafficMobile(Context context, boolean isDayAndMonth) { TrafficBean trafficBean = new TrafficBean(); trafficBean.setUid(packageUid + ""); NetworkStats networkStats = null; try { networkStats = networkStatsManager.querySummary( ConnectivityManager.TYPE_MOBILE, getSubscriberId(context, ConnectivityManager.TYPE_MOBILE), isDayAndMonth ? DateUtil.getTimesmorning() :DateUtil. getTimesMonthmorning(), System.currentTimeMillis()); long mobileTraffic = 0;// long mobileRx = 0; long mobileTx = 0; NetworkStats.Bucket bucket = new NetworkStats.Bucket(); do { networkStats.getNextBucket(bucket); int summaryUid = bucket.getUid(); if (packageUid == summaryUid) { mobileTx += bucket.getTxBytes(); mobileRx += bucket.getRxBytes(); } } while (networkStats.hasNextBucket()); mobileTraffic = mobileRx + mobileTx; trafficBean.setTxBytes(mobileTx); trafficBean.setRxBytes(mobileRx); trafficBean.setTotalData(mobileTraffic); } catch (RemoteException e) { e.printStackTrace(); } finally { if (networkStats != null) { networkStats.close(); } } return trafficBean; } /** * 获取用户id android 10 以后获取不了 传null 即可 * 需要权限 * * * @param context 上下文 * @param networkType 网络类型 * @return null */ @SuppressLint("MissingPermission") private String getSubscriberId(Context context, int networkType) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { return null; } if (ConnectivityManager.TYPE_MOBILE == networkType) { TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); return tm.getSubscriberId(); } return null; } } ``` ### 4 测试demo的准备工作 #### 4.1 需要的相关权限及权限的申请 大家先在 AndroidManifest.xml 添加如下权限内 ``` ``` #### 4.2 权限的申请 ``` 1.获取使用情况统计权限 // 打开 "有权查看使用情况的应用"页面 private void requestReadNetworkStats() { Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // 经过测试,只有在 Android 10 及以上加包名才有效果 // 如果在 Android 10 以下加包名会导致无法跳转 intent.setData(Uri.parse("package:" + getPackageName())); } startActivity(intent); } private boolean hasPermissionToReadNetworkStats() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { Log.i("流量-", "==当前版本小于23=="); return true; } final AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE); int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, Process.myUid(), getPackageName()); if (mode == AppOpsManager.MODE_ALLOWED) { return true; } requestReadNetworkStats(); return false; } 2.读取应用列表权限 下面这俩个选择一个就可以 点击应用排行列表的时候需要手动授权 ``` #### 4.3 获取手机应用列表 下面选择一个使用即可 ``` 1.获取拥有internet权限的应用列表 /** * 获取拥有internet权限的应用列表 * * @return */ public List getInternetTrafficInfos() { List trafficInfos = new ArrayList(); //获取手机中安装的并且具有权限的应用 PackageManager.GET_UNINSTALLED_PACKAGES | List installedPackages = pm.getInstalledPackages( PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS); for (PackageInfo info : installedPackages) { //获取权限数组 String[] permissions = info.requestedPermissions; if (permissions != null && permissions.length > 0) { for (String permission : permissions) { if (permission.equals(Manifest.permission.INTERNET)) { ApplicationInfo applicationInfo = info.applicationInfo; Drawable icon = applicationInfo.loadIcon(pm); String appname = applicationInfo.loadLabel(pm).toString(); String packagename = applicationInfo.packageName; int uid = applicationInfo.uid; TrafficInfo trafficInfo = new TrafficInfo(icon, appname, packagename, uid); trafficInfos.add(trafficInfo); } } } } return trafficInfos; } 2.获取所有 的应用列表 /** * 获取所有 的应用列表 * * @return */ public List getInstalledApplications() { List trafficInfos = new ArrayList(); List installedPackages = pm.getInstalledApplications( 0); for (ApplicationInfo applicationInfo : installedPackages) { Drawable icon = applicationInfo.loadIcon(pm); String appname = applicationInfo.loadLabel(pm).toString(); String packagename = applicationInfo.packageName; int uid = applicationInfo.uid; TrafficInfo trafficInfo = new TrafficInfo(icon, appname, packagename, uid); trafficInfos.add(trafficInfo); } return trafficInfos; } ``` #### 4.4 Drawable转换成Bitmap 这个是展示App流量排行时 需要把获取到的icon 转成bitmap 进行显示 ``` /** * 将Drawable转成Bitmap * * @param drawable * @return */ public static Bitmap drawableToBitmap(Drawable drawable) { Bitmap bitmap; try { bitmap = Bitmap.createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); drawable.draw(canvas); } catch (Exception e) { bitmap = null; } return bitmap; } ``` #### 4.5 流量文本格式化工具类 ``` /** * @author: tjf * @desc: 流量文本格式化工具类 */ public class TrafficFormat { //定义TB的计算常量 private static final double TB = 1024f * 1024f * 1024f * 1024f; //定义GB的计算常量 private static final double GB = 1024f * 1024f * 1024f; //定义MB的计算常量 private static final double MB = 1024f * 1024f; //定义KB的计算常量 private static final double KB = 1024f; /** * 格式化数据 * * @param data * @return */ public static String formatByte(long data) { DecimalFormat format = new DecimalFormat("####.##"); if (data < KB) { return data + "B"; } else if (data < MB) { return format.format(data / KB) + "KB"; } else if (data < GB) { return format.format(data / MB) + "MB"; } else if (data < TB) { return format.format(data / GB) + "GB"; } else { return "超出统计范围"; } } } ``` #### 4.6 日期工具类 ``` import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; /** * @author: tjf * @desc: 日期操作工具类. */ public class DateUtil { /** * 格式到天 * * @param time * @return */ public static String getDay(long time) { return new SimpleDateFormat("yyyy-MM-dd").format(time); } /** * 获得当前时间的 * HH:mm:ss SS为零 * * @return */ public static Date firstDayOfMonthData() { Calendar cal = Calendar.getInstance(); cal.set(Calendar.DAY_OF_MONTH, 1); // M月置1 cal.set(Calendar.HOUR_OF_DAY, 0);// H置零 cal.set(Calendar.MINUTE, 0);// m置零 cal.set(Calendar.SECOND, 0);// s置零 cal.set(Calendar.MILLISECOND, 0);// S置零 return cal.getTime(); } /** * 获得当前月的第一天 *

* HH:mm:ss SS为零 * * */ public static long firstDayOfMonth(int DAY_OF_MONTH) { Calendar cal = Calendar.getInstance(); cal.set(Calendar.DAY_OF_MONTH, DAY_OF_MONTH); // M月置1 cal.set(Calendar.HOUR_OF_DAY, 0);// H置零 cal.set(Calendar.MINUTE, 0);// m置零 cal.set(Calendar.SECOND, 0);// s置零 cal.set(Calendar.MILLISECOND, 0);// S置零 return cal.getTimeInMillis(); } /** * 获得当前月的第一天到当前时间的时间戳集合 *

* HH:mm:ss SS为零 * * */ public static List getListDayOfMonth() { List dayOfMonth = new ArrayList<>(); long days = getDayDiff(firstDayOfMonthData(), now()); for (int i = 1; i <= days; i++) { long day = firstDayOfMonth(i); dayOfMonth.add(day); } Collections.sort(dayOfMonth, new Comparator() { @Override public int compare(Long o1, Long o2) { return (int) (o2 - o1); } }); return dayOfMonth; } /** * 获得天数差 * * @param begin 开始 * @param end 结束 * @return 天数 */ public static long getDayDiff(Date begin, Date end) { long day = 1; if (end.getTime() < begin.getTime()) { day = -1; } else if (end.getTime() == begin.getTime()) { day = 1; } else { day += (end.getTime() - begin.getTime()) / (24 * 60 * 60 * 1000); } return day; } } ``` ### 5.主App的结构展示及具体实现 #### 5.1 结构如下图所示 ![流量监控项目代码类图.png](https://upload-images.jianshu.io/upload_images/12221638-da40bd64cfab4f51.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) #### 5.2 功能的实现 ##### 5.2.1. TrafficActivity 流量监控首页 进行整个流量及每一天的总流量展示 ``` import android.app.AppOpsManager; import android.app.usage.NetworkStatsManager; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Process; import android.provider.Settings; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.tjf.traffic.adapter.TrafficDateAdapter; import com.tjf.traffic.bean.TrafficDayInfo; import com.tjf.traffic.utils.DateUtil; import com.tjf.traffic.utils.NetworkStatsHelper; import com.tjf.traffic.bean.TrafficBean; import com.tjf.traffic.utils.TrafficFormat; import java.util.ArrayList; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; /** * 流量监控首页 进行整个流量及每一天的总流量展示 */ public class TrafficActivity extends AppCompatActivity { @BindView(R.id.traffic_list_rev) RecyclerView trafficListRev; @BindView(R.id.traffic_month_all_tv) TextView trafficMonthAllTv; @BindView(R.id.traffic_day_all_tv) TextView trafficDayAllTv; @BindView(R.id.traffic_month_downall_tv) TextView trafficMonthDownallTv; @BindView(R.id.traffic_month_upall_tv) TextView trafficMonthUpallTv; @BindView(R.id.traffic_day_downall_tv) TextView trafficDayDownallTv; @BindView(R.id.traffic_day_upall_tv) TextView trafficDayUpallTv; @BindView(R.id.total_traffic_tv) TextView totalTrafficTv; @BindView(R.id.total_up_traffic_tv) TextView totalUpTrafficTv; @BindView(R.id.total_down_traffic_tv) TextView totalDownTrafficTv; @BindView(R.id.traffic_app_sort_lly) LinearLayout trafficAppSortLly; @BindView(R.id.traffic_goback) ImageView trafficGoback; TrafficDateAdapter trafficDateAdapter;//每天流量展示适配器 NetworkStatsManager networkStatsManager = null;//网络数据管理 NetworkStatsHelper helper = null;//网络数据助手 List dateTraficlist = new ArrayList<>();//每天的流量集合 @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_traffic); ButterKnife.bind(this); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { networkStatsManager = (NetworkStatsManager) getSystemService(NETWORK_STATS_SERVICE); } helper = new NetworkStatsHelper(networkStatsManager); trafficAppSortLly.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { checkUserAppListPerMission(); } }); trafficGoback.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); } @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onResume() { super.onResume(); if (!hasPermissionToReadNetworkStats()) { Log.i("流量-", "==有权查看使用情况的应用的权限未授权=="); return; } Log.i("流量-", "==有权查看使用情况的应用的权限授权成功=="); setTrafficList(); } @RequiresApi(api = Build.VERSION_CODES.M) public void setTrafficList() { TrafficBean monthTraffic = helper.getAllDay_MonthMobileInfo(this, false); TrafficBean dayTraffic = helper.getAllDay_MonthMobileInfo(this, true); trafficMonthAllTv.setText(String.format("当月已用总流量\n%s", TrafficFormat.formatByte( monthTraffic.getTotalData()))); trafficDayAllTv.setText(String.format("当天已用总流量\n%s", TrafficFormat.formatByte( dayTraffic.getTotalData()))); trafficMonthDownallTv.setText(String.format("当月下载流量\n%s", TrafficFormat.formatByte( monthTraffic.getRxBytes()))); trafficMonthUpallTv.setText(String.format("当月上传流量\n%s", TrafficFormat.formatByte( monthTraffic.getTxBytes()))); trafficDayDownallTv.setText(String.format("当天下载流量\n%s", TrafficFormat.formatByte( dayTraffic.getRxBytes()))); trafficDayUpallTv.setText(String.format("当天上传流量\n%s", TrafficFormat.formatByte( dayTraffic.getTxBytes()))); getDateAndMonthInfo(); trafficDateAdapter = new TrafficDateAdapter(this, dateTraficlist, R.layout.traffic_date_item); trafficListRev.setLayoutManager(new LinearLayoutManager(this)); trafficListRev.setAdapter(trafficDateAdapter); } public void startAppListPage() { Toast.makeText(this, "点击了应用排行", Toast.LENGTH_SHORT).show(); startActivity(new Intent(TrafficActivity.this, TrafficAppActivity.class)); } // 打开 "有权查看使用情况的应用"页面 private void requestReadNetworkStats() { Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // 经过测试,只有在 Android 10 及以上加包名才有效果 // 如果在 Android 10 以下加包名会导致无法跳转 intent.setData(Uri.parse("package:" + getPackageName())); } startActivity(intent); } /** * 检查是否有有权查看使用情况的应用的权限 * * @return true false */ private boolean hasPermissionToReadNetworkStats() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { Log.i("流量-", "==当前版本小于23=="); return true; } final AppOpsManager appOps = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, Process.myUid(), getPackageName()); if (mode == AppOpsManager.MODE_ALLOWED) { return true; } requestReadNetworkStats(); return false; } /** * 查询应用列表权限进行跳页 */ public void checkUserAppListPerMission() { startAppListPage(); } @RequiresApi(api = Build.VERSION_CODES.M) public void getDateAndMonthInfo() { dateTraficlist.clear(); long totalTx = 0; long totalRx = 0; long totalTraffic; List dateList = DateUtil.getListDayOfMonth(); for (Long datelong : dateList) { TrafficBean oneTraffic = helper.getOneDayMobileInfo(this, datelong); TrafficDayInfo usageDate = new TrafficDayInfo(); usageDate.setDateDay(datelong); usageDate.setDateDayStr(DateUtil.getDay(datelong)); usageDate.setTotalRxBytes(TrafficFormat.formatByte(oneTraffic.getRxBytes())); usageDate.setTotalTxBytes(TrafficFormat.formatByte(oneTraffic.getTxBytes())); usageDate.setTotalTraffic(TrafficFormat.formatByte(oneTraffic.getTotalData())); dateTraficlist.add(usageDate); totalTx += oneTraffic.getTxBytes(); totalRx += oneTraffic.getRxBytes(); } totalTraffic = totalRx + totalTx; setTotalTraffic(TrafficFormat.formatByte(totalTraffic), TrafficFormat.formatByte(totalTx), TrafficFormat.formatByte(totalRx)); Log.i("流量-", "==流量的汇总==\n" + TrafficFormat.formatByte(totalTraffic) + "\n上传流量合计:" + TrafficFormat.formatByte(totalTx) + "\n下载流量合计:" + TrafficFormat.formatByte(totalRx)); } /** * 流量合计 */ public void setTotalTraffic(String totalTraffic, String upTraffic, String downTraffic) { totalTrafficTv.setText(String.format("总流量:%s", totalTraffic)); totalUpTrafficTv.setText(String.format("上传总流量:%s", upTraffic)); totalDownTrafficTv.setText(String.format("下载总流量:%s", downTraffic)); } } ``` ##### 5.2.2. TrafficAppActivity 应用流量排行APP页 进行今日和今月列表切换 App流量的排行展示 ``` import android.app.usage.NetworkStatsManager; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.tjf.traffic.adapter.TrafficAppAdapter; import com.tjf.traffic.bean.TrafficInfo; import com.tjf.traffic.utils.NetworkStatsHelper; import com.tjf.traffic.bean.TrafficBean; import com.tjf.traffic.utils.TrafficFormat; import com.tjf.traffic.utils.TrafficManagerApp; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; /** * 应用流量排行APP页 进行今日和今月列表切换 App流量的排行展示 */ public class TrafficAppActivity extends AppCompatActivity { @BindView(R.id.traffic_list_rev) RecyclerView trafficListRev; @BindView(R.id.traffic_day_month_tv) TextView trafficDayMonthTv; @BindView(R.id.total_traffic_tv) TextView totalTrafficTv; @BindView(R.id.total_up_traffic_tv) TextView totalUpTrafficTv; @BindView(R.id.total_down_traffic_tv) TextView totalDownTrafficTv; @BindView(R.id.traffic_goback) ImageView trafficGoback; TrafficAppAdapter trafficAppAdapter; NetworkStatsManager networkStatsManager = null; NetworkStatsHelper helper; List trafficApplist = new ArrayList<>(); TrafficManagerApp trafficManagerApp; @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_traffic_app); ButterKnife.bind(this); trafficManagerApp = new TrafficManagerApp(this); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { networkStatsManager = (NetworkStatsManager) getSystemService(NETWORK_STATS_SERVICE); } helper = new NetworkStatsHelper(networkStatsManager); getDayAndMonthInfo(true); trafficAppAdapter = new TrafficAppAdapter(this, trafficApplist, R.layout.traffic_app_item); trafficListRev.setLayoutManager(new LinearLayoutManager(this)); trafficListRev.setAdapter(trafficAppAdapter); trafficDayMonthTv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (trafficDayMonthTv.getText().toString().equals("今日")) { trafficDayMonthTv.setText("今月"); getDayAndMonthInfo(false); } else if (trafficDayMonthTv.getText().toString().equals("今月")) { trafficDayMonthTv.setText("今日"); getDayAndMonthInfo(true); } Toast.makeText(TrafficAppActivity.this, "点击了" + trafficDayMonthTv.getText().toString(), Toast.LENGTH_SHORT).show(); if (trafficAppAdapter != null) { trafficAppAdapter.notifyDataSetChanged(); } } }); trafficGoback.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); } @RequiresApi(api = Build.VERSION_CODES.M) public void getDayAndMonthInfo(boolean isDayAndMonth) { trafficApplist.clear(); trafficApplist.addAll(trafficManagerApp.getInternetTrafficInfos()); long totalTx = 0; long totalRx = 0; long totalTraffic; for (TrafficInfo trafficInfo : trafficApplist) { helper.setPackageUid(trafficInfo.getUid()); TrafficBean trafficBean = helper.getSummaryTrafficMobile(this, isDayAndMonth); trafficInfo.setTraffic(TrafficFormat.formatByte(trafficBean.getTotalData())); trafficInfo.setMobleTotalData(trafficBean.getTotalData()); trafficInfo.setMobleRxBytes(trafficBean.getRxBytes()); trafficInfo.setMobleTxBytes(trafficBean.getTxBytes()); totalTx += trafficBean.getTxBytes(); totalRx += trafficBean.getRxBytes(); } totalTraffic = totalRx + totalTx; Log.i("流量-", "==流量的汇总==\n" + TrafficFormat.formatByte(totalTraffic) + "\n上传流量合计:" + TrafficFormat.formatByte(totalTx) + "\n下载流量合计:" + TrafficFormat.formatByte(totalRx)); setTotalTraffic(TrafficFormat.formatByte(totalTraffic), TrafficFormat.formatByte(totalTx), TrafficFormat.formatByte(totalRx)); for (int i = trafficApplist.size() - 1; i >= 0; i--) { TrafficInfo trafficInfo = trafficApplist.get(i); if (trafficInfo.getTraffic().startsWith("0")) { trafficApplist.remove(i); } } Log.i("流量-", "过滤后的app的数量=" + trafficApplist.size()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Collections.sort(trafficApplist, new Comparator() { @Override public int compare(TrafficInfo o1, TrafficInfo o2) { //流量最大值排列 int max = (int) (o2.getMobleTotalData() - o1.getMobleTotalData()); return max; } }); } } /** * 流量合计 */ public void setTotalTraffic(String totalTraffic, String upTraffic, String downTraffic) { totalTrafficTv.setText(String.format("总流量:%s", totalTraffic)); totalUpTrafficTv.setText(String.format("上传总流量:%s", upTraffic)); totalDownTrafficTv.setText(String.format("下载总流量:%s", downTraffic)); } ``` ##### 5.2.3. 具体布局及整体实现请看开头的gif 或者源码 自行实现 demo项目地址 https://toscode.gitee.com/code_tjf/traffic-demo ### 6.下面是参考的博客地址: https://blog.csdn.net/Rookie_or_beginner/article/details/123376004?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-123376004-blog-8440160.pc_relevant_multi_platform_whitelistv4&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-123376004-blog-8440160.pc_relevant_multi_platform_whitelistv4&utm_relevant_index=1 ### 7.完结 ![撒花.gif](https://upload-images.jianshu.io/upload_images/12221638-5f84f36029e2f32f.gif?imageMogr2/auto-orient/strip)