Verwenden Sie den Sensor Manager, um Schrittdaten in einer mobilen App zu erfassen, wie hier beschrieben . Weitere Informationen zum Entwerfen und Verwalten der Benutzeroberfläche einer Trainings-App siehe Erstellen Sie eine einfache Fitness-App.
Erste Schritte
Um mit dem Messen der Schritte deines Basis-Schrittzählers zu beginnen,
Mobilgerät verwenden, müssen Sie die Abhängigkeiten zu Ihrem App-Modul hinzufügen.
build.gradle
-Datei. 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 ausweitest,
und fügen Sie die Abhängigkeiten hinzu,
die diese Formfaktoren erfordern.
Im Folgenden finden Sie einige Beispiele für einige UI-Abhängigkeiten. Eine vollständige Liste Weitere Informationen finden Sie in diesem 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ähler-Sensor abrufen
Nachdem der Nutzer die erforderliche Berechtigung zur Aktivitätserkennung gewährt hat, können Sie auf den Sensor des Schrittzählers zugreifen:
- Rufen Sie das
SensorManager
-Objekt ausgetSystemService()
ab. - Erwerben Sie den Schrittzähler-Sensor von
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ähler-Sensor. Du solltest nach dem Sensor suchen und eine Fehlermeldung anzeigen, wenn auf dem Gerät keine vorhanden ist:
if (sensor == null) {
Text(text = "Step counter sensor is not present on this device")
}
Dienst im Vordergrund erstellen
In einer einfachen Fitness-App gibt es vielleicht eine Schaltfläche um Start- und Stopp-Ereignisse vom Nutzer zum Verfolgen von Schritten zu empfangen.
Beachte die Best Practices für Sensoren. Insbesondere sollte der Schrittzähler-Sensor nur Schritte zählen, während der Sensor Listener registriert ist. Durch Verknüpfung der Sensorregistrierung mit einem Vordergrund wird der Sensor registriert, solange er benötigt wird, bleiben registriert, wenn die App nicht im Vordergrund ausgeführt wird.
Verwenden Sie das folgende Snippet, um die Registrierung des Sensors in der Methode onPause()
von
Ihren Dienst im Vordergrund:
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
Daten für Ereignisse analysieren
Implementieren Sie die SensorEventListener
-Schnittstelle, um auf die Sensordaten zuzugreifen. Hinweis
müssen Sie die Sensorregistrierung
Dadurch wird die Registrierung des Sensors aufgehoben, wenn der Dienst pausiert oder beendet wird. Die
folgendes Snippet zeigt, wie die SensorEventListener
-Schnittstelle implementiert wird
für Sensor.TYPE_STEP_COUNTER
:
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 Sensorereignisse erstellen
In Ihrer App wird möglicherweise ein Bildschirm angezeigt, auf dem der Nutzer seine Schritte im Zeitverlauf sehen kann. Verwenden Sie die Raumpersistenzbibliothek, um diese Funktion in Ihrer App bereitzustellen.
Mit dem folgenden Snippet wird eine Tabelle erstellt, die eine Anzahl von Schritten enthält Messungen sowie die Zeit, 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,
)
Datenzugriffsobjekt (Data Access Object, DAO) erstellen 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 zum Instanziieren des DAO ein RoomDatabase
-Objekt:
@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 so schnell wie möglich speichern können. während 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äßiges Abrufen von Sensordaten
Wenn Sie einen Dienst im Vordergrund verwenden, müssen Sie WorkManager
nicht konfigurieren
Denn während der Zeit, in der Ihre App aktiv die Schritte
des Nutzers erfasst,
sollte die aktualisierte Gesamtschrittzahl in deiner App angezeigt werden.
Wenn Sie Ihre Schrittdatensätze im Batch zusammenfassen möchten, können Sie WorkManager
verwenden, um
die Schritte in einem bestimmten Intervall messen, zum Beispiel alle 15 Minuten.
WorkManager
ist die Komponente, die den Hintergrund ausführt.
dass die Umsetzung garantiert ist. Weitere Informationen findest du im WorkManager-Codelab.
Wenn Sie das Worker
-Objekt zum Abrufen der Daten konfigurieren möchten, überschreiben Sie den doWork()
an, 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()
}
}
Um WorkManager
so einzurichten, dass die aktuelle Schrittzahl alle 15 Minuten gespeichert wird, musst du Folgendes tun:
Folgendes:
Application
-Klasse erweitern, umConfiguration.Provider
zu implementieren .- Stelle in der Methode
onCreate()
einePeriodicWorkRequestBuilder
in die Warteschlange ein.
Dieser Prozess 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()
}
Initialisieren Sie den Contentanbieter, der den Zugriff auf den Schritt Ihrer App steuert Zähler-Datenbank sofort beim Start der Anwendung das folgende Element in die Manifestdatei Ihrer App ein:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />