Sensors (Accelerometer, Gyroscope)

Most phones carry a handful of physical sensors — an accelerometer, sometimes a gyroscope, a light sensor, a proximity sensor. Android exposes all of them through a single system service, the SensorManager, regardless of which sensor you actually want to read.

To keep things grounded, we'll add one small, playful feature to RecipeBox: shake the phone on the recipe list screen and get a random recipe suggestion — a "surprise me" gesture powered by the accelerometer.

Getting the SensorManager

// RecipeListActivity.java
private SensorManager sensorManager;
private Sensor accelerometer;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_recipe_list);

    sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

    if (accelerometer == null) {
        // Not every device has one — degrade gracefully
        Log.w("RecipeList", "No accelerometer on this device, shake feature disabled");
    }
}
Explanation: getDefaultSensor() returns null when the device has no sensor of that type. Cheap devices, TVs, or emulators without sensor simulation may lack an accelerometer entirely — always check before registering a listener for it.

Registering a SensorEventListener

A listener is registered in onResume(), so it's only active while the screen is visible:

private long lastShakeTime = 0;
private static final float SHAKE_THRESHOLD = 12f; // m/s^2 above gravity, tuned by feel

private final SensorEventListener shakeListener = new SensorEventListener() {
    @Override
    public void onSensorChanged(SensorEvent event) {
        float x = event.values[0];
        float y = event.values[1];
        float z = event.values[2];

        double magnitude = Math.sqrt(x * x + y * y + z * z) - SensorManager.GRAVITY_EARTH;

        long now = System.currentTimeMillis();
        if (magnitude > SHAKE_THRESHOLD && (now - lastShakeTime) > 1000) {
            lastShakeTime = now;
            suggestRandomRecipe();
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Called when sensor accuracy changes; usually safe to ignore for a shake gesture
    }
};

@Override
protected void onResume() {
    super.onResume();
    if (accelerometer != null) {
        sensorManager.registerListener(shakeListener, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
    }
}
Explanation:
  • onSensorChanged() fires repeatedly with fresh x, y, z readings — dozens of times per second.
  • Subtracting GRAVITY_EARTH isolates the extra acceleration caused by movement, not gravity itself sitting still.
  • onAccuracyChanged() is required by the interface but for a simple shake gesture there's rarely anything useful to do in it.
  • SENSOR_DELAY_NORMAL is fine here — we don't need the fastest possible sampling rate for a shake gesture.

Unregistering in onPause()

This is the one habit that genuinely matters with sensors: always unregister the listener when the screen is no longer visible.

@Override
protected void onPause() {
    super.onPause();
    sensorManager.unregisterListener(shakeListener);
}
Why this matters: A registered sensor listener keeps the underlying hardware active and keeps calling your callback continuously — even while your Activity is in the background. Left registered across many screens or a long time, this drains battery for no benefit, since the user isn't even looking at the shake-to-suggest feature anymore. Pairing registerListener() in onResume() with unregisterListener() in onPause() keeps it active only when it can actually be useful.

Common Sensor Types

ConstantMeasuresTypical use
Sensor.TYPE_ACCELEROMETERAcceleration on x/y/z axes (incl. gravity)Shake detection, step counting, tilt
Sensor.TYPE_GYROSCOPERotation rate around each axisOrientation tracking, game controls
Sensor.TYPE_LIGHTAmbient light level (lux)Auto-adjusting screen/UI brightness
Sensor.TYPE_PROXIMITYDistance to nearest object (often binary: near/far)Turning off the screen during a phone call

Checking Availability Before Use

Not every device has every sensor — a budget phone might skip the gyroscope, a tablet might have no proximity sensor at all. getDefaultSensor(type) is the standard availability check, and it always returns null rather than throwing when the sensor is missing:

Sensor gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
if (gyroscope != null) {
    // safe to register a listener for it
} else {
    // hide or disable whatever UI depends on it
}
Honest take: Most everyday apps — including RecipeBox — use very few sensors directly, if any. Step counters, camera apps needing orientation, and games are the typical users of raw sensor data. Treat this lesson as a fun, narrow-use-case capability to reach for occasionally, not something a typical app needs to lean on regularly.
With sensors covered, the next hardware topic is connecting to nearby devices over Bluetooth.