수동적 데이터 업데이트

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

수동적 데이터 업데이트는 백그라운드에서 건강 관리 서비스 데이터를 모니터링해야 하는 앱에 적합합니다. 이는 데이터 업데이트가 자주 발생하지 않고 긴 시간에 걸쳐 간간이 발생할 수 있는 장기 지속 환경을 위한 것입니다. 수동적 데이터 업데이트는 업데이트가 전송될 때 앱이 사용 중이거나 실행 중이 아닌 경우 사용할 수 있습니다. 이러한 API를 사용하면 모든 데이터 포인트를 수신할지, 특정 DataType과 관련된 데이터 포인트만 수신할지 선택할 수 있습니다.

GitHub의 수동적 데이터수동적 목표 샘플을 참고하세요.

종속 항목 추가

건강 관리 서비스에 종속 항목을 추가하려면 프로젝트에 Google Maven 저장소를 추가해야 합니다. 자세한 내용은 Google의 Maven 저장소를 참고하세요.

그런 다음 모듈 수준 build.gradle 파일에 다음 종속 항목을 추가합니다.

Groovy

dependencies {
    implementation "androidx.health:health-services-client:1.0.0-alpha03"
}

Kotlin

dependencies {
    implementation("androidx.health:health-services-client:1.0.0-alpha03")
}

앱이 건강 관리 서비스와 상호작용할 수 있도록 AndroidManifest.xml 파일의 manifest 태그 내부에 다음을 추가합니다. 자세한 내용은 패키지 공개 상태를 참고하세요.

<queries>
    <package android:name="com.google.android.wearable.healthservices" />
</queries>

기능 확인

데이터 업데이트를 등록하기 전에 기기에서 앱에 필요한 백그라운드 데이터 유형을 제공할 수 있는지 확인합니다. 기능을 미리 확인하면 특정 기능을 사용 설정 또는 사용 중지하거나, 앱의 UI를 수정하여 사용할 수 없는 기능을 보완할 수 있습니다.

Kotlin

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 in capabilities.supportedDataTypesEvents
}

자바

HealthServicesClient healthClient = HealthServices.getClient(this /*context*/);
PassiveMonitoringClient passiveClient = healthClient.getPassiveMonitoringClient();

ListenableFuture<PassiveMonitoringCapabilities> capabilitiesFuture = passiveClient.getCapabilities();
Futures.addCallback(capabilitiesFuture,
        new FutureCallback<PassiveMonitoringCapabilities>() {
            @Override
            public void onSuccess(@Nullable PassiveMonitoringCapabilities result) {
                supportsHeartRate = result
                        .getSupportedDataTypesPassiveMonitoring()
                        .contains(DataType.HEART_RATE_BPM)
                supportsStepsEvent = result
                        .supportedDataTypesEvents()
                        .contains(DataType.STEPS)
            }

            @Override
            public void onFailure(Throwable t) {
                // display an error
            }
        },
        ContextCompat.getMainExecutor(this /*context*/));

수동적 데이터 수신

백그라운드에서 데이터 업데이트를 받으려면 PassiveMonitoringClient를 사용하세요. 앱의 AndroidManifest.xml에는 BroadcastReceiver가 선언되어 있어야 합니다. 건강 관리 서비스의 업데이트를 수신하도록 등록하면 이 수신기로 전송됩니다.

건강 관리 서비스는 업데이트를 일괄 처리하므로 다른 유형의 데이터 포인트나 같은 유형의 여러 데이터 포인트를 수신할 수 있습니다. 또한 데이터 포인트와 사용자 활동 상태는 이전에 발생한 이벤트를 반영할 수 있습니다. 예를 들어 사용자가 잠든 상태인지 감지하는 데 다소 시간이 걸릴 수 있으며 사용자 활동 업데이트는 소급되어 게시됩니다. 이러한 객체에 포함된 타임스탬프를 사용하여 이를 적절히 평가합니다.

