Advertisement
Scroll to top
This post is part of a series called Android From Scratch.
Android From Scratch: Understanding Android Broadcasts
Android From Scratch: Using REST APIs

One of the things that makes developing for mobile devices different from other platforms is that a mobile phone or tablet is packed full of sensors and other hardware that developers can take advantage of for data input. In this tutorial you will be introduced to the Android sensor framework. You'll learn how to detect what sensors are available on a device and how to read data from those sensors.

Device Sensors

There are three major categories of base sensors to be aware of: motion, environmental, and device position. Motion sensors detect changes in forces around the three axes of the device: X, Y, and Z, as shown here:

The X Y and Z axes of a mobile deviceThe X Y and Z axes of a mobile deviceThe X Y and Z axes of a mobile device

Motion sensors include the gyroscope, accelerometer, and rotational vector sensors.

Environmental sensors gather information about the environment that the phone is in. This includes humidity and ambient temperature sensors, light detection, and barometers. 

The final category of base sensors, device position, does exactly what you'd expect: it detects the position of the device. This category includes orientation sensors and the magnetometer.

Any sensor that doesn't fall under one of the base categories is considered a composite sensor. These sensors aren't actually sensors in the traditional sense, but instead gather their data from multiple other physical sensors on a device and aggregate it. Examples include the step counter, gravity sensor, and rotational vector sensor.

Reading Sensor Data

Now that you know a little about the types of sensors in mobile devices, it's time to start reading data from actual hardware. Let's start by discovering what sensors are available on a device.

Discovering Sensors on Device

In order to access the sensors on a device, you will need to use the SensorManager class, which you can retrieve as a system service from an Activity

1
mSensorManager = (SensorManager) getSystemService( Context.SENSOR_SERVICE );

Once you have a SensorManager, you can retrieve a default Sensor based on type, or retrieve a List of Sensor objects based on a type parameter. For this example we'll retrieve a List of all Sensor objects, like so:

1
List<Sensor> sensorList = mSensorManager.getSensorList( Sensor.TYPE_ALL );

You'll notice that we used TYPE_ALL here. If we only want a specific subset of sensors, we can pass in any of the supported type attributes, such as the items in the following, noninclusive, list.

1
TYPE_AMBIENT_TEMPERATURE 
2
TYPE_DEVICE_PRIVATE_BASE
3
TYPE_GAME_ROTATION_VECTOR 
4
TYPE_GEOMAGNETIC_ROTATION_VECTOR 
5
TYPE_GRAVITY
6
TYPE_GYROSCOPE
7
TYPE_GYROSCOPE_UNCALIBRATED 
8
TYPE_HEART_BEAT 
9
TYPE_HEART_RATE 
10
TYPE_LIGHT
11
TYPE_LINEAR_ACCELERATION 
12
TYPE_MAGNETIC_FIELD
13
TYPE_MAGNETIC_FIELD_UNCALIBRATED 
14
TYPE_MOTION_DETECT 

Once you have the Sensor(s) that you want to work with, you can start retrieving data from them.

Sensor Event Listeners

Each Sensor object contains information about the physical hardware for that sensor, such as power, input delay, name, type, and vendor. If you want to retrieve information from that sensor, however, you will need to register a SensorEventListener that will be called at set intervals with new information readings.

1
mSensorManager.registerListener( this, mSensor, SensorManager.SENSOR_DELAY_UI );

There are four specified reading speeds: fastest, game, normal, and UI. Some will allow you to read information more often, but will consume more power from the device. The less frequently a sensor is polled, the less power it will use.

Once a SensorEventListener is registered, the listener's onSensorChanged method will be called with a new SensorEvent every time the sensor is polled. SensorEvent wraps the Sensor object that provides it with data, as well as information about the time, the accuracy of the sensor, and the actual input data. 

The input data is stored in an array of floats called values. The length of values can vary depending on the sensor. For example, the accelerometer will have three values (X, Y, and Z input), whereas a barometer will only have one value. When you use sensor data, understanding what data is available and how it's stored in the values array will be important for understanding how you will build out your app, while also avoiding crashes from issues, such as an ArrayIndexOutOfBoundsException

1
@Override
2
public void onSensorChanged(SensorEvent sensorEvent) {
3
    Log.e("Tuts+", "Accuracy: " + sensorEvent.accuracy );
4
    Log.e("Tuts+", "Timestamp: " + sensorEvent.timestamp);
5
    Log.e("Tuts+", "Accelerometer X: " + sensorEvent.values[0]);
6
    Log.e("Tuts+", "Accelerometer Y: " + sensorEvent.values[1]);
7
    Log.e("Tuts+", "Accelerometer Z: " + sensorEvent.values[2]);
8
}

The above code segment, when run with the device accelerometer, will provide output like this:

1
E/Tuts+: Accuracy: 3
2
E/Tuts+: Timestamp: 572565573007319
3
E/Tuts+: Accelerometer X: 0.80444336
4
E/Tuts+: Accelerometer Y: 1.7597351
5
E/Tuts+: Accelerometer Z: 9.425964

And since the listener is providing input over time, it can be fed into a graph to display information similar to the following (though the graphing library itself is out of the scope of this tutorial).

Graph showing changes in X Y and Z axes over timeGraph showing changes in X Y and Z axes over timeGraph showing changes in X Y and Z axes over time

Event Triggers

While a SensorEventListener will continue to provide information over time, a TriggerEventListener will listen for an event and then immediately disable itself. This sort of listener is used for things such as significant motion. You can add a TriggerEventListener to the SensorManager like so.

1
mSensorManager.requestTriggerSensor(new TriggerEventListener() {
2
    @Override
3
    public void onTrigger(TriggerEvent triggerEvent) {
4
        Log.e("Tuts+", "onTrigger");
5
        for(int i = 0; i < triggerEvent.values.length; i++ ) {
6
            Log.e("Tuts+", "item " + i + ": " + triggerEvent.values[i]);
7
        }
8
    }
9
}, mSensor);

If the Sensor passed to requestTriggerSensor is a significant motion sensor, then the above will have an output when the device is moved like so:

1
E/Tuts+: onTrigger
2
E/Tuts+: item 0: 1.0

One thing to note with both SensorEventListener and TriggerEventListener is that they should be unregistered from your SensorManager when you are done using them, such as in onDestroy within your Fragment or Activity classes, in order to prevent leaks in your application.

Conclusion

In this tutorial you have been introduced to Android's sensor framework. You have learned how to find sensors on a device and gather information from them. Using this, you can create complex apps that are aware of their environment, special input, and actions from users. Learning to use sensor data well will open a lot of interesting features and functionalities that you can use for your applications.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.