Verwenden Sie Sensor Manager, um Schrittdaten in einer mobilen App auszufüllen, wie in dieser Anleitung beschrieben. Weitere Informationen zum Entwerfen und Verwalten einer Benutzeroberfläche für Trainings-Apps findest du unter Einfache Fitness-App erstellen.
Erste Schritte
Damit Sie die Schritte Ihres einfachen Schrittzählers auf Ihrem Mobilgerät messen können, müssen Sie die Abhängigkeiten der Datei build.gradle
Ihres App-Moduls hinzufügen. Achten Sie darauf, dass Sie die neuesten Versionen der Abhängigkeiten verwenden.
Wenn du die Unterstützung deiner App auf andere Formfaktoren wie Wear OS ausdehnst, füge außerdem die Abhängigkeiten hinzu, die für diese Formfaktoren erforderlich sind.
Im Folgenden finden Sie einige Beispiele für einige UI-Abhängigkeiten. Eine vollständige Liste finden Sie im Leitfaden zu UI-Elementen.
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material:material")
Schrittzählersensor abrufen
Nachdem der Nutzer die erforderliche Berechtigung zur Aktivitätserkennung erteilt hat, kannst du auf den Schrittzähler zugreifen:
- Rufen Sie das
SensorManager
-Objekt vongetSystemService()
ab. - Beziehen Sie den Schrittzählersensor aus dem
SensorManager
:
private val sensorManager by lazy {
getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val sensor: Sensor? by lazy {
sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) }
Einige Geräte haben keinen Schrittzählersensor. Sie sollten nach dem Sensor suchen und eine Fehlermeldung anzeigen lassen, wenn das Gerät keinen hat:
if (sensor == null) {
Text(text = "Step counter sensor is not present on this device")
}
Dienst im Vordergrund erstellen
In einer einfachen Fitness-App haben Sie möglicherweise eine Schaltfläche, um Start- und Stoppereignisse vom Nutzer für das Tracking von Schritten zu empfangen.
Beachte die Best Practices für Sensoren. Insbesondere sollte der Schrittzählersensor nur Schritte zählen, wenn der Sensor-Listener registriert ist. Durch die Verknüpfung der Sensorregistrierung mit einem Dienst im Vordergrund wird der Sensor registriert, solange er benötigt wird. Er kann auch dann registriert bleiben, wenn sich die App nicht im Vordergrund befindet.
Mit dem folgenden Snippet können Sie die Registrierung des Sensors in der Methode onPause()
Ihres Dienstes im Vordergrund aufheben:
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
Ereignisdaten analysieren
Implementieren Sie die SensorEventListener
-Schnittstelle, um auf die Sensordaten zuzugreifen. Sie sollten die Sensorregistrierung dem Lebenszyklus Ihres Dienstes im Vordergrund zuordnen und die Registrierung des Sensors aufheben, wenn der Dienst pausiert oder beendet wird. Das folgende Snippet zeigt, wie die SensorEventListener
-Schnittstelle für Sensor.TYPE_STEP_COUNTER
implementiert wird:
private const val TAG = "STEP_COUNT_LISTENER"
context(Context)
class StepCounter {
private val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
private val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
suspend fun steps() = suspendCancellableCoroutine { continuation ->
Log.d(TAG, "Registering sensor listener... ")
val listener: SensorEventListener by lazy {
object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent?) {
if (event == null) return
val stepsSinceLastReboot = event.values[0].toLong()
Log.d(TAG, "Steps since last reboot: $stepsSinceLastReboot")
if (continuation.isActive) {
continuation.resume(stepsSinceLastReboot)
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
Log.d(TAG, "Accuracy changed to: $accuracy")
}
}
}
val supportedAndEnabled = sensorManager.registerListener(listener,
sensor, SensorManager.SENSOR_DELAY_UI)
Log.d(TAG, "Sensor listener registered: $supportedAndEnabled")
}
}
Datenbank für die Sensorereignisse erstellen
Möglicherweise zeigt Ihre App einen Bildschirm an, auf dem der Nutzer seine Schritte im Zeitverlauf sehen kann. Sie können diese Funktion in Ihrer App mit der Chatroom-Persistenzbibliothek bereitstellen.
Mit dem folgenden Snippet wird eine Tabelle erstellt, die eine Reihe von Messungen der Schrittzahl enthält. Außerdem wird die Zeit angegeben, zu der die App auf die einzelnen Messungen zugegriffen hat:
@Entity(tableName = "steps")
data class StepCount(
@ColumnInfo(name = "steps") val steps: Long,
@ColumnInfo(name = "created_at") val createdAt: String,
)
Erstellen Sie ein Datenzugriffsobjekt (Data Access Object, DAO) zum Lesen und Schreiben der Daten:
@Dao
interface StepsDao {
@Query("SELECT * FROM steps")
suspend fun getAll(): List<StepCount>
@Query("SELECT * FROM steps WHERE created_at >= date(:startDateTime) " +
"AND created_at < date(:startDateTime, '+1 day')")
suspend fun loadAllStepsFromToday(startDateTime: String): Array<StepCount>
@Insert
suspend fun insertAll(vararg steps: StepCount)
@Delete
suspend fun delete(steps: StepCount)
}
Erstellen Sie ein RoomDatabase
-Objekt, um den DAO zu instanziieren:
@Database(entities = [StepCount::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun stepsDao(): StepsDao
}
Sensordaten in der Datenbank speichern
ViewModel verwendet die neue StepCounter-Klasse, sodass Sie die Schritte speichern können, sobald Sie sie lesen:
viewModelScope.launch {
val stepsFromLastBoot = stepCounter.steps()
repository.storeSteps(stepsFromLastBoot)
}
Die Klasse repository
würde so aussehen:
class Repository(
private val stepsDao: StepsDao,
) {
suspend fun storeSteps(stepsSinceLastReboot: Long) = withContext(Dispatchers.IO) {
val stepCount = StepCount(
steps = stepsSinceLastReboot,
createdAt = Instant.now().toString()
)
Log.d(TAG, "Storing steps: $stepCount")
stepsDao.insertAll(stepCount)
}
suspend fun loadTodaySteps(): Long = withContext(Dispatchers.IO) {
printTheWholeStepsTable() // DEBUG
val todayAtMidnight = (LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT).toString())
val todayDataPoints = stepsDao.loadAllStepsFromToday(startDateTime = todayAtMidnight)
when {
todayDataPoints.isEmpty() -> 0
else -> {
val firstDataPointOfTheDay = todayDataPoints.first()
val latestDataPointSoFar = todayDataPoints.last()
val todaySteps = latestDataPointSoFar.steps - firstDataPointOfTheDay.steps
Log.d(TAG, "Today Steps: $todaySteps")
todaySteps
}
}
}
}
Regelmäßig Sensordaten abrufen
Wenn Sie einen Dienst im Vordergrund verwenden, müssen Sie WorkManager
nicht konfigurieren. Während die App die Nutzerschritte aktiv erfasst, sollte die aktualisierte Gesamtschrittzahl in der App angezeigt werden.
Wenn Sie Ihre Schrittaufzeichnungen im Batch zusammenfassen möchten, können Sie WorkManager
verwenden, um Schritte in einem bestimmten Intervall zu messen, z. B. einmal alle 15 Minuten.
WorkManager
ist die Komponente, die die Hintergrundarbeit für eine garantierte Ausführung durchführt. Weitere Informationen dazu finden Sie im WorkManager-Codelab.
Wenn das Worker
-Objekt zum Abrufen der Daten konfiguriert werden soll, überschreiben Sie die Methode doWork()
, wie im folgenden Code-Snippet gezeigt:
private const val TAG = " StepCounterWorker"
@HiltWorker
class StepCounterWorker @AssistedInject constructor(
@Assisted appContext: Context,
@Assisted workerParams: WorkerParameters,
val repository: Repository,
val stepCounter: StepCounter
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(TAG, "Starting worker...")
val stepsSinceLastReboot = stepCounter.steps().first()
if (stepsSinceLastReboot == 0L) return Result.success()
Log.d(TAG, "Received steps from step sensor: $stepsSinceLastReboot")
repository.storeSteps(stepsSinceLastReboot)
Log.d(TAG, "Stopping worker...")
return Result.success()
}
}
So richten Sie WorkManager
so ein, dass die aktuelle Schrittzahl alle 15 Minuten gespeichert wird:
- Erweitern Sie die Klasse
Application
, um dieConfiguration.Provider
-Schnittstelle zu implementieren. - Fügen Sie in der Methode
onCreate()
einPeriodicWorkRequestBuilder
in die Warteschlange.
Dieser Vorgang wird im folgenden Code-Snippet dargestellt:
@HiltAndroidApp
@RequiresApi(Build.VERSION_CODES.S)
internal class PulseApplication : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory
override fun onCreate() {
super.onCreate()
val myWork = PeriodicWorkRequestBuilder<StepCounterWorker>(
15, TimeUnit.MINUTES).build()
WorkManager.getInstance(this)
.enqueueUniquePeriodicWork("MyUniqueWorkName",
ExistingPeriodicWorkPolicy.UPDATE, myWork)
}
override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
}
Um den Contentanbieter zu initialisieren, der den Zugriff auf die Schrittzähler-Datenbank Ihrer App direkt beim Start der App steuert, fügen Sie der Manifestdatei Ihrer App das folgende Element hinzu:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />