Health Services ให้การสนับสนุนแอปออกกำลังกายผ่าน
ExerciseClient
ExerciseClient
ช่วยให้แอปควบคุมได้ว่าเมื่อใดที่การออกกำลังกายกำลังดำเนินอยู่ เพิ่มเป้าหมายการออกกำลังกาย และรับข้อมูลอัปเดตเกี่ยวกับสถานะการออกกำลังกาย เหตุการณ์การออกกำลังกาย หรือเมตริกอื่นๆ ดูข้อมูลเพิ่มเติมได้ในรายการประเภทการออกกำลังกาย
ทั้งหมดที่บริการด้านสุขภาพรองรับ
ดูตัวอย่างการออกกำลังกาย ใน GitHub
เพิ่มทรัพยากร Dependency
หากต้องการเพิ่มการอ้างอิงในบริการด้านสุขภาพ คุณต้องเพิ่มที่เก็บ Maven ของ Google ลงในโปรเจ็กต์ ดูข้อมูลเพิ่มเติมได้ที่ ที่เก็บ Maven ของ Google
จากนั้นเพิ่มทรัพยากร Dependency ต่อไปนี้ในไฟล์ build.gradle
ระดับโมดูล
ดึงดูด
dependencies { implementation "androidx.health:health-services-client:1.1.0-alpha05" }
Kotlin
dependencies { implementation("androidx.health:health-services-client:1.1.0-alpha05") }
โครงสร้างแอป
ใช้โครงสร้างแอปต่อไปนี้เมื่อสร้างแอปออกกำลังกายด้วย บริการด้านสุขภาพ
- เก็บหน้าจอและการนำทางไว้ภายในกิจกรรมหลัก
- จัดการสถานะการออกกำลังกาย ข้อมูลเซ็นเซอร์ กิจกรรมที่กำลังดำเนินอยู่ และข้อมูลด้วย บริการที่ทำงานอยู่เบื้องหน้า
- จัดเก็บข้อมูลด้วย Room และใช้ WorkManager เพื่ออัปโหลดข้อมูล
เมื่อเตรียมพร้อมสำหรับการออกกำลังกายและระหว่างการออกกำลังกาย ระบบอาจหยุดกิจกรรมของคุณด้วยเหตุผลหลายประการ ผู้ใช้อาจเปลี่ยนไปใช้แอปอื่น
หรือกลับไปที่หน้าปัด ระบบอาจแสดงบางอย่างทับกิจกรรมของคุณ หรือหน้าจออาจปิดหลังจากไม่มีการใช้งานระยะหนึ่ง
ใช้ ForegroundService
ที่ทำงานอย่างต่อเนื่อง
ร่วมกับ ExerciseClient
เพื่อช่วยให้มั่นใจว่าการออกกำลังกาย
ทั้งหมดจะทำงานได้อย่างถูกต้อง
การใช้ ForegroundService
ช่วยให้คุณใช้ Ongoing Activity API เพื่อแสดง
ตัวบ่งชี้บนพื้นผิวของนาฬิกา ซึ่งช่วยให้ผู้ใช้กลับไปที่
การออกกำลังกายได้อย่างรวดเร็ว
คุณต้องขอข้อมูลตำแหน่งอย่างเหมาะสมในบริการที่ทำงานอยู่เบื้องหน้า ในไฟล์ Manifest ให้ระบุประเภทบริการที่ทำงานอยู่เบื้องหน้าและสิทธิ์ที่จำเป็น ดังนี้
<manifest ...> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <application ...> <!-- If your app is designed only for devices that run Wear OS 4 or lower, use android:foregroundServiceType="location" instead. --> <service android:name=".MyExerciseSessionRecorder" android:foregroundServiceType="health|location"> </service> </application> </manifest>
ใช้
AmbientLifecycleObserver
สำหรับกิจกรรมก่อนออกกำลังกายที่มีการเรียกใช้ prepareExercise()
และ
สำหรับกิจกรรมการออกกำลังกาย อย่างไรก็ตาม โปรดอย่าอัปเดตจอแสดงผลระหว่างออกกำลังกาย
ในโหมดแอมเบียนท์: เนื่องจากบริการด้านสุขภาพจะจัดกลุ่มข้อมูลการออกกำลังกาย
เมื่อหน้าจออุปกรณ์อยู่ในโหมดแอมเบียนท์เพื่อประหยัดพลังงาน ดังนั้นข้อมูล
ที่แสดงอาจไม่ใช่ข้อมูลล่าสุด ในระหว่างการออกกำลังกาย ให้แสดงข้อมูลที่เกี่ยวข้องกับ
ผู้ใช้ โดยแสดงข้อมูลล่าสุดหรือหน้าจอว่าง
ตรวจสอบความสามารถ
ExerciseType
แต่ละรายการรองรับข้อมูลบางประเภทสำหรับเมตริกและเป้าหมายการออกกำลังกาย
ตรวจสอบความสามารถเหล่านี้เมื่อเริ่มต้นใช้งาน เนื่องจากอาจแตกต่างกันไปตามอุปกรณ์
อุปกรณ์อาจไม่รองรับการออกกำลังกายบางประเภท หรืออาจไม่รองรับฟังก์ชันบางอย่าง เช่น
หยุดชั่วคราวอัตโนมัติ นอกจากนี้ ความสามารถของอุปกรณ์อาจเปลี่ยนแปลงเมื่อเวลาผ่านไป เช่น หลังจากอัปเดตซอฟต์แวร์
เมื่อแอปเริ่มต้น ให้ค้นหาความสามารถของอุปกรณ์ แล้วจัดเก็บและประมวลผลข้อมูลต่อไปนี้
- แบบฝึกหัดที่แพลตฟอร์มรองรับ
- ฟีเจอร์ที่รองรับสำหรับการออกกำลังกายแต่ละประเภท
- ประเภทข้อมูลที่รองรับสําหรับการออกกําลังกายแต่ละประเภท
- สิทธิ์ที่จำเป็นสำหรับข้อมูลแต่ละประเภท
ใช้ ExerciseCapabilities.getExerciseTypeCapabilities()
กับ
ประเภทการออกกำลังกายที่เลือกเพื่อดูว่าคุณขอเมตริกประเภทใดได้
กำหนดเป้าหมายการออกกำลังกายใดได้ และมีฟีเจอร์อื่นๆ ใดบ้างที่ใช้ได้กับ
การออกกำลังกายประเภทนั้น ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการดำเนินการนี้
val healthClient = HealthServices.getClient(this /*context*/)
val exerciseClient = healthClient.exerciseClient
lifecycleScope.launch {
val capabilities = exerciseClient.getCapabilitiesAsync().await()
if (ExerciseType.RUNNING in capabilities.supportedExerciseTypes) {
runningCapabilities =
capabilities.getExerciseTypeCapabilities(ExerciseType.RUNNING)
}
}
ภายใน ExerciseTypeCapabilities
ที่แสดงผล
supportedDataTypes
จะแสดงประเภทข้อมูลที่คุณขอข้อมูลได้ ซึ่งจะแตกต่างกันไปตามอุปกรณ์ ดังนั้น
โปรดอย่าขอ DataType
ที่ไม่รองรับ มิเช่นนั้นคำขออาจ
ไม่สำเร็จ
ใช้ช่อง
supportedGoals
และ
supportedMilestones
เพื่อพิจารณาว่าการออกกำลังกายรองรับเป้าหมายการออกกำลังกายที่คุณ
ต้องการสร้างหรือไม่
หากแอปอนุญาตให้ผู้ใช้ใช้การหยุดชั่วคราวอัตโนมัติ คุณ
ต้องตรวจสอบว่าอุปกรณ์รองรับฟังก์ชันการทำงานนี้โดยใช้
supportsAutoPauseAndResume
ExerciseClient
ปฏิเสธคำขอที่ไม่รองรับในอุปกรณ์
ตัวอย่างต่อไปนี้จะตรวจสอบการรองรับHEART_RATE_BPM
ประเภทข้อมูล
ความสามารถของเป้าหมาย STEPS_TOTAL
และฟังก์ชันการหยุดชั่วคราวอัตโนมัติ
// Whether we can request heart rate metrics.
supportsHeartRate = DataType.HEART_RATE_BPM in runningCapabilities.supportedDataTypes
// Whether we can make a one-time goal for aggregate steps.
val stepGoals = runningCapabilities.supportedGoals[DataType.STEPS_TOTAL]
supportsStepGoals =
(stepGoals != null && ComparisonType.GREATER_THAN_OR_EQUAL in stepGoals)
// Whether auto-pause is supported.
val supportsAutoPause = runningCapabilities.supportsAutoPauseAndResume
ลงทะเบียนเพื่อรับข้อมูลอัปเดตสถานะการออกกำลังกาย
ระบบจะส่งข้อมูลอัปเดตการออกกำลังกายไปยังผู้ฟัง แอปของคุณจะลงทะเบียน เครื่องรับได้ครั้งละ 1 เครื่องเท่านั้น ตั้งค่าเครื่องรับฟังก่อนเริ่มออกกำลังกาย ดังที่แสดงในตัวอย่างต่อไปนี้ ผู้ฟังจะได้รับการอัปเดตเกี่ยวกับแบบฝึกหัดที่แอปของคุณเป็นเจ้าของเท่านั้น
val callback = object : ExerciseUpdateCallback {
override fun onExerciseUpdateReceived(update: ExerciseUpdate) {
val exerciseStateInfo = update.exerciseStateInfo
val activeDuration = update.activeDurationCheckpoint
val latestMetrics = update.latestMetrics
val latestGoals = update.latestAchievedGoals
}
override fun onLapSummaryReceived(lapSummary: ExerciseLapSummary) {
// For ExerciseTypes that support laps, this is called when a lap is marked.
}
override fun onAvailabilityChanged(
dataType: DataType<*, *>,
availability: Availability
) {
// Called when the availability of a particular DataType changes.
when {
availability is LocationAvailability -> // Relates to Location/GPS.
availability is DataTypeAvailability -> // Relates to another DataType.
}
}
}
exerciseClient.setUpdateCallback(callback)
จัดการอายุการใช้งานของการออกกำลังกาย
บริการด้านสุขภาพรองรับการออกกำลังกายครั้งละ 1 รายการในแอปทั้งหมดบนอุปกรณ์ หากมีการติดตามการออกกำลังกายและแอปอื่นเริ่มติดตาม การออกกำลังกายใหม่ ระบบจะสิ้นสุดการออกกำลังกายแรก
ก่อนเริ่มออกกำลังกาย ให้ทำดังนี้
- ตรวจสอบว่ามีการติดตามการออกกำลังกายอยู่แล้วหรือไม่ และตอบสนองตามนั้น เช่น ขอให้ผู้ใช้ยืนยัน ก่อนที่จะลบล้างการออกกำลังกายครั้งก่อนและเริ่มติดตามการออกกำลังกายครั้งใหม่
ตัวอย่างต่อไปนี้แสดงวิธีตรวจสอบว่ามีแบบฝึกหัดอยู่แล้วหรือไม่ด้วย
getCurrentExerciseInfoAsync
lifecycleScope.launch {
val exerciseInfo = exerciseClient.getCurrentExerciseInfoAsync().await()
when (exerciseInfo.exerciseTrackedStatus) {
OTHER_APP_IN_PROGRESS -> // Warn user before continuing, will stop the existing workout.
OWNED_EXERCISE_IN_PROGRESS -> // This app has an existing workout.
NO_EXERCISE_IN_PROGRESS -> // Start a fresh workout.
}
}
สิทธิ์
เมื่อใช้ ExerciseClient
โปรดตรวจสอบว่าแอปขอและรักษาสิทธิ์ที่จำเป็น
หากแอปใช้LOCATION
ข้อมูล โปรดตรวจสอบว่าแอปขอและรักษาสิทธิ์ที่เหมาะสมสำหรับข้อมูลนั้นด้วย
สำหรับข้อมูลทุกประเภท ก่อนเรียกใช้ prepareExercise()
หรือ startExercise()
ให้ทำดังนี้
- ระบุสิทธิ์ที่เหมาะสมสำหรับประเภทข้อมูลที่ขอในไฟล์
AndroidManifest.xml
- ตรวจสอบว่าผู้ใช้ได้ให้สิทธิ์ที่จำเป็นแล้ว ดูข้อมูลเพิ่มเติมได้ที่ขอสิทธิ์ของแอป Health Services จะปฏิเสธคำขอหากยังไม่ได้รับสิทธิ์ที่จำเป็น
สำหรับข้อมูลตำแหน่ง ให้ทำตามขั้นตอนเพิ่มเติมต่อไปนี้
- ตรวจสอบว่าเปิดใช้ GPS ในอุปกรณ์แล้วโดยใช้
isProviderEnabled(LocationManager.GPS_PROVIDER)
แจ้งให้ผู้ใช้เปิดการตั้งค่าตำแหน่ง หากจำเป็น - ตรวจสอบว่าได้รักษาระดับ
ForegroundService
ที่มีforegroundServiceType
ที่เหมาะสมตลอดการออกกำลังกาย
เตรียมพร้อมสำหรับการออกกำลังกาย
เซ็นเซอร์บางอย่าง เช่น GPS หรืออัตราการเต้นของหัวใจ อาจใช้เวลาสักครู่ในการวอร์มอัพ หรือผู้ใช้อาจต้องการดูข้อมูลก่อนเริ่มออกกำลังกาย วิธีการ
prepareExerciseAsync()
ที่ไม่บังคับช่วยให้เซ็นเซอร์เหล่านี้อุ่นเครื่องและรับข้อมูลได้โดยไม่ต้องเริ่ม
ตัวจับเวลาสำหรับการออกกำลังกาย activeDuration
ไม่ได้รับผลกระทบจากระยะเวลาเตรียมการนี้
ก่อนโทรหา prepareExerciseAsync()
โปรดตรวจสอบสิ่งต่อไปนี้
ตรวจสอบการตั้งค่าตำแหน่งทั่วทั้งแพลตฟอร์ม ผู้ใช้ควบคุมการตั้งค่านี้ได้ใน เมนูการตั้งค่าหลัก ซึ่งแตกต่างจากการตรวจสอบสิทธิ์ระดับแอป
หากการตั้งค่าปิดอยู่ ให้แจ้งผู้ใช้ว่าได้ปฏิเสธการเข้าถึง ตำแหน่ง และแจ้งให้ผู้ใช้เปิดใช้หากแอปของคุณต้องใช้ตำแหน่ง
ยืนยันว่าแอปมีสิทธิ์รันไทม์สำหรับเซ็นเซอร์ร่างกาย (API ระดับ 35 หรือต่ำกว่า) หรืออัตราการเต้นของหัวใจ (API ระดับ 36 ขึ้นไป) การจดจำกิจกรรม และ ตำแหน่งที่แม่นยำ สำหรับสิทธิ์ที่ขาดหายไป ให้แจ้งให้ผู้ใช้ขอสิทธิ์รันไทม์ โดยระบุบริบทที่เพียงพอ หากผู้ใช้ไม่ให้สิทธิ์ที่เฉพาะเจาะจง ให้นำประเภทข้อมูลที่เชื่อมโยงกับสิทธิ์นั้นออกจากคำขอไปยัง
prepareExerciseAsync()
หากไม่ได้ให้สิทธิ์เซ็นเซอร์ร่างกาย (อัตราการเต้นของหัวใจใน API ระดับ 36 ขึ้นไป) หรือสิทธิ์เข้าถึงตำแหน่ง ให้งดเรียกใช้prepareExerciseAsync()
เนื่องจาก การเรียกใช้ prepare มีไว้เพื่อรับอัตราการเต้นของหัวใจที่เสถียรหรือการแก้ไข GPS โดยเฉพาะก่อน เริ่มออกกำลังกาย แอปจะยังคงรับระยะทาง ก้าว อัตราเร็ว ความเร็ว และเมตริกอื่นๆ ที่ไม่จำเป็นต้องใช้สิทธิ์เหล่านั้นได้
ทำดังนี้เพื่อยืนยันว่าการเรียกใช้ prepareExerciseAsync()
สำเร็จ
- ใช้
AmbientLifecycleObserver
สำหรับกิจกรรมก่อนออกกำลังกายที่มีการเรียกเตรียม - เรียกใช้
prepareExerciseAsync()
จากบริการที่ทำงานอยู่เบื้องหน้า หากไม่ได้อยู่ใน บริการและเชื่อมโยงกับวงจรของกิจกรรม ระบบอาจปิดการเตรียมเซ็นเซอร์ โดยไม่จำเป็น - เรียกใช้
endExercise()
เพื่อปิดเซ็นเซอร์และลดการใช้พลังงานหากผู้ใช้ ออกจากกิจกรรมก่อนออกกำลังกาย
ตัวอย่างต่อไปนี้แสดงวิธีเรียกใช้ prepareExerciseAsync()
val warmUpConfig = WarmUpConfig(
ExerciseType.RUNNING,
setOf(
DataType.HEART_RATE_BPM,
DataType.LOCATION
)
)
// Only necessary to call prepareExerciseAsync if body sensor (API level 35
// or lower), heart rate (API level 36+), or location permissions are given.
exerciseClient.prepareExerciseAsync(warmUpConfig).await()
// Data and availability updates are delivered to the registered listener.
เมื่อแอปอยู่ในสถานะ PREPARING
ระบบจะส่งข้อมูลอัปเดตความพร้อมใช้งานของเซ็นเซอร์ใน ExerciseUpdateCallback
ผ่าน onAvailabilityChanged()
จากนั้นระบบจะแสดงข้อมูลนี้ต่อผู้ใช้เพื่อให้ผู้ใช้ตัดสินใจว่าจะเริ่มออกกำลังกายหรือไม่
เริ่มออกกำลังกาย
เมื่อต้องการเริ่มออกกำลังกาย ให้สร้างExerciseConfig
เพื่อกำหนดค่า
ประเภทการออกกำลังกาย ประเภทข้อมูลที่คุณต้องการรับเมตริก และ
เป้าหมายหรือเหตุการณ์สำคัญในการออกกำลังกาย
เป้าหมายการออกกำลังกายประกอบด้วยDataType
และ
เงื่อนไข เป้าหมายการออกกำลังกายเป็นเป้าหมายแบบครั้งเดียวที่จะทริกเกอร์เมื่อตรงตาม
เงื่อนไข เช่น เมื่อผู้ใช้วิ่งในระยะทางที่กำหนด นอกจากนี้ยังตั้งค่าเป้าหมายการออกกำลังกายได้ด้วย ระบบจะทริกเกอร์เหตุการณ์สำคัญในการออกกำลังกายได้หลายครั้ง
เช่น ทุกครั้งที่ผู้ใช้
วิ่งผ่านจุดที่กำหนดหลังจากระยะทางที่ตั้งไว้
ตัวอย่างต่อไปนี้แสดงวิธีสร้างเป้าหมายประเภทละ 1 รายการ
const val CALORIES_THRESHOLD = 250.0
const val DISTANCE_THRESHOLD = 1_000.0 // meters
suspend fun startExercise() {
// Types for which we want to receive metrics.
val dataTypes = setOf(
DataType.HEART_RATE_BPM,
DataType.CALORIES_TOTAL,
DataType.DISTANCE
)
// Create a one-time goal.
val calorieGoal = ExerciseGoal.createOneTimeGoal(
DataTypeCondition(
dataType = DataType.CALORIES_TOTAL,
threshold = CALORIES_THRESHOLD,
comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
)
)
// Create a milestone goal. To make a milestone for every kilometer, set the initial
// threshold to 1km and the period to 1km.
val distanceGoal = ExerciseGoal.createMilestone(
condition = DataTypeCondition(
dataType = DataType.DISTANCE_TOTAL,
threshold = DISTANCE_THRESHOLD,
comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL
),
period = DISTANCE_THRESHOLD
)
val config = ExerciseConfig(
exerciseType = ExerciseType.RUNNING,
dataTypes = dataTypes,
isAutoPauseAndResumeEnabled = false,
isGpsEnabled = true,
exerciseGoals = mutableListOf<ExerciseGoal<Double>>(calorieGoal, distanceGoal)
)
exerciseClient.startExerciseAsync(config).await()
}
นอกจากนี้ คุณยังทำเครื่องหมายรอบสำหรับการออกกำลังกายทั้งหมดได้ด้วย Health Services จะให้ExerciseLapSummary
พร้อมเมตริกที่รวบรวมในช่วง
รอบการวิ่ง
ตัวอย่างก่อนหน้าแสดงการใช้ isGpsEnabled
ซึ่งต้องเป็นจริง
เมื่อขอข้อมูลตำแหน่ง อย่างไรก็ตาม การใช้ GPS ยังช่วยในเรื่องเมตริกอื่นๆ ได้ด้วย
หาก ExerciseConfig
ระบุระยะทางเป็น DataType
ค่าเริ่มต้นนี้จะใช้จำนวนก้าวเพื่อประมาณระยะทาง หากเลือกเปิดใช้ GPS
คุณจะใช้ข้อมูลตำแหน่งแทนเพื่อประมาณระยะทางได้
หยุดชั่วคราว กลับมาออกกำลังกายต่อ และสิ้นสุดการออกกำลังกาย
คุณสามารถหยุดชั่วคราว เล่นต่อ และสิ้นสุดการออกกำลังกายได้โดยใช้วิธีที่เหมาะสม เช่น
pauseExerciseAsync()
หรือ
endExerciseAsync()
ใช้สถานะจาก ExerciseUpdate
เป็นแหล่งข้อมูลที่ถูกต้อง ระบบจะไม่ถือว่าการออกกำลังกายหยุดชั่วคราวเมื่อการเรียกใช้ pauseExerciseAsync()
กลับมา แต่จะถือว่าหยุดชั่วคราวเมื่อสถานะดังกล่าวแสดงในข้อความ ExerciseUpdate
โดยเฉพาะอย่างยิ่ง
เมื่อพิจารณาถึงสถานะ UI หากผู้ใช้กดหยุดชั่วคราว ให้
ปิดใช้ปุ่มหยุดชั่วคราวและเรียกใช้ pauseExerciseAsync()
on
Health Services รอให้บริการด้านสุขภาพเข้าสู่สถานะหยุดชั่วคราวโดยใช้ ExerciseUpdate.exerciseStateInfo.state
แล้วสลับปุ่ม
เพื่อกลับมาทำงานต่อ เนื่องจากการอัปเดตสถานะของบริการด้านสุขภาพอาจใช้เวลานานกว่าการกดปุ่ม ดังนั้นหากคุณเชื่อมโยงการเปลี่ยนแปลง UI ทั้งหมดกับการกดปุ่ม UI อาจไม่ซิงค์กับสถานะของบริการด้านสุขภาพ
โปรดคำนึงถึงเรื่องนี้ในสถานการณ์ต่อไปนี้
- เปิดใช้การหยุดชั่วคราวอัตโนมัติ: การออกกำลังกายจะหยุดชั่วคราวหรือเริ่มได้โดยไม่ต้องมีการโต้ตอบจากผู้ใช้
- แอปอื่นเริ่มการออกกำลังกาย: ระบบอาจสิ้นสุดการออกกำลังกายโดยไม่ต้องมีการโต้ตอบจากผู้ใช้
หากแอปอื่นสิ้นสุดการออกกำลังกายของแอป คุณต้องจัดการการสิ้นสุดอย่างเหมาะสม
- บันทึกสถานะการออกกำลังกายบางส่วนเพื่อไม่ให้ระบบลบความคืบหน้าของผู้ใช้
- นำไอคอนกิจกรรมที่กำลังดำเนินอยู่ออกและส่งการแจ้งเตือนให้ผู้ใช้ทราบว่าแอปอื่นเป็นผู้สิ้นสุดการออกกำลังกาย
นอกจากนี้ ให้จัดการกรณีที่สิทธิ์ถูกเพิกถอนในระหว่าง
การออกกำลังกายที่กำลังดำเนินอยู่ด้วย ระบบจะส่งข้อมูลนี้โดยใช้สถานะ isEnded
พร้อมด้วย
ExerciseEndReason
ของ AUTO_END_PERMISSION_LOST
จัดการกรณีนี้ในลักษณะเดียวกับกรณีการสิ้นสุด โดยบันทึกสถานะบางส่วน นำไอคอนกิจกรรมที่กำลังดำเนินอยู่ออก และส่งการแจ้งเตือนเกี่ยวกับสิ่งที่เกิดขึ้นให้ผู้ใช้ทราบ
ตัวอย่างต่อไปนี้แสดงวิธีตรวจสอบการสิ้นสุดอย่างถูกต้อง
val callback = object : ExerciseUpdateCallback {
override fun onExerciseUpdateReceived(update: ExerciseUpdate) {
if (update.exerciseStateInfo.state.isEnded) {
// Workout has either been ended by the user, or otherwise terminated
}
...
}
...
}
จัดการระยะเวลาการใช้งาน
ในระหว่างการออกกำลังกาย แอปจะแสดงระยะเวลาการออกกำลังกายที่ใช้งานอยู่ได้
แอป บริการด้านสุขภาพ และหน่วยควบคุมขนาดเล็กของอุปกรณ์
(MCU) ซึ่งเป็นโปรเซสเซอร์ที่ใช้พลังงานต่ำ
ที่รับผิดชอบในการติดตามการออกกำลังกาย ทั้งหมดต้องซิงค์กันโดยมีระยะเวลาการใช้งานปัจจุบันที่ใช้งานอยู่เหมือนกัน Health Services จะส่งActiveDurationCheckpoint
เพื่อช่วยจัดการเรื่องนี้ ซึ่งจะให้จุดยึดที่แอปใช้เริ่มตัวจับเวลาได้
เนื่องจากระยะเวลาที่ใช้งานอยู่จะส่งจาก MCU และอาจใช้เวลาสักครู่ในการมาถึงแอป ActiveDurationCheckpoint
จึงมีพร็อพเพอร์ตี้ 2 รายการดังนี้
activeDuration
: ระยะเวลาที่การออกกำลังกายดำเนินอยู่time
: เมื่อมีการคำนวณระยะเวลาที่ใช้งาน
ดังนั้น ในแอป ระยะเวลาที่ใช้งานอยู่ของการออกกำลังกายจึงคำนวณได้จาก ActiveDurationCheckpoint
โดยใช้สมการต่อไปนี้
(now() - checkpoint.time) + checkpoint.activeDuration
ซึ่งจะอธิบายถึงความแตกต่างเล็กๆ น้อยๆ ระหว่างระยะเวลาที่ใช้งานอยู่ซึ่งคำนวณใน MCU กับระยะเวลาที่มาถึงแอป คุณสามารถใช้ค่านี้เพื่อเริ่มต้นโครโนมิเตอร์ในแอปและช่วยให้มั่นใจว่าตัวจับเวลาของแอปจะสอดคล้องกับเวลาในบริการด้านสุขภาพและ MCU อย่างสมบูรณ์
หากหยุดออกกำลังกายชั่วคราว แอปจะรอรีสตาร์ทตัวจับเวลาใน UI
จนกว่าเวลาที่คำนวณแล้วจะผ่านเวลาที่ UI แสดง
เนื่องจากสัญญาณหยุดชั่วคราวจะไปถึงบริการด้านสุขภาพและ MCU โดยมี
ความล่าช้าเล็กน้อย เช่น หากหยุดแอปชั่วคราวที่ t=10 วินาที Health
Services อาจไม่ส่งการอัปเดต PAUSED
ไปยังแอปจนกว่าจะถึง t=10.2 วินาที
ทำงานกับข้อมูลจาก ExerciseClient
ระบบจะส่งเมตริกสำหรับประเภทข้อมูลที่แอปของคุณลงทะเบียนไว้ในข้อความ
ExerciseUpdate
โปรเซสเซอร์จะส่งข้อความเมื่อตื่นอยู่เท่านั้น หรือเมื่อถึงระยะเวลาการรายงานสูงสุด เช่น ทุกๆ 150 วินาที อย่าใช้ExerciseUpdate
ความถี่เพื่อเลื่อนโครโนมิเตอร์ด้วยactiveDuration
ดูตัวอย่างวิธีใช้โครโนมิเตอร์อิสระได้ที่ตัวอย่างการออกกำลังกาย
ใน GitHub
เมื่อผู้ใช้เริ่มออกกำลังกาย ระบบจะส่งExerciseUpdate
ข้อความได้บ่อย เช่น ทุกวินาที
เมื่อผู้ใช้เริ่มออกกำลังกาย หน้าจอ
อาจปิดลง จากนั้นบริการข้อมูลสุขภาพจะส่งข้อมูลน้อยลง แต่ยังคง
สุ่มตัวอย่างที่ความถี่เดิมเพื่อหลีกเลี่ยงการปลุกโปรเซสเซอร์หลัก เมื่อผู้ใช้
มองที่หน้าจอ ระบบจะส่งข้อมูลที่กำลังประมวลผลเป็นกลุ่มไปยังแอปของคุณทันที
ควบคุมอัตราการประมวลผลแบบกลุ่ม
ในบางสถานการณ์ คุณอาจต้องการควบคุมความถี่ที่แอป
ได้รับข้อมูลบางประเภทขณะที่หน้าจอปิดอยู่ ออบเจ็กต์
BatchingMode
ช่วยให้แอปของคุณลบล้างลักษณะการทำงานของการจัดกลุ่มเริ่มต้นเพื่อรับการนำส่งข้อมูลได้บ่อยขึ้น
หากต้องการกำหนดค่าอัตราการประมวลผลแบบกลุ่ม ให้ทำตามขั้นตอนต่อไปนี้
ตรวจสอบว่าอุปกรณ์รองรับ
BatchingMode
ความละเอียดที่ต้องการหรือไม่// Confirm BatchingMode support to control heart rate stream to phone. suspend fun supportsHrWorkoutCompanionMode(): Boolean { val capabilities = exerciseClient.getCapabilities() return BatchingMode.HEART_RATE_5_SECONDS in capabilities.supportedBatchingModeOverrides }
ระบุว่าออบเจ็กต์
ExerciseConfig
ควรใช้BatchingMode
ที่เฉพาะเจาะจงตามที่แสดงในข้อมูลโค้ดต่อไปนี้val config = ExerciseConfig( exerciseType = ExerciseType.WORKOUT, dataTypes = setOf( DataType.HEART_RATE_BPM, DataType.TOTAL_CALORIES ), // ... batchingModeOverrides = setOf(BatchingMode.HEART_RATE_5_SECONDS) )
คุณจะกำหนดค่า
BatchingMode
แบบไดนามิกระหว่างการออกกำลังกายก็ได้ (ไม่บังคับ) แทนที่จะให้ลักษณะการจัดกลุ่มที่เฉพาะเจาะจงคงอยู่ตลอดระยะเวลา ของการออกกำลังกายval desiredModes = setOf(BatchingMode.HEART_RATE_5_SECONDS) exerciseClient.overrideBatchingModesForActiveExercise(desiredModes)
หากต้องการล้าง
BatchingMode
ที่ปรับแต่งแล้วและกลับไปใช้ลักษณะการทำงานเริ่มต้น ให้ส่งชุดข้อมูลว่างไปยังexerciseClient.overrideBatchingModesForActiveExercise()
การประทับเวลา
จุดข้อมูลแต่ละจุดแสดงระยะเวลาตั้งแต่เริ่มระบบอุปกรณ์ หากต้องการแปลงเป็นแสตมป์เวลา ให้ทำดังนี้
val bootInstant =
Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime())
จากนั้นจะใช้ค่านี้กับ getStartInstant()
หรือ getEndInstant()
สำหรับแต่ละจุดข้อมูลได้
ความถูกต้องของข้อมูล
ข้อมูลบางประเภทอาจมีข้อมูลความแม่นยำที่เชื่อมโยงกับจุดข้อมูลแต่ละจุด
ซึ่งแสดงในพร็อพเพอร์ตี้ accuracy
ระบบจะป้อนข้อมูลให้กับคลาส HrAccuracy
และ LocationAccuracy
สำหรับประเภทข้อมูล HEART_RATE_BPM
และ LOCATION
ตามลำดับ หากมี ให้ใช้พร็อพเพอร์ตี้
accuracy
เพื่อพิจารณาว่าจุดข้อมูลแต่ละจุดมีความแม่นยำเพียงพอ
สําหรับแอปพลิเคชันของคุณหรือไม่
จัดเก็บและอัปโหลดข้อมูล
ใช้ Room เพื่อบันทึกข้อมูลที่ส่งจากบริการข้อมูลสุขภาพ การอัปโหลดข้อมูลจะเกิดขึ้นเมื่อสิ้นสุดการออกกำลังกายโดยใช้กลไก เช่น Work Manager ซึ่งจะช่วยยืนยัน ว่าการเรียกเครือข่ายเพื่ออัปโหลดข้อมูลจะเลื่อนออกไปจนกว่าการออกกำลังกายจะสิ้นสุด เพื่อลดการใช้พลังงานระหว่างการออกกำลังกายและลดความซับซ้อนของงาน
รายการตรวจสอบการรวมระบบ
ก่อนเผยแพร่แอปที่ใช้ ExerciseClient
ของบริการด้านสุขภาพ โปรดดูรายการตรวจสอบต่อไปนี้เพื่อยืนยันว่าประสบการณ์ของผู้ใช้หลีกเลี่ยงปัญหาที่พบบ่อยบางอย่าง โปรดตรวจสอบว่า
- แอปจะตรวจสอบความสามารถ ของประเภทการออกกำลังกายและความสามารถของอุปกรณ์ทุกครั้งที่แอปทำงาน วิธีนี้จะช่วยให้คุณตรวจพบได้เมื่ออุปกรณ์หรือการออกกำลังกายใดๆ ไม่รองรับ ข้อมูลประเภทใดประเภทหนึ่งที่แอปของคุณต้องการ
- คุณขอและรักษาสิทธิ์ที่จำเป็นไว้ และระบุสิทธิ์เหล่านี้ใน
ไฟล์ Manifest ก่อนที่จะเรียกใช้
prepareExerciseAsync()
แอปของคุณ จะยืนยันว่าได้รับสิทธิ์รันไทม์แล้ว - แอปของคุณใช้
getCurrentExerciseInfoAsync()
เพื่อจัดการกรณีที่- ระบบกำลังติดตามการออกกำลังกายอยู่แล้ว และแอปของคุณจะลบล้างการออกกำลังกายก่อนหน้า
- แอปอื่นได้สิ้นสุดการออกกำลังกายของคุณ ซึ่งอาจเกิดขึ้นเมื่อผู้ใช้ เปิดแอปอีกครั้ง โดยจะเห็นข้อความที่อธิบายว่าการออกกำลังกาย หยุดลงเนื่องจากแอปอื่นเข้ามาแทนที่
- หากคุณใช้
LOCATION
ข้อมูล ให้ทำดังนี้- แอปของคุณจะรักษา
ForegroundService
ที่มีforegroundServiceType
ที่เกี่ยวข้องตลอดระยะเวลาการออกกำลังกาย (รวมถึง การเรียกใช้ฟังก์ชันเตรียม) - ตรวจสอบว่าเปิดใช้ GPS ในอุปกรณ์โดยใช้
isProviderEnabled(LocationManager.GPS_PROVIDER)
และแจ้งให้ผู้ใช้ เปิดการตั้งค่าตำแหน่งหากจำเป็น - สำหรับกรณีการใช้งานที่ต้องการความแม่นยำสูง ซึ่งการรับข้อมูลตำแหน่งที่มีเวลาในการตอบสนองต่ำมีความสำคัญอย่างยิ่ง ให้พิจารณาผสานรวม Fused Location Provider (FLP) และใช้ข้อมูลดังกล่าวเป็นการแก้ไขตำแหน่งเริ่มต้น เมื่อมีข้อมูลตำแหน่งที่เสถียรมากขึ้นจากบริการด้านสุขภาพ ให้ใช้ข้อมูลดังกล่าวแทน FLP
- แอปของคุณจะรักษา
- หากแอปของคุณต้องอัปโหลดข้อมูล การเรียกเครือข่ายเพื่ออัปโหลดข้อมูลจะ เลื่อนออกไปจนกว่าการออกกำลังกายจะสิ้นสุด มิเช่นนั้น ตลอดการทดสอบ แอปของคุณจะทำการเรียกเครือข่ายที่จำเป็นอย่างประหยัด
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- การอัปเดตข้อมูลแบบพาสซีฟ
- บริการด้านสุขภาพใน Wear OS
- เริ่มต้นใช้งานไทล์