Używaj Menedżera czujników do wypełniania danych o krokach w aplikacji mobilnej w sposób opisany w tym przewodniku. Więcej informacji o projektowaniu i zarządzaniu interfejsem aplikacji do ćwiczeń znajdziesz w artykule Tworzenie podstawowej aplikacji do fitnessu.
Wprowadzenie
Aby zacząć mierzyć liczbę kroków podstawowego licznika kroków na urządzeniu mobilnym, musisz dodać zależności do pliku build.gradle
modułu aplikacji. Używaj najnowszych wersji zależności.
Jeśli rozszerzysz obsługę aplikacji na inne formaty, takie jak Wear OS, dodaj zależności, których wymagają te formaty.
Poniżej znajdziesz kilka przykładów niektórych zależności interfejsu. Pełną listę znajdziesz w tym przewodniku po elementach interfejsu.
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material:material")
Uzyskaj czujnik licznika kroków
Gdy użytkownik przyzna niezbędne uprawnienia do rozpoznawania aktywności, możesz uzyskać dostęp do czujnika licznika kroków:
- Uzyskaj obiekt
SensorManager
z serweragetSystemService()
. - Znajdź czujnik licznika kroków z
SensorManager
:
private val sensorManager by lazy {
getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val sensor: Sensor? by lazy {
sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) }
Niektóre urządzenia nie są wyposażone w czujnik licznika kroków. Sprawdź czujnik i wyświetl komunikat o błędzie, jeśli urządzenie go nie ma:
if (sensor == null) {
Text(text = "Step counter sensor is not present on this device")
}
Tworzenie usługi na pierwszym planie
W podstawowej aplikacji do fitnessu możesz mieć przycisk do otrzymywania zdarzeń rozpoczęcia i zakończenia śledzenia kroków użytkownika.
Skorzystaj ze sprawdzonych metod dotyczących czujników. W szczególności czujnik licznika kroków powinien zliczać kroki tylko wtedy, gdy jest zarejestrowany. Powiązanie rejestracji czujnika z usługą działającą na pierwszym planie powoduje, że czujnik rejestruje się tak długo, jak jest to potrzebne, a czujnik pozostaje zarejestrowany, gdy aplikacja nie działa na pierwszym planie.
Użyj tego fragmentu kodu, aby wyrejestrować czujnik w metodzie onPause()
usługi na pierwszym planie:
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
Analizowanie danych zdarzeń
Aby uzyskać dostęp do danych z czujnika, zaimplementuj interfejs SensorEventListener
. Pamiętaj, że rejestrację czujników należy powiązać z cyklem życia usługi na pierwszym planie, co spowoduje wyrejestrowanie czujnika po jej wstrzymaniu lub zakończeniu. Ten fragment kodu pokazuje, jak wdrożyć interfejs SensorEventListener
w 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")
}
}
Tworzenie bazy danych na potrzeby zdarzeń związanych z czujnikami
Aplikacja może wyświetlać ekran, na którym użytkownik może sprawdzać swoje kroki w czasie. Aby udostępnić tę funkcję w swojej aplikacji, użyj biblioteki trwałości sal.
Ten fragment kodu tworzy tabelę zawierającą zestaw pomiarów liczby kroków wraz z czasem uzyskania dostępu przez aplikację do poszczególnych pomiarów:
@Entity(tableName = "steps")
data class StepCount(
@ColumnInfo(name = "steps") val steps: Long,
@ColumnInfo(name = "created_at") val createdAt: String,
)
Utwórz obiekt dostępu do danych (DAO), aby odczytywać i zapisywać dane:
@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)
}
Aby utworzyć instancję DAO, utwórz obiekt RoomDatabase
:
@Database(entities = [StepCount::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun stepsDao(): StepsDao
}
Zapisywanie danych z czujnika w bazie danych
ViewModel używa nowej klasy StepCounter, dzięki czemu możesz zapisywać kroki od razu po ich odczytaniu:
viewModelScope.launch {
val stepsFromLastBoot = stepCounter.steps()
repository.storeSteps(stepsFromLastBoot)
}
Klasa repository
wyglądałaby tak:
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
}
}
}
}
Okresowe pobieranie danych z czujnika
Jeśli korzystasz z usługi na pierwszym planie, nie musisz konfigurować funkcji WorkManager
, ponieważ w okresie, gdy aplikacja aktywnie śledzi kroki użytkownika, powinna pojawić się w niej zaktualizowana łączna liczba kroków.
Jeśli jednak chcesz zgrupować rekordy kroków, możesz użyć funkcji WorkManager
, aby mierzyć kroki w określonych odstępach czasu, np. co 15 minut.
WorkManager
to komponent, który wykonuje w tle pracę na potrzeby gwarantowanego wykonania. Więcej informacji znajdziesz w ćwiczeniach z programowania w usłudze WorkManager.
Aby skonfigurować obiekt Worker
do pobierania danych, zastąp metodę doWork()
jak w tym fragmencie kodu:
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()
}
}
Aby skonfigurować w WorkManager
zapisywanie bieżącej liczby kroków co 15 minut, wykonaj te czynności:
- Rozszerz klasę
Application
, aby zaimplementować interfejsConfiguration.Provider
. - W metodzie
onCreate()
umieśćPeriodicWorkRequestBuilder
w kolejce.
Ten proces pojawia się w tym fragmencie kodu:
@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()
}
Aby bezpośrednio po uruchomieniu aplikacji zainicjować dostawcę treści, który kontroluje dostęp do bazy danych licznika kroków, dodaj do pliku manifestu aplikacji ten element:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />