November 26, 2014
Hot Topics:

How to Build Mobile Apps with Android Sensors

  • January 28, 2013
  • By Keith Vance
  • Send Email »
  • More Articles »

One of most interesting aspects of writing code for mobile devices are sensors. Desktop and laptop computers don't normally come with thermometers, gyroscopes, geomagnetic field detectors or ambient air pressure sensors.  But all that changed with mobile devices, such as Android.

Mobile computers don't sit on a desk and wait for the user to interact with it. An Android device can continuously monitor the device's surrounding environment and applications can be written to make use of that sensor data.

The Android operating system currently supports 13 different sensors. However, not all Android devices have the necessary hardware to support all of the sensors.

Fortunately it's a trivial task to determine the sensors available on a given device.

The Android sensor framework is part of the android.hardware package, which includes the SensorManager. To check available sensors, simply instantiate a SensorManager object.

private SensorManager mSensorMgr;

And then execute some code like this to get a list of sensors.

mSensorMgr = SensorManager) getSystemService(Context.SENSOR_SERVICE);
List sensors = mSensorMgr.getSensorList(Sensor.TYPE_ALL);

The sensors object will be a list of all available sensors on the device. To check for a specific sensor, use one of the other sensor constants such as, TYPE_TEMPERATURE, TYPE_RELATIVE_HUMIDITY or TYPE_PRESSURE.

There's also the getDefaultSensor() method. Passing a specific sensor constant to it will also determine whether a sensor is available on a device.

And if a device has more than one sensor of a given type, one of the sensors will be set as the default sensor. If there is no default sensor set, getDefaultSensor() will return null, thus indicating that the sensor is not present.

For example, the code to check for a gyroscope sensor using the getDefaultSensor() method could look something like this.

 if (mSensorMgr.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null) {
 // Yes. I can now measure orientation based on the principles of angular momentum
 } else {
 // There's no gyroscope on this device, perhaps I can use the more simple accelerometer instead.
 }
 

Testing for sensors is vital on Android devices because Google does not require hardware manufacturers to build any particular sensors into a device.  And whether a sensor is available isn't always the only test because not all sensors are created equal.

For example, an application might require the gravity sensor that's of a particular version.
The gravity sensor is actually a virtual sensor, but it still relies on a device having particular hardware sensors available for it to work.

The following code first checks if the gravity sensor is present. If so, it then checks that the vendor is Google and that the sensor is version 3.

if
(mSensorMgr.getDefaultSensor(Sensor.TYPE_GRAVITY) != null) {
List gravity = mSensorMgr.getSensorList(Sensor.TYPE_GRAVITY);
for (int i = 0; i < gravity.size(); i++) {
if (gravity.get(i).getVendor().contains("Google Inc.") &&
gravity.get(i).getVersion() == 3) {
// I've got the version 3 Google gravity sensor I need.
mGravity = gravity.get(i);
}
}
} else {
// No Google gravity sensor version 3, perhaps I can use the accelerometer
}

It's also possible, if an application requires a specific sensor, to use Google Play to target only devices that have that particular sensor. For example, for devices that must have an accelerometer, add the following to an application's manifest file.

android:required="true"
/>

Another useful method is getMinDelay(). Calling it determines the minimum time interval, in microseconds, a sensor takes to get sensor data. If getMinDelay() returns a non-zero value, that means the sensor is a streaming sensor.

Introduced in Android 2.3, streaming sensors sense data at regular intervals. Non-streaming sensors only report data when the sensor's parameters change and will return zero when calling getMinDelay().

Let's put it all together with a simple application.

The application uses the device's light sensor and adjusts the background and text color based on the ambient light. As the ambient light dims, the background brightens and vice versa.

 package com.take88.mylightsensor;

 import android.graphics.Color;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Bundle;
 import android.app.Activity;
 import android.content.Context;
 import android.view.Menu;
 import android.widget.RelativeLayout;
 import android.widget.TextView;

 public class MainActivity extends Activity implements SensorEventListener {
 private SensorManager mSensorMgr;
 private TextView mLightTxt;
 private Sensor mLight;
 private RelativeLayout mLayout;

 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 mSensorMgr = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
 mLight = mSensorMgr.getDefaultSensor(Sensor.TYPE_LIGHT);
 mLayout = (RelativeLayout) findViewById(R.id.layout);
 mLightTxt = (TextView) findViewById(R.id.light);
 brightness(Color.BLACK, Color.WHITE, "0");
 }

 private void brightness(int bground, int txt, String lux) {
 mLayout.setBackgroundColor(bground);
 mLightTxt.setBackgroundColor(bground);
 mLightTxt.setTextColor(txt);
 mLightTxt.setText("LUX: " + lux);
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
 getMenuInflater().inflate(R.menu.activity_main, menu);
 return true;
 }

 @Override
 public void onAccuracyChanged(Sensor arg0, int arg1) {
 // TODO Auto-generated method stub

 }

 @Override
 public void onSensorChanged(SensorEvent event) {
 // The light sensor returns one value, which is the ambient light level in SI lux units
 float lux = event.values[0];
 if (lux brightness(Color.WHITE, Color.BLACK, String.valueOf(lux));
 } else if (lux > 10 && lux brightness(Color.GRAY, Color.BLACK, String.valueOf(lux));
 } else {
 brightness(Color.BLACK, Color.WHITE, String.valueOf(lux));
 }
 }

 @Override
 protected void onResume() {
 super.onResume();
 mSensorMgr.registerListener(this, mLight, SensorManager.SENSOR_DELAY_NORMAL);
 }

 @Override
 protected void onPause() {
 super.onPause();
 mSensorMgr.unregisterListener(this);
 }
 }
 

Be careful with the onSensorChanged() method. Sensor data can change rapidly and onSensorChanged() can be called frequently. Therefore, do as little as possible in this method and avoid blocking, or an application will be really clunky and not fun to use.

It's also very important to use the onPause() method and unregister the sensor listener.

If it's not unregistered, the sensor will continue to hammer away at the device's battery, even when the screen goes to sleep. Users don't like applications that drain their battery unnecessarily.

Use the onResume() method to register the listener when the device wakes up again.

Sensors can not be tested in the Android emulator, they must be tested on an actual device.


Tags: Android, mobile apps, Android App development, sensor support




Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel