How to get the correct number of bytes sent and received in TrafficStats?

MLQ picture MLQ · Oct 7, 2012 · Viewed 19.7k times · Source

My app is trying to count the number of bytes send and received over WiFi/LAN and mobile data connections. To do that, I get the values of TrafficStats counters at one point in time and subtract that from its values the next time I check.

// get current values of counters
long currentMobileTxBytes = TrafficStats.getMobileTxBytes();
long currentMobileRxBytes = TrafficStats.getMobileRxBytes();
long totalTxBytes = TrafficStats.getTotalTxBytes();
long totalRxBytes = TrafficStats.getTotalRxBytes();

// to get mobile data count, subtract old from current
long currentMobileSent = currentMobileTxBytes - oldMobileTxBytes;
long currentMobileReceived = currentMobileRxBytes - oldMobileRxBytes;

// to get WiFi/LAN data count, subtract total from mobile
long currentNetworkSent = totalTxBytes - currentMobileTxBytes;
long currentNetworkReceived = totalRxBytes - currentMobileRxBytes;

I feel that the above algorithm is reasonable, however, I'm not sure how to check the accuracy of these counters. For example, when I tried uploading a 2.7MB file to Dropbox via WiFi, the currentMobileSent value I got was around 10MB. And even without surfing the web until the next check, I get non-zero values indicating that I did receive some bytes of data over the waiting period.

Is there a way for me to check how TrafficStats arrives at these numbers? I'm aware that besides my browser, there might be other applications running in the background that connect to the internet, but 2.7MB to 10MB just seems like a huge jump--I even "received" 90MB once without doing anything. Or is there something wrong with the way I'm computing the bytes sent and received?

Answer

G M Ramesh picture G M Ramesh · Oct 7, 2012

From TechRepublic:

  1. Create a new Android project in Eclipse. Remember to use the TrafficStats class you must target the API for Android 2.2 (Froyo) or higher.

  2. In the /res/layout folder we will create a activity_main.xml resource. For this project, we are just using a series of text views in a vertically stacked linear layout.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical">

   <TextView
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:paddingBottom="20dip"
       android:text="Traffic Stats Demo"
       android:textSize="16sp"
       android:textStyle="bold" />

   <TextView
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:text="Transmit Bytes"
       android:textColor="#00ff00"
       android:textSize="14sp" />

   <TextView
       android:id="@+id/TX"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:text="0"
       android:textSize="14sp" />

   <TextView
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:text="Receive Bytes"
       android:textColor="#ff0000"
       android:textSize="14sp" />

   <TextView
       android:id="@+id/RX"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:text="0"
       android:textSize="14sp" />
</LinearLayout>

With our layout in place we can move on to the /src folder. Create MainActivity.java by extending the Activity/AppCompatActivity class. Let’s also go ahead and declare three private class variables.

MainActivity.java

package com.authorwjf;

import android.app.Activity;
import android.app.AlertDialog;
import android.net.TrafficStats;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;

public class Main extends Activity {
    private Handler mHandler = new Handler();
    private long mStartRX = 0;
    private long mStartTX = 0;
}

We will use the on create override to initialize our private variables, as well as schedule a callback on the UI thread. Make a note of the check for the enum TrafficStats.UNSUPPORTED. While my experience with the TrafficStats class has been without a hitch, the official Google documentation states that some devices may not support this type of reporting and when that is the case the call returns the aforementioned value. For that reason it’s a good idea to write your code defensively, as I’ve demonstrated here.

MainActivity.java

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mStartRX = TrafficStats.getTotalRxBytes();
    mStartTX = TrafficStats.getTotalTxBytes();

    if (mStartRX == TrafficStats.UNSUPPORTED || mStartTX == TrafficStats.UNSUPPORTED) {
        AlertDialog.Builder alert = new AlertDialog.Builder(this);
        alert.setTitle("Uh Oh!");
        alert.setMessage("Your device does not support traffic stat monitoring.");
        alert.show();
    } else {
        mHandler.postDelayed(mRunnable, 1000);
    }
}

Last but not least we need to update our display and reschedule the runnable.

MainActivity.java

private final Runnable mRunnable = new Runnable() {
    public void run() {
        TextView RX = (TextView) findViewById(R.id.RX);
        TextView TX = (TextView) findViewById(R.id.TX);
        long rxBytes = TrafficStats.getTotalRxBytes() - mStartRX;
        RX.setText(Long.toString(rxBytes));
        long txBytes = TrafficStats.getTotalTxBytes() - mStartTX;
        TX.setText(Long.toString(txBytes));
        mHandler.postDelayed(mRunnable, 1000);
    }
};