onReceive 내에서 PassiveMonitoringUpdate.fromIntent(intent)를 사용하여 데이터의 압축을 해제합니다. 반환된 PassiveMonitoringUpdate에는 요청된 측정의 데이터 포인트 목록이 포함되어 있습니다. 또한 다음 예와 같이 사용자의 활동 상태 변경사항과 이러한 변경이 발생한 시점을 설명하는 UserActivityInfo 객체 목록도 포함되어 있습니다.

class BackgroundDataReceiver : BroadcastReceiver() {
   override fun onReceive(context: Context, intent: Intent) {
       // Check that the Intent is for passive data
       if (intent?.action != PassiveMonitoringUpdate.ACTION_DATA) {
           return
       }
       val update = PassiveMonitoringUpdate.fromIntent(intent) ?: return

       // List of available data points
       val dataPoints = update.dataPoints

       // List of available user state info
       val userActivityInfoList = update.userActivityInfoUpdates
   }
}

<application> 태그 내에서 AndroidManifest.xml에 다음 수신기를 추가합니다.

<receiver
   android:name=".BackgroundDataReceiver"
   android:exported="true">
   <intent-filter>
       <action android:name="hs.passivemonitoring.DATA" />
   </intent-filter>
</receiver>

BroadcastReceiver를 만든 후에는 PassiveMonitoringClient를 사용하여 업데이트를 등록하고 PassiveMonitoringConfig를 제공합니다. 데이터 수신을 중지하려면 등록을 취소하세요.

등록은 앱에 연결되며 각 앱은 한 번에 최대 1회 등록만 허용됩니다. 두 번 이상 등록하면 이전 등록이 대체됩니다. 즉, 등록에는 수신하려는 모든 데이터 유형이 포함되어야 하고 BroadcastReceiver는 이러한 모든 유형을 처리해야 합니다.

val dataTypes = setOf(DataType.HEART_RATE_BPM, DataType.STEPS)
val config = PassiveMonitoringConfig.builder()
   .setDataTypes(dataTypes)
   .setComponentName(ComponentName(context, BackgroundDataReceiver::class.java))
   // To receive UserActivityState updates, ACTIVITY_RECOGNITION permission is required.
   .setShouldIncludeUserActivityState(true)
   .build()
lifecycleScope.launch {
   HealthServices.getClient(context)
       .passiveMonitoringClient
       .registerDataCallback(config)
       .await()
}

수동적 데이터 사용

PassiveMonitoringUpdatedataPoints 속성에는 구독한 모든 유형의 데이터 포인트 목록이 포함되어 있습니다. 다음 예와 같이 부팅 타임스탬프를 먼저 계산하여 각 DataPoint의 타임스탬프를 가져옵니다.

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

그런 다음 이 값을 각 데이터 포인트의 getStartInstant() 또는 getEndInstant()와 함께 적절하게 사용할 수 있습니다.

UserActivityState 정보 사용

PassiveMonitoringUpdate는 사용자가 자고 있는지 운동하고 있는지 등 사용자 상태에 관한 대략적인 정보를 제공할 수 있습니다. 이러한 업데이트를 받으려면 다음 단계를 따르세요.

  1. ACTIVITY_RECOGNITION 권한이 부여되었는지 확인합니다.
  2. PassiveMonitoringConfig 빌더에서 setShouldIncludeUserActivityState(true)를 설정합니다.

userActivityInfoUpdates 속성은 전환과 연결된 상태 및 타임스탬프 목록을 반환합니다. 마지막 PassiveMonitoringUpdate 이후 상태 변경사항이 발생하지 않았으면 반환된 목록은 비어 있을 수 있습니다.

// Inspect the last reported state change, if present
passiveMonitoringUpdate.userActivityInfoUpdates.lastOrNull()?.let { userActivityInfo ->
   // When the transition to this state took place
   val stateChangeInstant = userActivityInfo.stateChangeTime

   // The high-level state of the user, e.g. USER_ACTIVITY_ASLEEP, USER_ACTIVITY_EXERCISE
   val state = userActivityInfo.userActivityState

   if (state == UserActivityState.USER_ACTIVITY_EXERCISE) {
       // Obtain info about the exercise and whether it is owned by the app
       val exerciseInfo = userActivityInfo.exerciseInfo
   }
}

