3.1: Sensor basics

Contents:

Most Android-powered devices have built-in sensors that measure motion, orientation, and various environmental conditions. These sensors are capable of providing raw data with high precision and accuracy, and are useful if you want to monitor three-dimensional device movement or positioning, or you want to monitor changes in the ambient environment near a device.

For example, a game might track readings from a device's gravity sensor to infer complex user gestures and motions, such as tilt, shake, rotation, or swing. Likewise, a compass app might use the geomagnetic field sensor and accelerometer to report a compass bearing.

About sensors

The Android platform supports three major categories of sensors:

  • Motion sensors, to measure device motion. These sensors include accelerometers, gravity sensors, gyroscopes, and rotational vector sensors.
  • Environmental sensors, to measure various environmental conditions, such as ambient air temperature and pressure, illumination, and humidity. These sensors include barometers, photometers (light sensors), and thermometers.
  • Position sensors, to measure the physical position of a device. This category includes magnetometers (geomagnetic field sensors) and proximity sensors.

The device camera, fingerprint sensor, microphone, and GPS (location) sensor all have their own APIs and are not considered "sensors" for the purposes of the Android sensor framework. You learn more about location in a different chapter.

Hardware and software sensors

Sensors can be hardware- or software-based. Hardware-based sensors are physical components built into a handset or tablet device. Hardware sensors derive their data by directly measuring specific environmental properties, such as acceleration, geomagnetic field strength, or angular change.

Software-based sensors are not physical devices, although they mimic hardware-based sensors. Software-based sensors derive their data from one or more of the hardware-based sensors and are sometimes called virtual sensors or composite sensors . The proximity sensor and step counter sensors are examples of software-based sensors.

The Android sensor framework

You can access available sensors and acquire raw sensor data in your app with the Android sensor framework. The sensor framework, part of the android.hardware package, provides classes and interfaces to help you perform a wide variety of sensor-related tasks. With the Android sensor framework you can:

  • Determine which sensors are available on a device.
  • Determine an individual sensor's capabilities, such as its maximum range, manufacturer, power requirements, and resolution.
  • Acquire raw sensor data and define the minimum rate at which you acquire sensor data.
  • Register and unregister sensor event listeners that monitor sensor changes.

The following classes are the key parts of the Android sensor framework:

  • SensorManager: Represents the Android sensor service. This class provides methods for accessing and listing sensors, registering and unregistering sensor event listeners, and acquiring orientation information. This class also provides several sensor constants. The constants are used to represent sensor accuracy, data acquisition rates, and sensor calibration.
  • Sensor: Represents a specific sensor. This class provides methods that let you determine sensor's capabilities.
  • SensorEvent: Represents information about a sensor event. A sensor event includes the raw sensor data, the type of sensor that generated the event, the accuracy of the data, and the timestamp for the event.
  • SensorEventListener: An interface that includes callback methods to receive notifications (sensor events) when a sensor has new data or when sensor accuracy changes.

Sensor types and availability

Few Android-powered devices have every type of sensor. For example, most handset devices and tablets have an accelerometer and a magnetometer, but many fewer devices have barometers or thermometers. Also, a device can have more than one sensor of a given type. For example, a device can have two gravity sensors, each one having a different range. The Sensor class defines the sensor types listed in the table below.

Sensor availability can also vary between Android versions, because the Android sensors have been introduced over the course of several platform releases.

The following table summarizes the sensors that the Android platform supports.

Sensor Used for
TYPE_ACCELEROMETER Motion detection (shake, tilt, and so on).
TYPE_AMBIENT_TEMPERATURE Monitoring air temperature.
TYPE_GRAVITY Motion detection (shake, tilt, and so on).
TYPE_GYROSCOPE Rotation detection (spin, turn, and so on).
TYPE_LIGHT Controlling screen brightness.
TYPE_LINEAR_ACCELERATION Monitoring acceleration along a single axis.
TYPE_MAGNETIC_FIELD Creating a compass.
TYPE_ORIENTATION Determining device position.
TYPE_PRESSURE Monitoring air pressure changes.
TYPE_PROXIMITY Phone position during a call.
TYPE_RELATIVE_HUMIDITY Monitoring ambient humidity (relative and absolute), and dew point.
TYPE_ROTATION_VECTOR Motion and rotation detection.
TYPE_TEMPERATURE Monitoring temperatures.

