Monitorowanie danych w tle

Pasywne aktualizacje danych są odpowiednie dla aplikacji, które mają monitorować usługi medyczne. i przetwarzanie danych w tle. Sprawdzają się w przypadkach użycia, które obejmują godziny, dni lub jeszcze dłużej. Jeśli musisz przechowywać lub przetwarzać dane dotyczące zdrowia, gdy aplikacja nie jest bieganie, a użytkownik nie jest wyraźnie zaangażowany w ćwiczenie, użyj Pasywny klient usługi Health Service.

Przykłady pasywnego wykorzystywania danych znajdziesz tutaj: Dane pasywne oraz Cele pasywne na GitHubie.

Dodaj zależności

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

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

Groovy

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

Kotlin

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

Sprawdź możliwości

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

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
}

Rejestracja w celu korzystania z danych pasywnych

Pasywne dane możesz otrzymywać przy użyciu usługi, wywołania zwrotnego lub obu tych metod. O umożliwia aplikacji odbieranie danych w tle, gdy żadna część Aplikacja jest widoczna na pierwszym planie. Przy odbiorze danych w tle wysyłane partiami. Wywołanie zwrotne otrzymuje dane z nieco większą szybkością, ale tylko wtedy, gdy aplikacja jest uruchomiona i wywołanie zwrotne zostanie powiadomione.

Niezależnie od wybranej metody najpierw utwórz PassiveListenerConfig określający typy danych, które chcesz otrzymywać, jak w tym przykładzie:

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

Aby odebrać dane za pomocą wywołania zwrotnego, zdefiniuj i zarejestruj wywołanie zwrotne, tak jak to pokazano w następujący przykład:

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 przebiega podobnie, ale zamiast tworzyć klasę pochodzącą z PassiveListenerCallback, pochodzą od PassiveListenerService, jak pokazano w następujący przykład:

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

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

Następnie zadeklaruj tę usługę w pliku AndroidManifest.xml. Wymaganie stanu zdrowia Uprawnienie dotyczące usług, które daje pewność, że powiązanie można utworzyć tylko przez usługi zdrowotne do usługi:

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

Interpretacja czasu

Dane, które otrzymujesz z Usług zdrowotnych, są grupowane, więc możesz otrzymywać punktów różnego typu lub wiele punktów danych tego samego typu w jednym wsad. Używaj sygnatur czasowych zawartych w tych obiektach, a nie czasu były otrzymywane przez aplikację, aby można było określić właściwą kolejność zdarzeń.

Uzyskuj sygnatury czasowe dla każdego urządzenia DataPoint, najpierw obliczając sygnaturę czasową uruchamiania jak w tym przykładzie:

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

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

Przywróć rejestracje po uruchomieniu

Pasywne rejestracje danych nie są zachowywane po ponownym uruchomieniu. Aby odebrać dane po ponowne uruchomienie urządzenia, odtwórz rejestracje przy użyciu urządzenia BroadcastReceiver, które nasłuchuje ACTION_BOOT_COMPLETED. komunikat systemowy.

Nie próbuj bezpośrednio przywracać rejestracji w odbiorniku. Zamiast tego: przekazać tę funkcję do instancji roboczej WorkManager. Gdy urządzenie się uruchamia, Usługi zdrowotne mogą potrwać co najmniej 10 sekund i zaakceptować pasywne żądanie rejestracji danych. Może to spowodować przekroczenie dozwolony czas wykonania instancji BroadcastReceiver. Dla kontrastu: WorkManager pracownicy Limit wykonywania to 10 minut.

Ten fragment kodu pokazuje, jak może wyglądać pole 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 uruchomił ten kod po włączeniu urządzenia, skonfiguruj dwie zmiany w pliku AndroidManifest.xml.

Najpierw dodaj to uprawnienie jako podrzędne wobec <manifest>:

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

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

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

Stan aktywności

Pasywny klient może też dostarczać ogólne informacje o stanie użytkownika, takie jak czy użytkownik śpi. Aby je otrzymywać:

  1. Poproś o uprawnienia ACTIVITY_RECOGNITION.
  2. Zadzwoń na nr setShouldUserActivityInfoBeRequested(true) w Kreator PassiveListenerConfig.

Zastąp metodę onUserActivityInfoReceived() w wywołaniu zwrotnym lub w 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ć pasywnego klienta, aby powiadamiał aplikację o celach pasywnych realizowanych przez użytkownika, np. wykonujący 10 000 kroków dziennie.

W tym celu utwórz cel zgodnie z poniższym przykładem:

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 PassiveListenerConfig, jak pokazano poniżej przykład:

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

Zastąp metodę onGoalCompleted() w wywołaniu zwrotnym lub w 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 -> {
            // ...
        }
    }
}
.