Ler dados brutos

O exemplo abaixo mostra como ler dados brutos do fluxo de trabalho comum.

Ler dados

A Conexão Saúde permite que os apps leiam dados do repositório quando estão em primeiro e segundo plano:

  • Leituras em primeiro plano: normalmente, você pode ler dados da Conexão Saúde quando o app está em primeiro plano. Nesses casos, considere usar um serviço em primeiro plano para executar essa operação caso o usuário ou o sistema coloque seu app em segundo plano durante uma operação de leitura.

  • Leituras em segundo plano: ao solicitar uma permissão extra do usuário, você pode ler dados depois que o usuário ou o sistema coloca o app em segundo plano. Confira o exemplo completo de leitura em segundo plano.

O tipo de dados "passos" no Conexão Saúde captura o número de passos que um usuário deu entre as leituras. A contagem de passos representa uma medida comum nas plataformas de saúde, condicionamento físico e bem-estar. A Conexão Saúde permite ler e gravar dados de contagem de passos.

Para ler registros, crie uma ReadRecordsRequest e forneça quando você chamar readRecords.

O exemplo abaixo mostra como ler dados de contagem de passos de um usuário em um determinado período. Para um exemplo mais longo com SensorManager, consulte o guia de dados de contagem de passos.

suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.readRecords(
            ReadRecordsRequest(
                StepsRecord::class,
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
            )
        )
        for (record in response.records) {
            // Process each record
        }
    } catch (e: Exception) {
        // Run error handling here
    }
}

Você também pode ler seus dados de maneira agregada usando aggregate.

suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
            )
        )
        // The result may be null if no data is available in the time range
        val stepCount = response[StepsRecord.COUNT_TOTAL]
    } catch (e: Exception) {
        // Run error handling here
    }
}

Ler passos em dispositivos móveis

Com o Android 14 (nível 34 da API) e a extensão do SDK versão 20 ou mais recente, a Conexão Saúde oferece contagem de passos no dispositivo. Se algum app tiver recebido a permissão READ_STEPS, o Conexão Saúde vai começar a capturar etapas do dispositivo Android, e os usuários vão ver os dados de etapas adicionados automaticamente às entradas de Etapas do Conexão Saúde.

Para verificar se a contagem de passos no dispositivo está disponível, confira se o dispositivo está executando o Android 14 (API de nível 34) e tem pelo menos a versão 20 da extensão do SDK. Use o seguinte código:

val isStepTrackingAvailable =
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
        SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 20

Os passos capturados pela Conexão Saúde têm o DataOrigin definido como o nome do pacote android. Se o app apenas ler contagens de passos agregadas usando aggregate e não filtrar por DataOrigin, as etapas no dispositivo serão incluídas automaticamente no total.

Se o app precisar ler etapas no dispositivo ou mostrar dados de etapas divididos por aplicativo ou dispositivo de origem, você poderá consultar registros em que o DataOrigin é android. Se o app mostrar atribuição para dados de etapas, atribua os dados do pacote Android ao dispositivo atual. Para isso, use um rótulo como "Seu smartphone", recupere o nome do dispositivo com Settings.Global.getString(resolver, Settings.Global.DEVICE_NAME) ou inspecione o campo Device nos metadados do registro.

O exemplo a seguir mostra como ler dados agregados de contagem de passos em dispositivos móveis filtrando a origem de dados android:

suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                dataOriginFilter = setOf(DataOrigin("android"))
            )
        )
        // The result may be null if no data is available in the time range
        val stepCount = response[StepsRecord.COUNT_TOTAL]
    } catch (e: Exception) {
        // Run error handling here
    }
}

Contagem de passos no dispositivo

Mais detalhes sobre o recurso de contagem de passos no dispositivo:

  • Uso de sensores: o app Conexão Saúde usa o sensor TYPE_STEP_COUNTER do SensorManager. Esse sensor é otimizado para baixo consumo de energia, o que o torna ideal para o rastreamento contínuo de passos em segundo plano.
  • Granularidade dos dados: para conservar a duração da bateria, os dados de passos geralmente são agrupados e gravados no banco de dados da Conexão Saúde no máximo uma vez por minuto.
  • Atribuição: como mencionado anteriormente, todas as etapas registradas por esse recurso no dispositivo são atribuídas ao nome do pacote android no DataOrigin.
  • Ativação: o mecanismo de contagem de passos no dispositivo só fica ativo quando pelo menos um aplicativo no dispositivo recebe a permissão READ_STEPS na Conexão Saúde.

Exemplo de leitura em segundo plano

Para ler dados em segundo plano, declare a seguinte permissão no arquivo de manifesto:

<application>
  <uses-permission android:name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND" />
...
</application>

O exemplo abaixo mostra como ler dados de contagem de passos em segundo plano para um usuário em um determinado período usando WorkManager:

class ScheduleWorker(private val appContext: Context, workerParams: WorkerParameters):
    CoroutineWorker(appContext, workerParams) {

    override suspend fun doWork(): Result {
        // Read data and process it.
        ...

        // Return success indicating successful data retrieval
        return Result.success()
    }
}