Sensors and the Android emulator

The Android emulator includes a set of virtual sensor controls that let you to test sensors such as the accelerometer, the ambient temperature sensor, the magnetometer, the proximity sensor, the light sensor, and more.  Sensors in the Android emulator

The Accelerometer tab lets you test your app against changes in device position, orientation, or both. For example, you can simulate device motion such as tilt and rotation. The control simulates the way accelerometers and magnetometers respond when you move or rotate a real device. As you adjust the device in the emulators, the Resulting Values fields change accordingly. These fields show the values that an app can access.

The Additional sensors tab can simulate various position and environment sensors. In this tab you adjust the following sensors to test them with your app:

  • Ambient temperature: This environmental sensor measures ambient air temperature.
  • Magnetic field: This position sensor measures the ambient magnetic field at the x -axis, y -axis, and z -axis. The values are in microtesla (μT).
  • Proximity: This position sensor measures the distance of the device from an object. For example, this sensor can notify a phone that a face is close to the phone to make a call.
  • Light: This environmental sensor measures illuminance.
  • Pressure: This environmental sensor measures ambient air pressure.
  • Relative humidity: This environmental sensor measures ambient relative humidity.

Discovering sensors and sensor capabilities

The Android sensor framework lets you determine at runtime which sensors are available on a device. The framework also provides methods that let you determine the capabilities of a sensor, such as its maximum range, its resolution, and its power requirements.

Identifying sensors

To identify the device sensors you must first access the sensor manager, an Android system service. Create an instance of the SensorManager class by calling the getSystemService() method and passing in the SENSOR_SERVICE argument.

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

To get a listing of all the device sensors, use the getSensorList() method and the sensor type TYPE_ALL.

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

To list all of the sensors of a specific type, use another sensor type constant, for example TYPE_PROXIMITY, TYPE_GYROSCOPE, or TYPE_GRAVITY.

To determine whether a specific type of sensor exists on a device, and to get a Sensor object that represents that sensor, use the getDefaultSensor() method and pass in the type constant for a specific sensor. If a device has more than one sensor of a given type, the system designates one of the sensors as the default sensor. If a default sensor does not exist for a given type of sensor, the method call returns null, which means the device does not have that type of sensor.

For example, the following code checks whether there's a magnetometer on a device:

private SensorManager mSensorManager;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){
  // Success! There's a magnetometer.
  }
else {
  // Failure! No magnetometer.
}

Identifying sensor features

Use the public methods of the Sensor class to determine the capabilities and attributes of individual sensors. These methods useful if you want your app to behave differently based on which sensors or sensor capabilities are available on a device. For example, you can use the getResolution() and getMaximumRange() methods to obtain a sensor's reported resolution and maximum range of measurement. You can also use the getPower() method to obtain a sensor's power requirements.

The getVendor() and getVersion() methods are particularly useful. Use these methods to optimize your app for different manufacturer's sensors or different versions of a sensor. For example, maybe your app needs to monitor device motion such as tilt and shake. In this case you could create two sets of data-filtering rules and optimizations: one set of rules and optimizations for newer devices that have a specific vendor's gravity sensor, and a second set of rules and optimizations for devices that do not have a gravity sensor and have only an accelerometer.

A streaming sensor is one that senses and reports new data as fast as possible. The getMinDelay() method returns the minimum time interval (in microseconds) that a sensor can use to sense data. Any sensor that returns a non-zero value for the getMinDelay() method is a streaming sensor. Streaming sensors sense data at regular intervals and were introduced in Android 2.3 (API Level 9). If a sensor returns zero when you call getMinDelay(), it means that the sensor is not a streaming sensor. Non-streaming sensors only report data when that data has changed.

