Monitorowanie danych w tle

Pasywne aktualizacje danych są odpowiednie dla aplikacji, które muszą w tle monitorować dane usług medycznych. Są przeznaczone do stosowania w przypadkach obejmujących godziny, dni, a nawet dłużej. Jeśli musisz przechowywać lub przetwarzać dane dotyczące zdrowia, gdy Twoja aplikacja nie jest uruchomiona, a użytkownik nie jest wyraźnie zaangażowany w ćwiczenie, użyj pasywnego klienta usługi Health Service.

Przykłady pasywnego wykorzystania danych znajdziesz w przykładach dotyczących danych pasywnych i celów pasywnych w GitHubie.

Dodaj zależności

Aby dodać zależność od usług zdrowotnych, musisz dodać do projektu repozytorium Google Maven. Więcej informacji znajdziesz w repozytorium Google Maven.

W pliku build.gradle na poziomie modułu dodaj tę zależność:

Odlotowy

dependencies {
    implementation "androidx.health:health-services-client:1.1.0-alpha02"
}

Kotlin

dependencies {
    implementation("androidx.health:health-services-client:1.1.0-alpha02")
}

Sprawdź możliwości

Zanim zarejestrujesz się w celu aktualizacji danych, sprawdź, czy urządzenie może dostarczać typ danych, których potrzebuje Twoja aplikacja. Sprawdzanie możliwości pozwala włączać i wyłączać niektóre funkcje oraz modyfikować interfejs aplikacji, aby zrekompensować funkcje, które są niedostępne.

val healthClient = HealthServices.getClient(this /*context*/)
val passiveMonitoringClient = healthClient.passiveMonitoringClient
lifecycleScope.launchWhenCreated {
    val capabilities = passiveMonitoringClient.capabilities.await()
    // Supported types for passive data collection
    supportsHeartRate =
        DataType.HEART_RATE_BPM in capabilities.supportedDataTypesPassiveMonitoring
    // Supported types for PassiveGoals
    supportsStepsGoal =
        DataType.STEPS_DAILY in capabilities.supportedDataTypesPassiveGoals
}

Zarejestruj dla danych pasywnych

Dane pasywne możesz otrzymywać za pomocą usługi, wywołania zwrotnego lub obu tych metod. Usługa umożliwia aplikacji odbieranie danych w tle, gdy żadna jej część nie jest widoczna na pierwszym planie. Gdy odbierasz dane w tle, są one wysyłane partiami. Wywołanie zwrotne odbiera dane z nieco szybszą szybkością, ale tylko wtedy, gdy aplikacja jest uruchomiona, a wywołanie zwrotne jest powiadamiane.

Niezależnie od wybranej metody utwórz najpierw element PassiveListenerConfig określający typy danych do otrzymywania, jak w tym przykładzie:

val passiveListenerConfig = PassiveListenerConfig.builder()
    .setDataTypes(setOf(DataType.HEART_RATE_BPM))
    .build()

Aby odbierać dane za pomocą wywołania zwrotnego, zdefiniuj i zarejestruj wywołanie zwrotne, tak jak w tym przykładzie:

val passiveListenerCallback: PassiveListenerCallback = object : PassiveListenerCallback {
    override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
        // TODO: Do something with dataPoints
    }
}

passiveMonitoringClient.setPassiveListenerCallback(
    passiveListenerConfig,
    passiveListenerCallback
)

// To remove the listener
passiveMonitoringClient.clearPassiveListenerCallbackAsync()

Korzystanie z usługi jest podobne, ale zamiast tworzyć klasę generowaną na podstawie PassiveListenerCallback, pobierz dane z elementu PassiveListenerService, jak pokazano w tym przykładzie:

class PassiveDataService : PassiveListenerService() {
    override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
        // TODO: Do something with dataPoints
    }
}

passiveMonitoringClient.setPassiveListenerServiceAsync(
    PassiveDataService::class.java,
    passiveListenerConfig
)

Następnie zadeklaruj usługę w pliku AndroidManifest.xml. Wymagaj uprawnienia dotyczącego usług medycznych, które daje pewność, że z usługą mogą być powiązane tylko te usługi:

<service android:name=".PassiveDataService"
    android:permission="com.google.android.wearable.healthservices.permission.PASSIVE_DATA_BINDING"
    android:exported="true" />

Zinterpretuj czas

Dane otrzymywane z usług zdrowotnych są grupowane, możesz więc otrzymywać w jednej grupie punkty danych różnego typu lub wiele punktów danych tego samego typu. Do określenia prawidłowej kolejności zdarzeń używaj sygnatur czasowych w tych obiektach, a nie czasu ich otrzymania przez aplikację.