if (healthConnectClient
    .features
    .getFeatureStatus(
    HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_IN_BACKGROUND
    ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {

    // Check if necessary permission is granted
    val grantedPermissions = healthConnectClient.permissionController.getGrantedPermissions()

    if (PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND !in grantedPermissions) {
        // Perform read in foreground
        ...
    } else {
        // Schedule the periodic work request in background
        val periodicWorkRequest = PeriodicWorkRequestBuilder<ScheduleWorker>(1, TimeUnit.HOURS)
            .build()

        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "read_health_connect",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicWorkRequest
        )
    }
} else {
  // Background reading is not available, perform read in foreground
  ...
}

O parâmetro ReadRecordsRequest tem um valor padrão pageSize de 1000. Se o número de registros em um único readResponse exceder o pageSize da solicitação, itere todas as páginas da resposta para recuperar todos os registros usando pageToken. No entanto, tome cuidado para evitar problemas de limitação de taxa.

Exemplo de leitura de pageToken

Recomendamos usar pageToken para ler registros e recuperar todos os dados disponíveis do período solicitado.

O exemplo a seguir mostra como ler todos os registros até que todos os tokens de página tenham sido usados:

val type = HeartRateRecord::class
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofDays(7))

try {
    var pageToken: String? = null
    do {
        val readResponse =
            healthConnectClient.readRecords(
                ReadRecordsRequest(
                    recordType = type,
                    timeRangeFilter = TimeRangeFilter.between(
                        startTime,
                        endTime
                    ),
                    pageToken = pageToken
                )
            )
        val records = readResponse.records
        // Do something with records
        pageToken = readResponse.pageToken
    } while (pageToken != null)
} catch (quotaError: IllegalStateException) {
    // Backoff
}

Para informações sobre práticas recomendadas ao ler grandes conjuntos de dados, consulte Planejar para evitar a limitação de taxa.

Ler dados gravados anteriormente

Se um app tiver gravado registros na Conexão Saúde antes, ele poderá ler dados históricos. Isso se aplica a cenários em que o app precisa ser sincronizado novamente com a Conexão Saúde depois que o usuário o reinstala.

Algumas restrições de leitura se aplicam:

  • Para Android 14 e versões mais recentes

    • Não há limite histórico para um app ler os próprios dados.
    • Limite de 30 dias para um app ler outros dados.
  • Para Android 13 e versões anteriores

    • Limite de 30 dias para leitura de dados por apps.

Para remover as restrições, peça uma permissão de leitura.

Para ler dados históricos, é necessário indicar o nome do pacote como um objeto DataOrigin no parâmetro dataOriginFilter da ReadRecordsRequest.

O exemplo abaixo mostra como indicar o nome de um pacote ao ler registros de frequência cardíaca:

try {
    val response =  healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = HeartRateRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
            dataOriginFilter = setOf(DataOrigin("com.my.package.name"))
        )
    )
    for (record in response.records) {
        // Process each record
    }
} catch (e: Exception) {
    // Run error handling here
}

Ler dados com mais de 30 dias

Por padrão, todos os aplicativos podem ler dados da Conexão Saúde registrados até 30 dias antes da permissão ser concedida.

Se você precisar estender as permissões de leitura além de qualquer uma das restrições padrão, solicite o PERMISSION_READ_HEALTH_DATA_HISTORY. Caso contrário, sem essa permissão, uma tentativa de ler registros com mais de 30 dias resulta em um erro.

Histórico de permissões de um app excluído

Se um usuário excluir seu app, todas as permissões, incluindo a de histórico, serão revogadas. Se o usuário reinstalar o app e conceder a permissão novamente, as mesmas restrições padrão serão aplicadas, e o app poderá ler dados da Conexão Saúde registrados até 30 dias antes dessa nova data.

Por exemplo, suponha que o usuário exclua seu app em 10 de maio de 2023 e o reinstale em 15 de maio de 2023, concedendo permissões de leitura. A data mais antiga de que o app poderá ler dados por padrão é 15 de abril de 2023.

Tratar exceções

A plataforma Conexão Saúde gera exceções padrão para operações CRUD quando um problema é encontrado. O app precisa capturar e processar cada uma dessas exceções conforme adequado.

Cada método no HealthConnectClient lista as exceções que podem ser geradas. Em geral, o app precisa lidar com as seguintes exceções:

Tabela 1: exceções da Conexão Saúde e práticas recomendadas
Exceção Descrição Prática recomendada
IllegalStateException Ocorreu um dos seguintes cenários:

  • O serviço da plataforma Conexão Saúde não está disponível.
  • A solicitação não é uma construção válida. Por exemplo, uma solicitação agregada em buckets periódicos em que um objeto Instant é usado para timeRangeFilter.

Gerencie possíveis problemas com as entradas antes de fazer uma solicitação. De preferência, atribua valores a variáveis ou use elas como parâmetros em uma função personalizada em vez de usá-las diretamente nas solicitações para aplicar estratégias de tratamento de erros.
IOException Ocorreram problemas ao ler e gravar dados do disco. Para evitar esse problema, confira algumas sugestões:

  • Faça backup de qualquer entrada do usuário.
  • Resolva qualquer problema encontrado durante as operações de gravação em massa. Por exemplo, verifique se o processo resolve o problema e realize as operações restantes.
  • Aplique novas tentativas e estratégias de espera para lidar com problemas de solicitação.

RemoteException Ocorreram erros na comunicação com o serviço subjacente ao qual o SDK se conecta.

Por exemplo, seu app está tentando excluir um registro com um determinado uid. No entanto, a exceção é gerada após o app descobrir, ao verificar no serviço subjacente, que o registro não existe.
Para evitar esse problema, confira algumas sugestões:

  • Faça sincronizações regulares entre o repositório de dados do app e a plataforma Conexão Saúde.
  • Aplique novas tentativas e estratégias de espera para lidar com problemas de solicitação.

SecurityException Há problemas encontrados quando as solicitações exigem permissões que não são concedidas. Para evitar isso, verifique se você declarou o uso dos tipos de dados do Conexão Saúde para o app publicado. Além disso, é necessário declarar as permissões do Conexão Saúde no arquivo de manifesto e na sua atividade.