Android NetworkStats.Bucket данные не обновляются
Я создаю приложение, которое отслеживает использование данных сети приложений в течение некоторого интервала.
На Android 5 у меня не было проблем с TrafficStats, хотя на Android 8 с использованием NetworkStats я всегда получаю одни и те же данные. Я читал, что ведро обновляется каждые 30 минут или около того, поэтому я пытался прослушивать youtube около 1 часа, но все равно использовал данные (в пакете com.google.android.youtube).
Итак, мой вопрос, почему NetworkStats всегда возвращает одни и те же данные?
Вот мой код:
public class DataService extends Service {
private int interval = 1000 * 5; //Time interval
private List<ApplicationInfo> infos;
private PackageManager packageManager;
private static int counter = 0;
private ArrayList<ValuesHolder> values;
private Context context;
private Handler mHandler;
public static final String DATA_INTENT_FILTER = "Transmitted data updated";
private Runnable mHandlerTask = new Runnable() {
@Override
public void run() {
update();
Intent intent = new Intent();
intent.setAction(DATA_INTENT_FILTER);
sendBroadcast(intent);
mHandler.postDelayed(mHandlerTask, interval);
}
};
@Override
public void onCreate() {
super.onCreate();
mHandler = new Handler();
context = this;
values = new ArrayList<>();
packageManager = getPackageManager();
infos = packageManager.getInstalledApplications(0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
getNonSystemPackages();
}
mHandlerTask.run();
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void getNonSystemPackages() {
PackageManager pm = getPackageManager();
List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo packageInfo : packages) {
if (pm.getLaunchIntentForPackage(packageInfo.packageName) != null) {
String currAppName = packageInfo.packageName;
try {
int uid = pm.getPackageUid(currAppName, 0);
values.add(counter, new ValuesHolder(currAppName,
getTotalDataForMobile(uid) + getTotalDataForWifi(uid),
uid));
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
} else {
//System App
}
}
}
private void update() {
String saveString;
long difference;
long data;
int uid;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
for (int i = 0; i < values.size(); i++) {
uid = values.get(i).getUid();
if (TrafficStats.getUidTxBytes(uid) == TrafficStats.UNSUPPORTED) {
data = getTotalDataForMobile(uid) + getTotalDataForWifi(uid);
//Always same data
Log.i(values.get(i).getPackageName(), " data" + data);
} else {
data = TrafficStats.getUidRxBytes(uid) + TrafficStats.getUidTxBytes(uid);
}
}
} catch (NullPointerException e) {
e.printStackTrace();
}
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
private long getTotalDataForWifi(int uid) {
NetworkStatsManager networkStatsManager = (NetworkStatsManager) getApplicationContext().getSystemService(Context.NETWORK_STATS_SERVICE);
NetworkStats.Bucket bucket;
long totalData = 0;
NetworkStats networkStats = null;
try {
networkStats = networkStatsManager.queryDetailsForUid(
ConnectivityManager.TYPE_WIFI,
"",
0,
System.currentTimeMillis(),
uid);
} catch (RemoteException e) {
return -1;
}
do {
bucket = new NetworkStats.Bucket();
networkStats.getNextBucket(bucket);
totalData += bucket.getRxBytes() + bucket.getTxBytes();
} while (networkStats.hasNextBucket());
return totalData;
}
@RequiresApi(api = Build.VERSION_CODES.M)
private long getTotalDataForMobile(int uid) {
NetworkStatsManager networkStatsManager = (NetworkStatsManager) getApplicationContext().getSystemService(Context.NETWORK_STATS_SERVICE);
NetworkStats.Bucket bucket;
NetworkStats networkStats = null;
long totalData = 0;
try {
networkStats = networkStatsManager.queryDetailsForUid(
ConnectivityManager.TYPE_MOBILE,
"",
0,
System.currentTimeMillis(),
uid);
} catch (RemoteException e) {
return -1;
}
do {
bucket = new NetworkStats.Bucket();
networkStats.getNextBucket(bucket);
totalData += bucket.getRxBytes() + bucket.getTxBytes();
} while (networkStats.hasNextBucket());
return totalData;
}
}
Я запрашиваю разрешения времени выполнения в моей MainActivity следующим образом:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, Permissions.ARR_PHONE_STATE, Permissions.REQUEST_PHONE_STATE);
}
if
((checkSelfPermission(Manifest.permission.ACCESS_NETWORK_STATE) != PackageManager.PERMISSION_GRANTED)
|| (checkSelfPermission(Manifest.permission.ACCESS_WIFI_STATE) != PackageManager.PERMISSION_GRANTED)
|| (checkSelfPermission(Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED)) {
ActivityCompat.requestPermissions(this, Permissions.ARR_NETWORK, Permissions.REQUEST_NETWORK);
}
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) {
} else {
}
if (!Permissions.checkForPermission(this)) {
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
} else {
Log.i("SYSTEM", "PERMISSION GRANTED");
}
}
Разрешения предоставлены, я их запрашиваю и в манифесте андроида.
Спасибо за любую помощь.
2 ответа
Я наткнулся на эту очень точную проблему, и я понял, что вы все еще можете использовать метод queryDetailsForUid
,
Поскольку вы сами сказали, что сегменты достаточно велики (что-то в диапазоне 2-3 часа), вы можете увеличить параметр интервала времени окончания метода.
Вместо того, чтобы использовать System.currentTimeMillis()
как последнее время, возьмите Calendar.getInstance()
объект, добавить 1 день к этому (calendar.add(Calendar.DATE, 1)
) и передать его в качестве параметра с calendar.getTimeInMillis()
,
Таким образом, вы убедитесь, что текущее ведро будет также включено.
Отвечая на мой вопрос, для кого-то, у кого может быть такая же проблема. Я выяснил, что с методом queryDetailsForUid данные обновляются с довольно большим интервалом (около 3 часов).
Вот решение, которое работает хорошо:
private long getApplicationUsage(int networkType, int uid) {
long usage = 0L;
NetworkStats networkStatsByApp;
NetworkStatsManager networkStatsManager = (NetworkStatsManager) getApplicationContext().getSystemService(Context.NETWORK_STATS_SERVICE);
try {
networkStatsByApp = networkStatsManager.querySummary(networkType, "", 0, System.currentTimeMillis());
do {
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
networkStatsByApp.getNextBucket(bucket);
if (bucket.getUid() == uid) {
// in some devices this is immediately looping twice
// and the second iteration is returning correct value.
// So result is returned in the end.
usage = (bucket.getRxBytes() + bucket.getTxBytes());
}
} while (networkStatsByApp.hasNextBucket());
} catch (RemoteException e) {
e.printStackTrace();
}
return usage;
}
При запуске приложения просто сохраните общий результат, затем по истечении заданного интервала вычтите общее использование с сохраненным значением, и вы получите результат. (не забудьте сохранить новый результат).