先上完成效果
编译 环境 及工具
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 流量查询辅助类
*
* <p> 使用方式
* * NetworkStatsManager networkStatsManager = (NetworkStatsManager) getSystemService(NETWORK_STATS_SERVICE);
* * NetworkStatsHelper helper = new NetworkStatsHelper(networkStatsManager);
* </p>
*/
@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 即可
* 需要权限
* <uses-permission android:name="android.permission.READ_PHONE_STATE" />
*
* @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 添加如下权限内
<!--Android 11获取应用列表 权限-->
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<!-- 适配安卓12&11获取当前已安装的所有应用列表-->
<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent>
</queries>
<!--包使用情况统计-->
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<!--读取手机状态 这个是在getSubscriberId()用到的 android10后就可删除,因为不让获取了 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
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.读取应用列表权限 下面这俩个选择一个就可以 点击应用排行列表的时候需要手动授权
<!-- Android 11获取应用列表 权限–>-->
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<!-- 适配安卓12&11获取当前已安装的所有应用列表-->
<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent>
</queries>
4.3 获取手机应用列表 下面选择一个使用即可
1.获取拥有internet权限的应用列表
/**
* 获取拥有internet权限的应用列表
*
* @return
*/
public List<TrafficInfo> getInternetTrafficInfos() {
List<TrafficInfo> trafficInfos = new ArrayList<TrafficInfo>();
//获取手机中安装的并且具有权限的应用 PackageManager.GET_UNINSTALLED_PACKAGES |
List<PackageInfo> 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<TrafficInfo> getInstalledApplications() {
List<TrafficInfo> trafficInfos = new ArrayList<TrafficInfo>();
List<ApplicationInfo> 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);
}
/**
* 获得当前时间的<code java.util.Date</code 对象
*
* @return
*/
public static Date now() {
return new Date();
}
/**
* 获取当天的零点时间
*/
public static long getTimesmorning() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.MILLISECOND, 0);
return (cal.getTimeInMillis());
}
/**
* 获得本月第一天0点时间
*/
public static long getTimesMonthmorning() {
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.getTimeInMillis();
}
/**
* 获得当前月的第一天
* <p>
* 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();
}
/**
* 获得当前月的第一天
* <p>
* 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();
}
/**
* 获得当前月的第一天到当前时间的时间戳集合
* <p>
* HH:mm:ss SS为零
*
*
*/
public static List<Long> getListDayOfMonth() {
List<Long> 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<Long>() {
@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 结构如下图所示
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<TrafficDayInfo> 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<Long> 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<TrafficInfo> 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<TrafficInfo>() {
@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