Se você quiser criar uma experiência de treino no seu app, use o Conexão Saúde para fazer o seguinte:
- Gravar sessões de exercícios
- Gravar trajetos de treino
- Gravar métricas de treino, como frequência cardíaca, velocidade e distância
- Ler dados de treino de outros apps
Este guia descreve como criar esses recursos de treino, abordando tipos de dados, execução em segundo plano, permissões, fluxos de trabalho recomendados e práticas recomendadas.
Visão geral: como criar um rastreador de treino abrangente
Você pode criar uma experiência abrangente de acompanhamento de treinos usando a Conexão Saúde seguindo estas etapas principais:
- Implementar corretamente as permissões com base nas permissões de saúde.
- Gravação de sessões usando
ExerciseSessionRecord. - Gravar dados de treino de forma consistente durante a sessão.
- Gerenciar a execução em segundo plano corretamente para verificar a captura contínua de dados.
- Leitura dos dados da sessão para resumos e análises pós-treino.
Esse fluxo de trabalho permite a interoperabilidade com outros apps da Conexão Saúde e verifica o acesso aos dados controlado pelo usuário.
Antes de começar
Antes de implementar recursos de treino:
- Integre a Conexão Saúde usando a dependência adequada.
- Crie uma instância
HealthConnectClient. - Verifique se o app implementa fluxos de permissão de execução com base nas permissões de saúde.
- Se o fluxo de trabalho usar GPS, configure a permissão de localização e um serviço em primeiro plano.
Principais conceitos
A Conexão Saúde representa dados de treino usando alguns componentes principais. Um
ExerciseSessionRecord funciona como o registro central de um treino,
contendo detalhes como horários de início ou término e tipo de exercício. Durante uma
sessão, vários tipos de dados, como HeartRateRecord ou SpeedRecord, podem
ser gravados. Para atividades ao ar livre, o ExerciseRoute armazena dados de GPS, que são vinculados à sessão correspondente.
Sessões de exercícios
ExerciseSessionRecord é o registro central dos dados de treino, representando uma
única sessão de treino. Cada registro armazena:
startTimeendTimeexerciseType- Metadados opcionais da sessão (título, observações)
Um ExerciseSessionRecord também pode conter trajetos de exercícios, voltas e segmentos como parte dos dados. Além disso, outros tipos de dados, como
HeartRateRecord ou SpeedRecord, podem ser gravados durante uma sessão e
associados a ela.
Tipos de dados associados
Os dados associados a sessões de treino são representados por tipos de registros individuais. Os tipos comuns incluem:
HeartRateRecord: representa uma série de medições de frequência cardíaca.SpeedRecord: representa uma série de medições de velocidade.DistanceRecord: representa a distância percorrida entre as leituras.TotalCaloriesBurnedRecord: representa o total de calorias queimadas entre as leituras.ElevationGainedRecord: representa a elevação ganha entre as leituras.StepsCadenceRecord: representa a cadência de etapas entre as leituras.PowerRecord: representa a potência entre as leituras, comum em atividades como ciclismo.
Para conferir uma lista completa de tipos de dados, consulte Tipos de dados do Conexão Saúde.
Trajetos de exercícios
É possível associar um trajeto a treinos ao ar livre usando o ExerciseRoute. As rotas
consistem em objetos ExerciseRoute.Location sequenciais, cada um contendo:
- Latitude e longitude
- Altitude opcional
- Ângulo opcional
- Informações de acurácia
- Carimbo de data/hora
Vincular rotas de sessão
Um ExerciseRoute contém os dados de localização sequenciais de uma sessão de exercícios. Ele não é tratado como um registro independente na Conexão Saúde. Em vez disso, você fornece dados de ExerciseRoute ao inserir ou atualizar um ExerciseSessionRecord.
Considerações de desenvolvimento
Os apps de monitoramento de exercícios geralmente precisam ser executados por longos períodos, muitas vezes em segundo plano quando a tela está desligada. Ao criar recursos de exercícios, é importante considerar como gerenciar a execução em segundo plano e solicitar as permissões necessárias para os dados de exercícios.
Execução em segundo plano
Os apps de treino geralmente são executados com a tela desligada. Nesse estado, use:
- Serviços em primeiro plano para amostragem de localização e sensores
WorkManagerpara gravação ou sincronização adiada- Estratégias de agrupamento para gravação regular de registros
Mantenha a continuidade mantendo o ID da sessão consistente em todas as gravações.
Permissões
Seu app precisa solicitar as permissões relevantes da Conexão Saúde antes de ler ou gravar dados de treino. As permissões comuns para treinos incluem sessões e rotas de exercícios, além de métricas como frequência cardíaca ou velocidade. Isso inclui o seguinte:
- Sessões de exercícios:permissões de leitura e gravação para
ExerciseSessionRecord. - Trajetos de exercícios:permissões de leitura e gravação para
ExerciseRoute. - Frequência cardíaca:permissões de leitura e gravação para
HeartRateRecord. - Velocidade:permissões de leitura e gravação para
SpeedRecord. - Distância:permissões de leitura e gravação para
DistanceRecord. - Calorias:permissões de leitura e gravação para
TotalCaloriesBurnedRecord. - Elevação ganha:permissões de leitura e gravação para
ElevationGainedRecord. - Cadência dos passos:permissões de leitura e gravação para
StepsCadenceRecord. - Energia:permissões de leitura e gravação para
PowerRecord. - Etapas:permissões de leitura e gravação para
StepsRecord.
Confira abaixo um exemplo de como solicitar várias permissões para uma sessão de treino que inclui dados de trajeto, frequência cardíaca, distância, calorias, velocidade e passos:
Depois de criar uma instância de cliente, seu app precisa solicitar permissões aos usuários. Eles precisam concedê-las ou negá-las a qualquer momento.
Para isso, crie um conjunto de permissões para os tipos de dados necessários. Verifique se as permissões no conjunto foram declaradas primeiro no manifesto do Android.
// Create a set of permissions for required data types
val PERMISSIONS =
setOf(
HealthPermission.getReadPermission(ExerciseSessionRecord::class),
HealthPermission.getWritePermission(ExerciseSessionRecord::class),
HealthPermission.getReadPermission(ExerciseRoute::class),
HealthPermission.getWritePermission(ExerciseRoute::class),
HealthPermission.getReadPermission(HeartRateRecord::class),
HealthPermission.getWritePermission(HeartRateRecord::class),
HealthPermission.getReadPermission(SpeedRecord::class),
HealthPermission.getWritePermission(SpeedRecord::class),
HealthPermission.getReadPermission(DistanceRecord::class),
HealthPermission.getWritePermission(DistanceRecord::class),
HealthPermission.getReadPermission(TotalCaloriesBurnedRecord::class),
HealthPermission.getWritePermission(TotalCaloriesBurnedRecord::class),
HealthPermission.getReadPermission(StepsRecord::class),
HealthPermission.getWritePermission(StepsRecord::class)
)
Use getGrantedPermissions para verificar se o app já tem as
permissões necessárias concedidas. Caso contrário, use
createRequestPermissionResultContract para solicitá-las. Isso mostra a tela de permissões da Conexão Saúde.
// Create the permissions launcher
val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract()
val requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted ->
if (granted.containsAll(PERMISSIONS)) {
// Permissions successfully granted
} else {
// Lack of required permissions
}
}
suspend fun checkPermissionsAndRun(healthConnectClient: HealthConnectClient) {
val granted = healthConnectClient.permissionController.getGrantedPermissions()
if (granted.containsAll(PERMISSIONS)) {
// Permissions already granted; proceed with inserting or reading data
} else {
requestPermissions.launch(PERMISSIONS)
}
}
Como os usuários podem conceder ou revogar permissões a qualquer momento, seu app precisa verificar as permissões sempre antes de usá-las e lidar com situações em que elas são perdidas.
Para solicitar permissões, chame a função checkPermissionsAndRun:
if (!granted.containsAll(PERMISSIONS)) {
requestPermissions.launch(PERMISSIONS)
// Check if required permissions are not granted, and return
}
// Permissions already granted; proceed with inserting or reading data
Se você precisar solicitar permissões para um único tipo de dado, como frequência cardíaca, inclua apenas esse tipo de dado no conjunto de permissões:
O acesso à frequência cardíaca é protegido pelas seguintes permissões:
android.permission.health.READ_HEART_RATEandroid.permission.health.WRITE_HEART_RATE
Para adicionar a capability de frequência cardíaca ao app, comece solicitando
permissões para o tipo de dado HeartRateRecord.
Confira a permissão necessária para poder gravar frequência cardíaca:
<application>
<uses-permission
android:name="android.permission.health.WRITE_HEART_RATE" />
...
</application>
Para ler a frequência cardíaca, solicite as seguintes permissões:
<application>
<uses-permission
android:name="android.permission.health.READ_HEART_RATE" />
...
</application>
Implementar uma sessão de treino
Nesta seção, descrevemos o fluxo de trabalho recomendado para registrar dados de treino.
Iniciar a sessão
Para criar um novo treino:
- Gere um ID de sessão exclusivo e verifique se ele é estável. Se o processo do app for encerrado e reiniciado, você precisará retomar o uso do mesmo ID para evitar sessões fragmentadas.
- Defina um
metadata.clientRecordIdpara evitar duplicatas durante novas tentativas de sincronização. - Escreva um
ExerciseSessionRecord: inclua o horário de início. - Comece a coletar dados de tipo e GPS somente depois que o registro da sessão for inicializado.
Exemplo:
val sessionId = UUID.randomUUID().toString()
val sessionClientId = UUID.randomUUID().toString()
val session = ExerciseSessionRecord(
id = sessionId,
exerciseType = ExerciseType.EXERCISE_TYPE_RUNNING,
startTime = Instant.now(),
endTime = null,
metadata = Metadata(clientRecordId = sessionClientId),
)
healthConnectClient.insertRecords(listOf(session))
Gravar trajetos de exercícios
Para saber mais sobre orientações de leitura, consulte Ler dados brutos.
Ao gravar um percurso de exercício, agrupe seus dados. Isso significa que, em vez de salvar cada ponto de GPS à medida que ele aparece, você coleta um grupo de pontos e salva todos de uma vez em uma única chamada.
Isso é importante porque toda vez que o app lê ou grava no Conexão Saúde, ele usa um pouco de bateria e poder de processamento.
O código a seguir mostra como gravar em lotes:
// 1. Create a list to hold your route locations
val routeLocations = mutableListOf<ExerciseRoute.Location>()
// 2. Add points to your list as the exercise happens
routeLocations.add(
ExerciseRoute.Location(
time = Instant.now(),
latitude = 37.7749,
longitude = -122.4194
)
)
// ... keep adding points over a period of time ...
// 3. Save the whole list at once (Batching)
val session = ExerciseSessionRecord(
startTime = startTime,
endTime = endTime,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
// We pass the whole list here
exerciseRoute = ExerciseRoute(routeLocations)
)
healthConnectClient.insertRecords(listOf(session))
Encerrar uma sessão
Depois de interromper a coleta de dados:
- Atualize o registro: seu app atualiza
ExerciseSessionRecordcom umendTime. - Finalize os dados: se quiser, calcule valores de resumo (como distância total ou ritmo médio) e grave-os como registros adicionais.
val finishedSession = session.copy(endTime = Instant.now())
healthConnectClient.updateRecords(listOf(finishedSession))
Como ler dados de treino
Os apps podem ler sessões de exercícios e os dados associados para resumir atividades,
fornecer insights de saúde ou sincronizar dados com um servidor externo. Por exemplo, é possível ler um ExerciseSessionRecord e consultar o HeartRateRecord ou DistanceRecord que ocorreu durante esse mesmo intervalo de tempo.
Se você precisar sincronizar dados de treino com um servidor de back-end ou manter o repositório de dados do app atualizado com a Conexão Saúde, use ChangeLogs. Isso permite recuperar uma lista de registros inseridos, atualizados ou excluídos desde um ponto específico no tempo, o que é mais eficiente do que rastrear manualmente as mudanças ou ler repetidamente todos os dados. Para mais informações, consulte Sincronizar dados com o app Conexão Saúde.
Ler sessões
Para ler sessões de exercício, use um ReadRecordsRequest com
ExerciseSessionRecord como o tipo. Normalmente, você filtra isso por um período específico.
suspend fun readExerciseSessions(
healthConnectClient: HealthConnectClient,
startTime: Instant,
endTime: Instant
) {
val response = healthConnectClient.readRecords(
ReadRecordsRequest(
recordType = ExerciseSessionRecord::class,
timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
)
)
for (exerciseRecord in response.records) {
// Process each session
val exerciseType = exerciseRecord.exerciseType
val notes = exerciseRecord.notes
}
}
Ler rotas
Embora os dados do ExerciseRoute sejam gravados como parte de uma sessão de exercícios, eles
precisam ser lidos separadamente. Use o método getExerciseRoute() com o ID da sessão para ler os dados da rota:
suspend fun readExerciseRoute(
healthConnectClient: HealthConnectClient,
exerciseSessionRecord: ExerciseSessionRecord
) {
// Check if the session has a route
val route = healthConnectClient.getExerciseRoute(
exerciseSessionRecordId = exerciseSessionRecord.metadata.id
)
when (route) {
is ExerciseRouteResponse.Success -> {
val locations = route.exerciseRoute.locations
for (location in locations) {
// Use latitude, longitude, and altitude
}
}
is ExerciseRouteResponse.NoData -> {
// Handle case where no route exists
}
is ExerciseRouteResponse.ConsentRequired -> {
// Handle case where permissions are missing
}
}
}
Ler tipos de dados
Para ler dados granulares específicos (como frequência cardíaca) que ocorreram durante uma sessão,
use startTime e endTime da sessão para filtrar a solicitação desse tipo de dados.
suspend fun readHeartRateData(
healthConnectClient: HealthConnectClient,
exerciseSession: ExerciseSessionRecord
) {
val response = healthConnectClient.readRecords(
ReadRecordsRequest(
recordType = HeartRateRecord::class,
timeRangeFilter = TimeRangeFilter.between(
exerciseSession.startTime,
exerciseSession.endTime
)
)
)
for (heartRateRecord in response.records) {
for (sample in heartRateRecord.samples) {
val bpm = sample.beatsPerMinute
}
}
}
Práticas recomendadas
Siga estas diretrizes para melhorar a confiabilidade dos dados e a experiência do usuário:
- Frequência de gravação
- Rastreamento ativo(em primeiro plano): para treinos ativos, grave os dados assim que eles ficarem disponíveis ou em um intervalo máximo de 15 minutos.
- Sincronização em segundo plano:use
WorkManagerpara gravações adiadas. Procure um intervalo de 15 minutos para equilibrar dados em tempo real e eficiência da bateria. - Loteamento:não grave cada evento do sensor individualmente. Divida suas solicitações. O Conexão Saúde processa até 1.000 registros por solicitação de gravação.
- Mantenha os IDs de sessão estáveis e exclusivos:use identificadores consistentes para suas sessões. Se uma sessão for editada ou atualizada, usar o mesmo ID vai impedir que ela seja tratada como um treino novo e separado.
- Use o agrupamento em lote para tipos de dados e pontos de rota:para reduzir a sobrecarga de entrada/saída e preservar a duração da bateria, agrupe seus pontos de dados em uma única chamada
insertRecordsem vez de gravar cada ponto individualmente. Evite gravar dados duplicados: use IDs de cliente. Ao criar registros, defina um
metadata.clientRecordId. O Conexão Saúde usa isso para identificar registros exclusivos. Se você tentar gravar um registro com umclientRecordIdque já existe, a Conexão Saúde vai ignorar o duplicado ou atualizar o registro atual em vez de criar um novo. Definir ummetadata.clientRecordIdé a maneira mais eficaz de evitar duplicatas durante novas tentativas de sincronização ou reinstalações de apps.val record = StepsRecord( count = 100, startTime = startTime, endTime = endTime, startZoneOffset = ZoneOffset.UTC, endZoneOffset = ZoneOffset.UTC, metadata = Metadata( // Use a unique ID from your own database clientRecordId = "daily_steps_2023_10_27_user_123" ) )Verifique os dados atuais:antes de sincronizar, consulte o período para saber se já existem registros do seu app.
Validar a precisão do GPS:filtre amostras de GPS de baixa precisão (por exemplo, pontos com um raio de alta precisão horizontal) antes de gravar no
ExerciseRoutepara verificar se o mapa está limpo e profissional.Verifique se os carimbos de data/hora não se sobrepõem:confira se uma nova sessão não começa antes da anterior terminar. Sessões sobrepostas podem causar conflitos nos painéis de condicionamento físico e nos cálculos de resumo.
Forneça justificativas claras para a permissão:use o fluxo
Permission.createIntentpara explicar por que seu app precisa de acesso a dados de saúde, como "Para mapear suas corridas e calcular a queima de calorias".Suporte para pausar e retomar:verifique se o app processa pausas corretamente. Quando um usuário pausa, pare de coletar pontos de rota e tipos de dados para que o ritmo e a duração médios permaneçam precisos.
Teste sessões de longa duração:monitore o consumo de bateria durante sessões de várias horas para verificar se o intervalo de agrupamento e o uso do sensor não descarregam o dispositivo.
Alinhe os carimbos de data/hora com as taxas dos sensores:faça com que os carimbos de data/hora dos registros correspondam à frequência real dos sensores (por exemplo, 1 Hz para GPS) para manter a alta fidelidade dos dados.
Teste
Para verificar a correção dos dados e uma experiência do usuário de alta qualidade, siga estas estratégias de teste e consulte a documentação oficial Testar principais casos de uso.
Ferramentas de verificação
- Caixa de ferramentas do app Conexão Saúde:use esse app complementar para inspecionar manualmente registros, excluir dados de teste e simular mudanças no banco de dados. Essa é a melhor maneira de verificar se seus registros estão sendo armazenados corretamente.
- Teste de unidade com
FakeHealthConnectClient:use a biblioteca de teste para verificar como o app lida com casos extremos, como revogação de permissão ou exceções de API, sem precisar de um dispositivo físico.
Checklist de qualidade
Arquitetura típica
Uma implementação de treino geralmente inclui:
| Componente | Gerencia |
|---|---|
| Controlador de sessão | Estado da sessão Timer Lógica de agrupamento em lotes Controladores de tipos de dados Amostragem de local |
| Camada de repositório (encapsula operações da Conexão Saúde): | Inserir sessão Inserir tipos de dados Inserir pontos de rota Ler resumos de sessão |
| Camada de interface (exibições): | Duração Tipos de dados em tempo real Prévia do mapa Cálculos de divisão Rastreamento de GPS em tempo real |
Solução de problemas
| Sintoma | Possível causa | Resolução |
|---|---|---|
| Rota não associada à sessão | O ID da sessão ou o período não correspondem. | Verifique se o ExerciseRoute está escrito com um intervalo de tempo que se enquadra totalmente na duração do ExerciseSessionRecord. Verifique se você está usando IDs consistentes ao referenciar a sessão mais tarde. Consulte Como gravar trajetos de exercícios. |
| Tipos de dados ausentes (por exemplo, frequência cardíaca) | Permissões de gravação ausentes ou filtros de período incorretos. | Verifique se você solicitou e se o usuário concedeu a permissão específica para o tipo de dados. Verifique se o ReadRecordsRequest usa um TimeRangeFilter que corresponde à sessão. Consulte Permissões. |
| A sessão não grava | Carimbos de data/hora sobrepostos. | A Conexão Saúde pode rejeitar registros que se sobrepõem a dados existentes do mesmo app. Valide se o startTime de uma nova sessão é posterior ao endTime da anterior. |
| Nenhum dado de GPS registrado | O serviço em primeiro plano foi encerrado ou está inativo. | Para coletar dados com a tela desligada, use um serviço em primeiro plano com o atributo foregroundServiceType="health" ou de local. |
| Registros duplicados aparecem | clientRecordId ausente |
Atribua um clientRecordId exclusivo no Metadata de cada registro. Isso permite que a Conexão Saúde faça a remoção de duplicidade se os mesmos dados forem gravados duas vezes durante uma nova tentativa de sincronização. Consulte as práticas recomendadas. |
Etapas comuns de depuração
- Verificar o estado da permissão:sempre chame
getPermissionStatus()antes de tentar uma operação de leitura ou gravação. Os usuários podem revogar permissões nas configurações do sistema a qualquer momento. - Verifique o modo de execução:se o app não estiver coletando dados em segundo plano, verifique se você declarou as permissões corretas em
AndroidManifest.xmle se o usuário não colocou o app no modo "Restrição de bateria".