수동적 목표 구독

수동적 목표 만들기 및 등록은 데이터 등록과 유사한 프로세스를 따릅니다. 그러나 데이터 등록과 달리 각 이벤트는 별도의 등록이며 앱은 여러 수동적 목표를 등록할 수 있습니다. 이벤트가 동일한 앱에서 요청한 이전 이벤트와 일치하는 경우에만 등록이 대체됩니다.

<application> 태그 내에서 AndroidManifest.xml 파일에 다음을 추가합니다.

<receiver
   android:name=".BackgroundGoalsReceiver"
   android:exported="true">
   <intent-filter>
       <action android:name="hs.passivemonitoring.GOAL" />
   </intent-filter>
</receiver>

등록할 때가 되면 관심 있는 이벤트를 정의하고 각 이벤트를 등록합니다. 이벤트 등록을 취소하려는 경우 동일한 이벤트 정의를 사용합니다.

PassiveGoals는 일회성 목표일 수도 있고 반복되도록 설정할 수도 있습니다. 다음 예는 1,000걸음에 도달할 때의 일회성 목표입니다. 그러나 TriggerType을 REPEATED로 변경하면 이벤트가 1,000걸음마다 브로드캐스트됩니다.

DAILY_STEPS 및 DAILY_CALORIES와 같은 일일 측정항목의 경우 REPEATED로 설정하면 목표가 하루에 한 번 트리거됩니다. 예를 들어 DAILY_STEPS 목표 1만 걸음은 2만 걸음을 걷는 경우 두 번 트리거되지 않으며 현지 시각으로 자정에 재설정됩니다.

// Create a goal for when 1000 steps are reached.
val thousandStepGoal = PassiveGoal(
   DataTypeCondition(
       DataType.STEPS,
       Value.ofLong(1000),
       ComparisonType.GREATER_THAN_OR_EQUAL),
   PassiveGoal.TriggerType.ONCE
)
lifecycleScope.launch {
    passiveClient.registerPassiveGoalCallback(
        thousandStepGoal,
        ComponentName(context, BackgroundGoalReceiver::class.java)
    ).await()
}

상응하는 수신기 코드에서 먼저 인텐트 작업을 확인하여 PassiveGoal에 관한 것인지 확인합니다. 그런 다음 인텐트에서 PassiveGoal을 재구성하여 이벤트를 발생시킨 기준을 파악합니다.

class BackgroundGoalReceiver : BroadcastReceiver() {
   override fun onReceive(context: Context, intent: Intent) {
       // Check that the Intent is for a passive goal
       if (intent?.action != PassiveGoal.ACTION_GOAL) {
           return
       }
       // Obtain the goal to determine which goal caused the event
       val goal = PassiveGoal.fromIntent(intent) ?: return
       // Check against goals defined by the app
       if (goal == thousandStepGoal) {
           // Take appropriate action on reaching goal
       }
   }
}

부팅 후 등록 복원

수동적 데이터 및 목표 등록은 기기를 다시 시작하면 유지되지 않습니다. 기기를 다시 시작할 때 수동적 데이터 등록을 유지해야 하는 경우 ACTION_BOOT_COMPLETED 시스템 브로드캐스트를 수신하는 BroadcastReceiver를 사용하면 됩니다.

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

기기가 켜져 있는 동안 데이터를 등록할 때 지연(10초 이상)이 있을 수 있습니다. 이는 허용되는 BroadcastReceiver 실행 시간을 초과할 수도 있습니다. 따라서 바로 데이터를 등록하는 대신 WorkManager를 사용하여 등록이 나중에 이루어지도록 예약합니다. 자세한 내용은 WorkManager 시작하기를 참고하세요.

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
               .registerDataCallback(...)
               .await()
       }
       return Result.success()
   }
}

<application> 태그 내에서 AndroidManifest.xml에 다음 수신기를 추가합니다.

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