【问题标题】:Android NetworkStats.Bucket data not updatingAndroid NetworkStats.Bucket 数据未更新
【发布时间】:2018-08-17 04:14:16
【问题描述】:

我正在构建一个应用程序,它在某个时间间隔内监控应用程序网络数据的使用情况。

在 android 5 上,我对 TrafficStats 没有任何问题,尽管在使用 NetworkStats 的 android 8 上,我总是收到相同的数据。我读到桶每 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");
        }
    }

已授予权限,我也在 android manifest 中请求它们。

感谢您的帮助。

【问题讨论】:

    标签: android bucket networkstatsmanager android-data-usage


    【解决方案1】:

    我偶然发现了这个非常确切的问题,我发现您仍然可以使用方法queryDetailsForUid。
    正如您自己所说的桶非常大(在 2-3 小时范围内),您可以增加该方法的结束时间间隔参数。

    不要使用 System.currentTimeMillis() 作为结束时间,而是使用 Calendar.getInstance() 对象,将其添加 1 天 (calendar.add(Calendar.DATE, 1)) 并将其作为参数传递给 calendar.getTimeInMillis()。
    这样,您就可以确保当前存储桶也将包含在内。

    【讨论】:

    • 经过测试,工作可靠。显然是获取最新数据的唯一方法。
    【解决方案2】:

    回答我自己的问题,对于可能有同样问题的人。我发现使用方法 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;
    }
    

    在应用程序启动时只保存总结果,然后在所需的时间间隔后用您保存的值减去总使用量,您就得到了结果。 (不要忘记保存新结果)。

    【讨论】:

    • 怎么发现bucket区间大的?
    猜你喜欢
    • 1970-01-01
    • 2016-10-28
    • 1970-01-01
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    • 2016-04-10
    • 2021-12-13
    • 2015-01-17
    相关资源
    最近更新 更多