The getMinDelay() method lets you determine the maximum rate at which a sensor can acquire data. If your app requires high data-acquisition rates or a streaming sensor, use getMinDelay() to determine whether a sensor meets your app's requirements. Then turn on or turn off the relevant features in your app accordingly.

Note: A sensor's maximum data acquisition rate is not necessarily the rate at which the sensor framework delivers sensor data to your app. The sensor framework reports data through sensor events, and several factors influence the rate at which your app receives sensor events.

Handling different sensor configurations

Android does not specify a standard sensor configuration for devices, which means device manufacturers can incorporate any sensor configuration that they want into their Android-powered devices. As a result, devices include a variety of sensors in a wide range of configurations. If your app relies on a specific type of sensor, you have to ensure that the sensor is present on a device so your app can run successfully.

You have two options for ensuring that a given sensor is present on a device:

  • Detect sensors at runtime, then turn on or turn off app features as appropriate.
  • Use Google Play filters to target devices with specific sensor configurations.

Detecting sensors at runtime

If your app uses a specific type of sensor but doesn't rely on it, you can use the sensor framework to detect the sensor at runtime, then turn off or turn on app features as appropriate. For example, a weather app might use the temperature sensor, pressure sensor, GPS sensor, and geomagnetic field sensor to display the temperature, barometric pressure, location, and compass bearing. You can use the sensor framework to detect the absence of the pressure sensor at runtime and then turn off the portion of your app's UI that displays pressure.

Using Google Play filters to target specific sensor configurations

If you publish your app on Google Play, use the <uses-feature> element in your app manifest file to filter your app from devices that do not have the appropriate sensor configuration for your app. The <uses-feature> element has several hardware descriptors that let you filter apps based on the presence of specific sensors. The sensors you can list include: accelerometer, barometer, compass (geomagnetic field), gyroscope, light, and proximity.

The following is an example manifest entry that filters out apps that do not have an accelerometer:

<uses-feature android:name="android.hardware.sensor.accelerometer"
   android:required="true" 
/>

If you add this element and descriptor to your app's manifest, users see your app on Google Play only if their device has an accelerometer.

About the android:required attribute:

  • Set the descriptor to android:required="true" only if your app relies entirely on a specific sensor.
  • If your app uses a sensor for some functionality but can run without the sensor, list the sensor in the <uses-feature> element and set the descriptor to android:required="false". Doing this helps ensure that devices can install your app even if they do not have that particular sensor.

Monitoring sensor events

The Android system generates sensor events each time the sensor has new data. To monitor sensor events in your app, you must:

  • Implement the SensorEventListener interface, which includes the onSensorChanged() and onAccuracyChanged() callback methods.
  • Register sensor event listeners for the specific sensor you want to monitor.
  • Get sensor types and values from the SensorEvent object, and update your app accordingly.

Implement the SensorEventListener interface

To monitor raw sensor data, implement the SensorEventListener interface's two callback methods: onAccuracyChanged() and onSensorChanged().

public class SensorActivity extends Activity implements SensorEventListener { ... }

When a sensor's accuracy changes, the Android system calls onAccuracyChanged() method and passes in two arguments:

An example of a change in sensor accuracy might be a heart-rate monitor. If the sensor stopped reporting a heartbeat, the accuracy would be SENSOR_STATUS_NO_CONTACT. If the sensor was reporting an unrealistically low or high heartbeat, the accuracy might be SENSOR_STATUS_UNRELIABLE. You could override onAccuracyChanged() to catch these changes in accuracy and report errors to the user.

@Override
public final void onAccuracyChanged(Sensor sensor, int accuracy) {
    // Do something here if the sensor accuracy changes.
}

The Android system calls the onSensorChanged() method when the sensor reports new data, passing in a SensorEvent object. The SensorEvent object contains information about the new sensor data, including the accuracy of the data, the sensor that generated the data, the timestamp at which the data was generated, as well as the actual new data itself.

@Override
public void onSensorChanged(SensorEvent sensorEvent) {
        // Do something here if sensor data changes
}