Aby uzyskać sygnatury czasowe dla każdego elementu DataPoint, najpierw oblicz sygnaturę czasową uruchamiania, jak pokazano w tym przykładzie:

val bootInstant =
    Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())

Wartość tę można przekazać do funkcji getStartInstant() lub getEndInstant().

Przywróć rejestracje po uruchomieniu

Pasywne rejestracje danych nie są zachowywane po ponownym uruchomieniu. Aby odebrać dane po ponownym uruchomieniu urządzenia, utwórz rejestracje jeszcze raz za pomocą BroadcastReceiver, który nasłuchuje transmisji systemowej ACTION_BOOT_COMPLETED.

Nie próbuj bezpośrednio przywrócić rejestracji w odbiorniku. Zamiast tego przekaż tę funkcję instancji roboczej WorkManager. Podczas uruchamiania urządzenia usługi zdrowotne mogą potrzebować co najmniej 10 sekund na potwierdzenie pasywnego żądania rejestracji danych. Może to spowodować przekroczenie dozwolonego czasu wykonywania instrukcji BroadcastReceiver. Natomiast w przypadku WorkManager instancji roboczych limit wykonywania wynosi 10 minut.

Ten fragment kodu pokazuje, jak może wyglądać element BroadcastReceiver:

class StartupReceiver : BroadcastReceiver() {

   override fun onReceive(context: Context, intent: Intent) {
       if (intent.action != Intent.ACTION_BOOT_COMPLETED) return


       // TODO: Check permissions first
       WorkManager.getInstance(context).enqueue(
           OneTimeWorkRequestBuilder<RegisterForPassiveDataWorker>().build()
       )
   }
}

class RegisterForPassiveDataWorker(
   private val appContext: Context,
   workerParams: WorkerParameters
) : Worker(appContext, workerParams) {

   override fun doWork(): Result {
       runBlocking {
           HealthServices.getClient(appContext)
                .passiveMonitoringClient
                .setPassiveListenerCallback(...)
       }
       return Result.success()
   }
}

Aby system mógł wykonywać ten kod po uruchomieniu urządzenia, wprowadź 2 zmiany w pliku AndroidManifest.xml.

Najpierw dodaj to uprawnienie jako uprawnienia podrzędne uprawnień <manifest>:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

Następnie dodaj ten filtr intencji odbiorcy jako element podrzędny elementu <application>:

<receiver
    android:name=".StartupReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

Stan aktywności

Klient pasywny może też udostępniać ogólne informacje o stanie użytkownika, np. o tym, czy użytkownik uśpił. Aby otrzymywać te powiadomienia, wykonaj te czynności:

  1. Poproś o uprawnienie ACTIVITY_RECOGNITION.
  2. Wywołaj setShouldUserActivityInfoBeRequested(true) w kreatorze PassiveListenerConfig.

Zastąp metodę onUserActivityInfoReceived() w wywołaniu zwrotnym lub usłudze i użyj zwróconego UserActivityInfo, jak pokazano w tym przykładzie:

override fun onUserActivityInfoReceived(info: UserActivityInfo) {
    val stateChangeTime: Instant = info.stateChangeTime // may be in the past!
    val userActivityState: UserActivityState = info.userActivityState
    if (userActivityState == UserActivityState.USER_ACTIVITY_ASLEEP) {
        // ...
    }
}

Cele pasywne

Możesz skonfigurować klienta pasywnego, aby powiadamiał aplikację o osiągnięciu celów pasywnych,np. gdy użytkownik zrealizuje 10 tys. kroków w ciągu dnia.

W tym celu utwórz cel w następujący sposób:

val dailyStepsGoal by lazy {
    val condition = DataTypeCondition(
        dataType = DataType.STEPS_DAILY,
        threshold = 10_000, // Trigger every 10000 steps
        comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
    )
    PassiveGoal(condition)
}

Dodaj ten cel do celu (PassiveListenerConfig), jak pokazano w tym przykładzie:

val passiveListenerConfig = PassiveListenerConfig.builder()
    .setDailyGoals(setOf(dailyStepsGoal))
    .build()

Zastąp metodę onGoalCompleted() w wywołaniu zwrotnym lub usłudze i użyj zwróconego PassiveGoal, jak pokazano w tym przykładzie:

override fun onGoalCompleted(goal: PassiveGoal) {
    when (goal.dataTypeCondition.dataType) {
        DataType.STEPS_DAILY -> {
            // ...
        }
    }
}