Hướng dẫn này tương thích với Health Connect phiên bản 1.1.0-alpha11.
Health Connect cung cấp một loại dữ liệu bài tập có kế hoạch để cho phép các ứng dụng huấn luyện ghi kế hoạch huấn luyện và cho phép các ứng dụng tập luyện đọc kế hoạch huấn luyện. Bạn có thể đọc lại các bài tập đã ghi (bài tập) để phân tích hiệu suất theo cách riêng nhằm giúp người dùng đạt được mục tiêu huấn luyện.
Phạm vi cung cấp tính năng
Để xác định xem thiết bị của người dùng có hỗ trợ kế hoạch tập luyện trên Health Connect hay không, hãy kiểm tra trạng thái cung cấp củaFEATURE_PLANNED_EXERCISE
trên ứng dụng:
if (healthConnectClient
.features
.getFeatureStatus(
HealthConnectFeatures.FEATURE_PLANNED_EXERCISE
) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {
// Feature is available
} else {
// Feature isn't available
}
Hãy xem phần Kiểm tra phạm vi cung cấp của các tính năng để tìm hiểu thêm.
Các quyền bắt buộc
Quyền truy cập vào bài tập đã lên kế hoạch được bảo vệ bằng các quyền sau:
android.permission.health.READ_PLANNED_EXERCISE
android.permission.health.WRITE_PLANNED_EXERCISE
Để thêm chức năng bài tập có kế hoạch vào ứng dụng của bạn, hãy bắt đầu bằng cách yêu cầu quyền ghi cho kiểu dữ liệu PlannedExerciseSession
.
Dưới đây là quyền bạn cần khai báo để có thể ghi bài tập đã lên kế hoạch:
<application>
<uses-permission
android:name="android.permission.health.WRITE_PLANNED_EXERCISE" />
...
</application>
Để đọc dữ liệu bài tập đã lên kế hoạch, bạn cần yêu cầu các quyền sau:
<application>
<uses-permission
android:name="android.permission.health.READ_PLANNED_EXERCISE" />
...
</application>
Yêu cầu người dùng cấp quyền
Sau khi tạo một phiên bản ứng dụng, ứng dụng của bạn cần yêu cầu người dùng cấp quyền. Người dùng phải được phép cấp hoặc từ chối cấp quyền bất cứ lúc nào.
Để thực hiện việc này, hãy tạo một tập hợp quyền cho các kiểu dữ liệu bắt buộc. Trước tiên, bạn cần khai báo các quyền trong tập hợp này ở tệp kê khai Android.
// Create a set of permissions for required data types
val PERMISSIONS =
setOf(
HealthPermission.getReadPermission(HeartRateRecord::class),
HealthPermission.getWritePermission(HeartRateRecord::class),
HealthPermission.getReadPermission(PlannedExerciseSessionRecord::class),
HealthPermission.getWritePermission(PlannedExerciseSessionRecord::class),
HealthPermission.getReadPermission(ExerciseSessionRecord::class),
HealthPermission.getWritePermission(ExerciseSessionRecord::class)
)
Hãy sử dụng getGrantedPermissions
để xem ứng dụng của bạn đã được cấp các quyền cần thiết chưa. Nếu chưa, hãy sử dụng createRequestPermissionResultContract
để yêu cầu các quyền đó. Thao tác này sẽ hiện màn hình các quyền của Health Connect.
// 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)
}
}
Vì người dùng có thể cấp hoặc thu hồi quyền bất cứ lúc nào, nên ứng dụng của bạn cần kiểm tra định kỳ các quyền đã cấp và xử lý các tình huống khi mất quyền.
Các quyền liên quan
Kế hoạch luyện tập được liên kết với các phiên tập thể dục. Do đó, người dùng phải cấp quyền sử dụng từng loại bản ghi liên quan đến kế hoạch tập luyện để khai thác tối đa tính năng này của Health Connect.
Ví dụ: nếu một kế hoạch tập luyện đo nhịp tim của người dùng trong một loạt lượt chạy, thì nhà phát triển có thể cần khai báo và người dùng có thể cần cấp các quyền sau để ghi phiên tập thể dục và đọc kết quả để đánh giá sau này:
android.permission.health.READ_EXERCISE
android.permission.health.READ_EXERCISE_ROUTES
android.permission.health.READ_HEART_RATE
android.permission.health.WRITE_EXERCISE
android.permission.health.WRITE_EXERCISE_ROUTE
android.permission.health.WRITE_HEART_RATE
Tuy nhiên, thường thì ứng dụng tạo kế hoạch tập luyện và đánh giá hiệu suất dựa trên kế hoạch không giống với ứng dụng sử dụng kế hoạch tập luyện và ghi dữ liệu tập luyện thực tế. Tuỳ thuộc vào loại ứng dụng, bạn không cần đến tất cả các quyền đọc và ghi. Ví dụ: bạn có thể chỉ cần những quyền này cho từng loại ứng dụng:
Ứng dụng kế hoạch luyện tập | Ứng dụng Bài tập |
---|---|
WRITE_PLANNED_EXERCISE |
READ_PLANNED_EXERCISE |
READ_EXERCISE |
WRITE_EXERCISE |
READ_EXERCISE_ROUTES |
WRITE_EXERCISE_ROUTE |
READ_HEART_RATE |
WRITE_HEART_RATE |
Thông tin có trong bản ghi của một phiên tập thể dục đã lên kế hoạch
- Tiêu đề của phiên.
- Danh sách các khối bài tập đã lên kế hoạch.
- Thời gian bắt đầu và kết thúc phiên.
- Loại bài tập.
- Ghi chú cho hoạt động.
- Siêu dữ liệu.
- Mã phiên tập luyện đã hoàn tất – Mã này được ghi tự động sau khi một phiên tập luyện liên quan đến phiên tập luyện đã lên kế hoạch này hoàn tất.
Thông tin có trong bản ghi khối bài tập đã lên kế hoạch
Một khối bài tập đã lên kế hoạch chứa danh sách các bước tập thể dục, để hỗ trợ việc lặp lại các nhóm bước khác nhau (ví dụ: thực hiện một chuỗi các động tác gập tay, động tác bật nhảy và động tác gập bụng 5 lần liên tiếp).
- Nội dung mô tả về khối.
- Danh sách các bước tập thể dục đã lên kế hoạch.
- Số lần lặp lại.
Thông tin có trong bản ghi bước tập thể dục đã lên kế hoạch
- Nội dung mô tả bước.
- Danh mục bài tập.
- Loại bài tập.
- Danh sách mục tiêu hiệu suất.
- Mục tiêu hoàn thành.
Các phép tổng hợp được hỗ trợ
Không có phép tổng hợp nào được hỗ trợ cho loại dữ liệu này.
Ví dụ về cách sử dụng
Giả sử một người dùng lên kế hoạch chạy bộ 90 phút trong 2 ngày nữa. Buổi chạy này sẽ có 3 vòng quanh hồ với tần số tim mục tiêu từ 90 đến 110 nhịp/phút.
- Người dùng xác định một phiên tập thể dục theo kế hoạch với những thông tin sau trong ứng dụng kế hoạch tập luyện:
- Thời gian bắt đầu và kết thúc dự kiến của lần chạy
- Loại bài tập (chạy bộ)
- Số vòng (số lần lặp lại)
- Mục tiêu hiệu suất cho nhịp tim (từ 90 đến 110 nhịp/phút)
- Thông tin này được nhóm thành các khối bài tập và bước tập, đồng thời được ứng dụng kế hoạch tập luyện ghi vào Health Connect dưới dạng
PlannedExerciseSessionRecord
. - Người dùng thực hiện phiên đã lên kế hoạch (chạy).
- Dữ liệu tập thể dục liên quan đến phiên được ghi lại theo một trong hai cách:
- Bằng thiết bị đeo trong phiên hoạt động. Ví dụ: nhịp tim.
Dữ liệu này được ghi vào Health Connect dưới dạng loại bản ghi cho hoạt động. Trong trường hợp này,
HeartRateRecord
. - Do người dùng thực hiện theo cách thủ công sau phiên. Ví dụ: cho biết thời điểm bắt đầu và kết thúc của lượt chạy thực tế. Dữ liệu này được ghi vào Health Connect dưới dạng
ExerciseSessionRecord
.
- Bằng thiết bị đeo trong phiên hoạt động. Ví dụ: nhịp tim.
Dữ liệu này được ghi vào Health Connect dưới dạng loại bản ghi cho hoạt động. Trong trường hợp này,
- Sau đó, ứng dụng kế hoạch tập luyện sẽ đọc dữ liệu từ Health Connect để đánh giá hiệu suất thực tế so với các mục tiêu mà người dùng đặt ra trong phiên tập luyện đã lên kế hoạch.
Lập kế hoạch tập luyện và đặt mục tiêu
Người dùng có thể lên kế hoạch tập thể dục trong tương lai và đặt mục tiêu. Ghi dữ liệu này vào Health Connect dưới dạng một phiên tập thể dục theo kế hoạch.
Trong ví dụ được mô tả trong phần Ví dụ về cách sử dụng, người dùng lên kế hoạch chạy bộ 90 phút trong 2 ngày nữa. Buổi chạy này sẽ có 3 vòng quanh hồ với nhịp tim mục tiêu từ 90 đến 110 nhịp/phút.
Bạn có thể tìm thấy một đoạn mã như thế này trong trình xử lý biểu mẫu của một ứng dụng ghi lại các phiên tập thể dục đã lên kế hoạch vào Health Connect. Bạn cũng có thể tìm thấy thông tin này trong điểm tiếp nhận để tích hợp, chẳng hạn như với một dịch vụ cung cấp hoạt động đào tạo.
// Verify the user has granted all necessary permissions for this task
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
HealthPermission.getWritePermission(PlannedExerciseSessionRecord::class))) {
// The user hasn't granted the app permission to write planned exercise session data.
return
}
val plannedDuration = Duration.ofMinutes(90)
val plannedStartDate = LocalDate.now().plusDays(2)
val plannedExerciseSessionRecord = PlannedExerciseSessionRecord(
startDate = plannedStartDate,
duration = plannedDuration,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
blocks = listOf(
PlannedExerciseBlock(
repetitions = 1, steps = listOf(
PlannedExerciseStep(
exerciseType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_RUNNING,
exercisePhase = PlannedExerciseStep.EXERCISE_PHASE_ACTIVE,
completionGoal = ExerciseCompletionGoal.RepetitionsGoal(repetitions = 3),
performanceTargets = listOf(
ExercisePerformanceTarget.HeartRateTarget(
minHeartRate = 90.0, maxHeartRate = 110.0
)
)
),
), description = "Three laps around the lake"
)
),
title = "Run at lake",
notes = null,
metadata = Metadata.manualEntry(
device = Device(type = Device.Companion.TYPE_PHONE)
)
)
val insertedPlannedExerciseSessions =
healthConnectClient.insertRecords(listOf(plannedExerciseSessionRecord)).recordIdsList
val insertedPlannedExerciseSessionId = insertedPlannedExerciseSessions.first()
Ghi lại dữ liệu về bài tập và hoạt động
Hai ngày sau, người dùng ghi lại phiên tập thể dục thực tế. Ghi dữ liệu này vào Health Connect dưới dạng một phiên tập thể dục.
Trong ví dụ này, thời lượng phiên của người dùng khớp chính xác với thời lượng đã lên kế hoạch.
Bạn có thể tìm thấy đoạn mã sau trong trình xử lý biểu mẫu của một ứng dụng ghi lại các phiên tập thể dục vào Health Connect. Bạn cũng có thể tìm thấy thông tin này trong các trình xử lý nhập và xuất dữ liệu cho thiết bị đeo có khả năng phát hiện và ghi lại các phiên tập thể dục.
insertedPlannedExerciseSessionId
ở đây được dùng lại từ ví dụ trước. Trong một ứng dụng thực tế, mã nhận dạng sẽ được xác định bằng cách người dùng chọn một phiên tập thể dục đã lên kế hoạch trong danh sách các phiên hiện có.
// Verify the user has granted all necessary permissions for this task
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
HealthPermission.getWritePermission(ExerciseSessionRecord::class))) {
// The user doesn't granted the app permission to write exercise session data.
return
}
val sessionDuration = Duration.ofMinutes(90)
val sessionEndTime = Instant.now()
val sessionStartTime = sessionEndTime.minus(sessionDuration)
val exerciseSessionRecord = ExerciseSessionRecord(
startTime = sessionStartTime,
startZoneOffset = ZoneOffset.UTC,
endTime = sessionEndTime,
endZoneOffset = ZoneOffset.UTC,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
segments = listOf(
ExerciseSegment(
startTime = sessionStartTime,
endTime = sessionEndTime,
repetitions = 3,
segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_RUNNING
)
),
title = "Run at lake",
plannedExerciseSessionId = insertedPlannedExerciseSessionId,
metadata = Metadata.manualEntry(
device = Device(type = Device.Companion.TYPE_PHONE)
)
)
val insertedExerciseSessions =
healthConnectClient.insertRecords(listOf(exerciseSessionRecord))
Thiết bị đeo cũng ghi lại nhịp tim của người dùng trong suốt quá trình chạy. Bạn có thể dùng đoạn mã sau để tạo các bản ghi trong phạm vi mục tiêu.
Trong một ứng dụng thực tế, các phần chính của đoạn mã này có thể nằm trong trình xử lý cho một thông báo từ thiết bị đeo, thông báo này sẽ ghi dữ liệu đo lường vào Health Connect khi thu thập.
// Verify the user has granted all necessary permissions for this task
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
HealthPermission.getWritePermission(HeartRateRecord::class))) {
// The user doesn't granted the app permission to write heart rate record data.
return
}
val samples = mutableListOf<HeartRateRecord.Sample>()
var currentTime = sessionStartTime
while (currentTime.isBefore(sessionEndTime)) {
val bpm = Random.nextInt(21) + 90
val heartRateRecord = HeartRateRecord.Sample(
time = currentTime,
beatsPerMinute = bpm.toLong(),
)
samples.add(heartRateRecord)
currentTime = currentTime.plusSeconds(180)
}
val heartRateRecord = HeartRateRecord(
startTime = sessionStartTime,
startZoneOffset = ZoneOffset.UTC,
endTime = sessionEndTime,
endZoneOffset = ZoneOffset.UTC,
samples = samples,
metadata = Metadata.autoRecorded(
device = Device(type = Device.Companion.TYPE_WATCH)
)
)
val insertedHeartRateRecords = healthConnectClient.insertRecords(listOf(heartRateRecord))
Đánh giá mục tiêu hiệu suất
Vào ngày hôm sau khi người dùng tập thể dục, bạn có thể truy xuất bài tập đã ghi nhật ký, kiểm tra mọi mục tiêu tập thể dục đã lên kế hoạch và đánh giá các loại dữ liệu bổ sung để xác định xem người dùng có đạt được mục tiêu đã đặt hay không.
Một đoạn mã như thế này có thể được tìm thấy trong một công việc định kỳ để đánh giá các mục tiêu hiệu suất hoặc khi tải danh sách bài tập và hiển thị thông báo về các mục tiêu hiệu suất trong một ứng dụng.
// Verify the user has granted all necessary permissions for this task
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.containsAll(
listOf(
HealthPermission.getReadPermission(ExerciseSessionRecord::class),
HealthPermission.getReadPermission(PlannedExerciseSessionRecord::class),
HealthPermission.getReadPermission(HeartRateRecord::class)
)
)
) {
// The user doesn't granted the app permission to read exercise session record data.
return
}
val searchDuration = Duration.ofDays(1)
val searchEndTime = Instant.now()
val searchStartTime = searchEndTime.minus(searchDuration)
val response = healthConnectClient.readRecords(
ReadRecordsRequest<ExerciseSessionRecord>(
timeRangeFilter = TimeRangeFilter.between(searchStartTime, searchEndTime)
)
)
for (exerciseRecord in response.records) {
val plannedExerciseRecordId = exerciseRecord.plannedExerciseSessionId
val plannedExerciseRecord =
if (plannedExerciseRecordId == null) null else healthConnectClient.readRecord(
PlannedExerciseSessionRecord::class, plannedExerciseRecordId
).record
if (plannedExerciseRecord != null) {
val aggregateRequest = AggregateRequest(
metrics = setOf(HeartRateRecord.BPM_AVG),
timeRangeFilter = TimeRangeFilter.between(
exerciseRecord.startTime, exerciseRecord.endTime
),
)
val aggregationResult = healthConnectClient.aggregate(aggregateRequest)
val maxBpm = aggregationResult[HeartRateRecord.BPM_MAX]
val minBpm = aggregationResult[HeartRateRecord.BPM_MIN]
if (maxBpm != null && minBpm != null) {
plannedExerciseRecord.blocks.forEach { block ->
block.steps.forEach { step ->
step.performanceTargets.forEach { target ->
when (target) {
is ExercisePerformanceTarget.HeartRateTarget -> {
val minTarget = target.minHeartRate
val maxTarget = target.maxHeartRate
if(
minBpm >= minTarget && maxBpm <= maxTarget
) {
// Success!
}
}
// Handle more target types
}
}
}
}
}
}
}
}
Phiên hoạt động tập thể dục
Phiên hoạt động tập thể dục có thể gồm mọi hoạt động từ chạy bộ đến chơi cầu lông.
Ghi phiên hoạt động tập thể dục
Dưới đây là cách tạo yêu cầu chèn kèm theo dữ liệu trong phiên hoạt động:
suspend fun writeExerciseSession(healthConnectClient: HealthConnectClient) {
healthConnectClient.insertRecords(
listOf(
ExerciseSessionRecord(
startTime = START_TIME,
startZoneOffset = START_ZONE_OFFSET,
endTime = END_TIME,
endZoneOffset = END_ZONE_OFFSET,
exerciseType = ExerciseSessionRecord.ExerciseType.RUNNING,
title = "My Run"
),
// ... other records
)
)
}
Đọc phiên hoạt động tập thể dục
Dưới đây là ví dụ về cách đọc một phiên hoạt động tập thể dục:
suspend fun readExerciseSessions(
healthConnectClient: HealthConnectClient,
startTime: Instant,
endTime: Instant
) {
val response =
healthConnectClient.readRecords(
ReadRecordsRequest(
ExerciseSessionRecord::class,
timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
)
)
for (exerciseRecord in response.records) {
// Process each exercise record
// Optionally pull in with other data sources of the same time range.
val distanceRecord =
healthConnectClient
.readRecords(
ReadRecordsRequest(
DistanceRecord::class,
timeRangeFilter =
TimeRangeFilter.between(
exerciseRecord.startTime,
exerciseRecord.endTime
)
)
)
.records
}
}
Ghi dữ liệu loại phụ
Phiên hoạt động cũng có thể có dữ liệu loại phụ không bắt buộc, làm phiên phong phú thêm bằng thông tin bổ sung.
Ví dụ: các phiên hoạt động tập thể dục có thể có các lớp ExerciseSegment
, ExerciseLap
và ExerciseRoute
:
val segments = listOf(
ExerciseSegment(
startTime = Instant.parse("2022-01-02T10:10:10Z"),
endTime = Instant.parse("2022-01-02T10:10:13Z"),
segmentType = ActivitySegmentType.BENCH_PRESS,
repetitions = 373
)
)
val laps = listOf(
ExerciseLap(
startTime = Instant.parse("2022-01-02T10:10:10Z"),
endTime = Instant.parse("2022-01-02T10:10:13Z"),
length = 0.meters
)
)
ExerciseSessionRecord(
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_CALISTHENICS,
startTime = Instant.parse("2022-01-02T10:10:10Z"),
endTime = Instant.parse("2022-01-02T10:10:13Z"),
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
segments = segments,
laps = laps,
route = route
)