You learn more about the onSensorChanged() method and sensor event objects below.

Register listeners

To receive sensor events, you must register a listener for that event in your app. Sensors generate a lot of data at a very high rate, and they use power and battery when they do. Registering a particular sensor enables your app to handle only specific events at a specific rate. To preserve device resources, unregister the listener when you don't need it, for example when your app pauses.

To register a listener, use the SensorManager.registerListener() method. This method takes three arguments:

  • An app or activity Context. You can use the current activity (this) as the context.
  • The Sensor object to listen to. Use the SensorManager.getDefaultSensor() method in your onCreate() to get a Sensor object.
  • A delay constant from the SensorManager class. The data delay (or sampling rate) controls the interval at which sensor events are sent to your app via the onSensorChanged() callback method. Make sure that your listener is registered with the minimum amount of new data that it needs.

    The default data delay (SENSOR_DELAY_NORMAL) is suitable for monitoring typical screen orientation changes and uses a delay of 200,000 microseconds (0.2 seconds). You can specify other data delays, such as SENSOR_DELAY_GAME (20,000 microseconds or 20 milliseconds), SENSOR_DELAY_UI (60,000 microseconds or 60 milliseconds), or SENSOR_DELAY_FASTEST (no delay, as fast as possible).

Register your sensor listeners in the onStart() lifecycle method, and unregister them in onStop(). Don't register your listeners in onCreate(), as that would cause the sensors to be on and sending data (using device power) even when your app was not in the foreground. To make sure sensors only use power when your app is running in the foreground, register and unregister your sensor listeners in the onStart() and onStop() methods.

The onStart() and onStop() methods are a better choice for registering listeners than onResume() and onPause(). As of Android 7.0 (API 24), apps can run in multi-window mode (split-screen or picture-in-picture mode). Apps running in this mode are paused (onPause() has been called) but still visible on screen. Use onStart() and onStart() to ensure that sensors continue running even if the app is in multi-window mode.

@Override
protected void onStart() {
   super.onStart();

   if (mIsLightSensorPresent) {
       mSensorManager.registerListener(this, mSensorLight, 
          SensorManager.SENSOR_DELAY_UI);
   }
}

@Override
protected void onStop() {
   super.onStop();
   mSensorManager.unregisterListener(this);
}

Handle sensor data changes

To handle changes to sensor data, override the onSensorChanged() callback from the SensorEventListener class. The onSensorChanged() method is passed a SensorEvent object that contains information about the event. Your implementation typically will need two pieces of data from the SensorEvent object:

  • sensor: The sensor that generated the event, as a Sensor object.
  • values: The data the sensor generated, as an array of floats. Different sensors provide different amounts and types of data in that array. For example, the light sensor produces only one piece of data, which is stored in values[0]. The values for other sensors may have additional data—for example, the values array from the accelerometer includes data for the x -axis, y -axis, and z -axis in values[0], values[1], and values[2], respectively.
Note: Sensor data can change at a high rate, which means the system may call the onSensorChanged() method quite often. As a best practice, do as little as possible within the onSensorChanged() method so you don't block it. If your app requires you to do any data filtering or reduction of sensor data, perform that work outside of the onSensorChanged() method.

This example shows an onSensorChanged() method that handles changes to the light sensor.

@Override
public void onSensorChanged(SensorEvent sensorEvent) {
   // The sensor type (as defined in the Sensor class).
   int sensorType = sensorEvent.sensor.getType();

   // The current value of the sensor. 
   float currentValue = sensorEvent.values[0];

   // Event came from the light sensor.
   if (sensorType == Sensor.TYPE_LIGHT)
   {
       // Get the light sensor string from the resources, fill 
       // in the data placeholder
       String str_current = getResources().getString(
          R.string.label_light, currentValue);
       mTextSensorLight.setText(str_current);
   }
}

The related practical documentation is in Working with Sensor Data.

Learn more

Android developer documentation:

Android API Reference:

results matching ""

    No results matching ""