Smartphones

Create a network monitor using Android's TrafficStats class

William Francis provides an introduction to Android's TrafficStats class and shows how to use it to create a simple but effective bandwidth monitor.

A few weeks ago I wrote a tutorial on how to monitor the battery state from within your Android application. In that post I described a number of reasons you might concern your application with the health of the battery. Chief among the motives mentioned was simply writing your app to use the device's limited resources in a responsible manner.

The same logic can be applied to a phone's network utilization, especially when you consider that not all smartphone users have unlimited bandwidth plans. Going over bandwidth allocations can get expensive fast. In certain application scenarios, allowing your user to configure the maximum amount of bandwidth your app consumes in a given time frame could be an extremely valuable feature offering.

On initial releases of the Android operating system, there wasn't a standard way to get at bandwidth metrics. As of Android 2.2 though, Google introduced the TrafficStats class for exactly that purpose. The tutorial that follows is a quick introduction to the class. The result is a simple bandwidth monitor that shows total bytes sent and received on the platform from the time the app was started. Feel free to follow along step-by-step or download the entire project here and import it directly into Eclipse.

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 main.xml resource. For this project, we are just using a series of text views in a vertically stacked linear layout.

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:textSize="16sp"
android:textStyle="bold"
android:gravity="center"
android:paddingBottom="20dip"
android:text="Traffic Stats Demo" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#00ff00"
android:gravity="center"
android:text="Transmit Bytes" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:gravity="center"
android:text="0"
android:id="@+id/TX"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#ff0000"
android:gravity="center"
android:text="Receive Bytes" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:gravity="center"
android:text="0"
android:id="@+id/RX"/>
</LinearLayout>

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

Main.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;
}

4. 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.

Main.java
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.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);
}
}

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

Main.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);
}
};

The example simply reports total bytes in and out of the device, but I encourage you to look into the TrafficStats class closer. You'll find the class capable of reporting the metrics using packets in lieu of bytes, and methods to differentiate between mobile and Wi-Fi network I/O.

Figure A

About

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

2 comments
kaustubhvp
kaustubhvp

i tried creating app by using above tutorial...but when i close the app by clicking on back button; after resume, it starts again with fresh values of rxBytes and txBytes. kindly help.

Pabloku
Pabloku

Thanks about this useful article. I am a dummy developer in Android and I found this article very intereseting for my idea. I have some doubts. I am trying to save in a SQLite database the 3G data usage. I would like to ask you about the best "design" you would suggest me. What I want to do is to log the 3G data usage into a table for query this information every month. I have been "playing" with services and trafficStats. I saw that If I turn off the 3G and then turn on it again, the TrafficStats.getMobileTxBytes and TrafficStats.getMobileRxBytes counter are reseted... Is there any way to avoid this or to "listen" for switching on/off the 3g? Thanks very much, Pabloku