9

I'm looking at implementing the step sensor API introduced in Android 4.4 (http://youtu.be/yv9jskPvLUc). However, I am unable to find a clear explanation on what the recommended way to monitor this in the background is? It seems like most examples only show how to do this with an activity while the app is running. I don't particularly need a high frequency of updates - I basically want to log the amount of steps the user has walked every hour to a backend service. Should I just spin up a background service that calls registerListener on SensorManager, or is there a more elegant way?

Mattias Petter Johansson
  • 1,064
  • 1
  • 15
  • 32

3 Answers3

12

As far as I know, there is no way around the SensorManager, but if you need the data very infrequently, you could trigger the sensor manually and get its values with a TriggerEventListener, which is a little cleaner than a SensorEventListener.

AlarmManager is typically the best option for starting an hourly timer, and it works even if your app isn't running. AlarmManager sends an Intent to a class that extends BroadcastReceiver, and that class will start your Service. The AlarmManager can be set anywhere in your app depending on your implementation.

StepCountService

SensorManager sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
Sensor stepCounter = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
sensorManager.requestTriggerSensor(listener, stepCounter);

private TriggerEventListener listener = new TriggerEventListener(){
    @Override
    public void onTrigger(TriggerEvent event) {
        //handle step count here 
    }
}

MainActivity

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, AlarmReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(context, 0, i,
    PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,AlarmManager.INTERVAL_HOUR, 
    AlarmManager.INTERVAL_HOUR, alarmIntent);

AlarmReceiver

public class AlarmReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    Intent service = new Intent(context, StepCountService.class);
    context.startService(service);
  }
} 
TheoKanning
  • 1,631
  • 11
  • 20
  • I'm perfectly okay with getting the data infrequently. Where would you place this code? In an alarm receiver triggered periodically? (Sorry if I'm being daft, not new to development, but new to Android) – Mattias Petter Johansson Jan 15 '16 at 17:43
  • Yes that's the correct way, I edited my answer to show the details – TheoKanning Jan 15 '16 at 19:27
  • @TheoKanning Could you elaborate on how the step sensor would be able to count the steps taken in an hour period if `registerListener()` wasn't called? Doesn't setting a trigger should be applied to one-shot sensors only? – ibe Mar 24 '17 at 17:24
  • @ibe as far as I know, the step count will only return the total number of steps since the device booted, so there's no way to know how many happened in the last hour without saving the total an hour ago. You can apply a `TriggerEventListener` instead of a `SensorEventListener` if you only need one value, and not a continuous stream. – TheoKanning Mar 26 '17 at 20:18
  • for any who copy code and get error replace alarmIntent with pending or vise versa I am talking about the name of the pending intent variable – 3bdoelnaggar Jan 08 '21 at 13:17
  • requestTriggerSensor returns false and onTrigger() is never called. I have the permission ACTIVITY_RECOGNITION. Any ideas why this happens? – Kewitschka Feb 15 '21 at 20:26
  • 1
    thanks for this solution, although: `Beginning with SDK Version Build.VERSION_CODES.O, apps targeting SDK Version Build.VERSION_CODES.O or higher are not allowed to start background services from the background.` so would this be a problem if the service is started from the alarm while the app is in the background? how are we supposed to workaround this restriction? – Raphael C Nov 23 '22 at 15:34
3

This is not a complete solution, but the most energy-efficient way could be to wake up your device every hour or so, start a service which quickly reads the data, then goes back to sleep.

Depending on which device level you target, using a WakefulBroadcastReceiver, as described in this answer, seems the way to go.

You need to

  1. create the code by modifying the templates at WakefulBroadcastReceiver
  2. add the Service and the BroadCastReceiver into the manifest.
  3. schedule a repeating alarm somewhere in your app

If any of the points a less than clear, say so. See http://code.tutsplus.com/tutorials/android-barometer-logger-acquiring-sensor-data--mobile-10558

Community
  • 1
  • 1
serv-inc
  • 35,772
  • 9
  • 166
  • 188
  • 1
    `WakefulBroadcastReceiver` This class was deprecated in API level 26.1.0. – Raphael C Nov 23 '22 at 15:40
  • @RaphaelC Yes, yet the question explicitly asks about Android 4.4. Since a great many useful features have been removed/deprecated because they had been abused, the answer can remain as-is. – serv-inc Nov 24 '22 at 13:02
2

@TheoKanning's answer is the correct way to do this manually. Alternatively, Google Fit continuously logs this data and has an API you can use to pull it into your app.

var47
  • 422
  • 3
  • 6
  • 2
    Note: The Fit Android API has been deprecated as of May 11, 2022 and will be turned down at the end of 2024. See the Fit Android API to Health Connect migration guide for instructions on which API or platform to migrate to. For a comparison of Health Connect with the Fit Android and Fitbit Web APIs, see the Health Connect comparison guide. To learn more about Health Connect and integrate with the API, see the Health Connect documentation. – Raphael C Nov 23 '22 at 14:54