In my app, multiple App Widgets can be placed on the home screen, with each widget fetching remote data on a periodic basis.
My goals are:
WebView
activity initiated from widgetIt's easy enough to log data usage for your own UID before and after a widget update with TrafficStats
, by using getUidRxBytes()
and getUidTxBytes()
(passing your own app's UID), and then taking the difference. Whilst this rough-and-ready approach gives sensible values when the widgets run in series, when they are running concurrently there is inevitably a degree of double (triple etc) counting going on.
I'd rather not force the widgets to update in series, because I'd rather let the job scheduling engine of the device to work out what is best. So I need a way to separate data usage for each widget from each other.
It seems that the use of tags might be the way to go... tag network operations with the appWidgetId
and then filter the stats based on this appWidgetId
. I'm not convinced this approach will actually enable data usage of one widget to be distinguished from another (maybe the buckets will become confused), but the fact is I can't get tagging to work at all so I can't yet tell.
You can set the tag with setThreadStatsTag(tag)
before a network operation starts, but I can't see any method in the TrafficStats
class for retrieving results filtered by tag. Does anyone know any different?
So I then looked at NetworkStatsManager, and in particular NetworkStatsManager.queryDetailsForUidTag(... , tag)
. This seems to allow you to filter based on a specific tag
, and works without any extra permissions (for your own app's UID) which is good. But I can't get that method to return any buckets with data, despite setting TrafficStats.setThreadStatsTag(appWidgetId)
before network operations using HttpURLConnection
, and querying with the same tag afterwards by calling NetworkStatsManager.queryDetailsForUidTag(... , appWidgetId)
.
On the other hand, NetworkStatsManager.queryDetailsForUid()
(no tag
parameter) does return buckets with valid data, so it seems to be just the tagging that isn't working, rather than anything fundamental I'm doing wrong with NetworkStatsManager
per se.
I set the tag with:
@Override
protected void onPreExecute() {
TrafficStats.setThreadStatsTag(appWidgetId);
}
@Override
protected Bitmap doInBackground(String... baseUrls) {
// fetch data with HttpURLConnection
}
@Override
protected void onPostExecute(Bitmap bmp) {
TrafficStats.clearThreadStatsTag();
}
And (for WiFi data) I query the stats with:
void networkStatsStuff() {
NetworkStats networkStats;
NetworkStats networkStatsTagged;
int networkType = ConnectivityManager.TYPE_WIFI;
try {
networkStats = networkStatsManager.queryDetailsForUid(networkType, "", startTime, endTime, myUid);
} catch (RemoteException e) {
return;
}
networkStatsTagged = networkStatsManager.queryDetailsForUidTag(networkType, "", startTime, endTime, myUid, appWidgetId);
long totalData = getTotalData(networkStats);
long totalDataTagged = getTotalData(networkStatsTagged);
Log.d(TAG, "totalData: " + totalData); // this seems to work
Log.d(TAG, "totalDataTagged: " + totalDataTagged); // this is always zero
}
long getTotalData(NetworkStats networkStats) {
long totalData = 0;
do {
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
networkStats.getNextBucket(bucket);
totalData = totalData + bucket.getRxBytes() + bucket.getTxBytes();
} while (networkStats.hasNextBucket());
return totalData;
}
What am I doing wrong? (EDIT: partial answer is below.) Or maybe there is a completely different way to achieve my aims?
Partial answer:
So I think I was led astray by this guide, which is where I got the placement of setThreadStatsTag()
and clearThreadStatsTag()
, in preExecute()
and postExecute()
respectively of the AsyncTask
.
When I move the setting and unsetting of the tag into the doInBackground()
part of the AsyncTask
, I then start seeing the tags... I noticed this first in the Android Device Monitor, but they're also there when using the NetworkStats
methods that filter by tag
.
So it seems more correct to use something like described in this guide, along the lines of:
@Override
protected Bitmap doInBackground(String... baseUrls) {
TrafficStats.setThreadStatsTag(appWidgetId);
try {
// fetch data with HttpURLConnection
} finally {
TrafficStats.clearThreadStatsTag();
}
}
So is the first guidance just plain wrong?
Still unresolved:
READ_PHONE_STATE
permission is required... seems like overkill, and annoying considering that the permission is not required with the TrafficStats
methods that also read mobile data usage, albeit along with other interfaces)WebView
(again, I fear that this is not possible, i.e. not possible to tag traffic in a WebView
and hence not possible to distinguish by tag just because of the nature of the protocols concerned... this and this may be relevant)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With