Nâng cao trải nghiệm về sức khoẻ và thể hình của ứng dụng bằng cách mở rộng ứng dụng sang thiết bị có thể đeo các thiết bị sử dụng công nghệ Wear OS.
Thêm mô-đun Wear OS
Android Studio cung cấp một trình hướng dẫn tiện lợi để thêm mô-đun Wear OS vào ứng dụng của bạn. Trong Tệp > Trên trình đơn New Module (Mô-đun mới), chọn Wear OS, như minh hoạ trong phần sau đây hình ảnh:
![Trình hướng dẫn mô-đun Wear OS trong Android Studio](https://developer.android.com/static/health-and-fitness/guides/basic-fitness-app/images/basic_fitness_app_img_8.png?authuser=9&hl=vi)
Điều quan trọng cần lưu ý là Minimum SDK (SDK tối thiểu) phải là API 30 trở lên để cho phép bạn sử dụng phiên bản Dịch vụ sức khoẻ mới nhất. Dịch vụ sức khoẻ giúp theo dõi chỉ số và ghi lại dữ liệu dễ dàng hơn bằng cách định cấu hình cảm biến.
Sau khi hoàn tất trình hướng dẫn, hãy đồng bộ hoá dự án của bạn. Thao tác Run (Chạy) sau đây cấu hình xuất hiện:
![Hình ảnh cho thấy nút chạy của ứng dụng Wear OS](https://developer.android.com/static/health-and-fitness/guides/basic-fitness-app/images/basic_fitness_app_img_5.png?authuser=9&hl=vi)
Thao tác này cho phép bạn chạy mô-đun Wear OS trên một thiết bị đeo. Bạn có hai tùy chọn:
Chạy trên một trình mô phỏng.
Chạy trên thiết bị thực.
Việc chạy cấu hình sẽ triển khai ứng dụng cho trình mô phỏng Wear OS hoặc thiết bị và hiện lời "chào mọi người" của bạn. Đây là cách thiết lập giao diện người dùng cơ bản, sử dụng Compose cho Wear OS để làm quen với ứng dụng của bạn.
Thêm Dịch vụ sức khoẻ và Hilt
Tích hợp các thư viện sau vào mô-đun Wear OS:
- Dịch vụ sức khoẻ: cung cấp quyền truy cập vào các cảm biến và dữ liệu trên đồng hồ rất tiện lợi và tiết kiệm điện hơn.
- Hilt: Cho phép chèn và quản lý phần phụ thuộc một cách hiệu quả.
Tạo Trình quản lý dịch vụ sức khoẻ
Để việc sử dụng Dịch vụ sức khoẻ thuận tiện hơn một chút và cung cấp và mượt mà hơn, bạn có thể tạo một trình bao bọc như sau:
private const val TAG = "WATCHMAIN"
class HealthServicesManager(context: Context) {
private val measureClient = HealthServices.getClient(context).measureClient
suspend fun hasHeartRateCapability() = runCatching {
val capabilities = measureClient.getCapabilities()
(DataType.HEART_RATE_BPM in capabilities.supportedDataTypesMeasure)
}.getOrDefault(false)
/**
* Returns a cold flow. When activated, the flow will register a callback for heart rate data
* and start to emit messages. When the consuming coroutine is canceled, the measure callback
* is unregistered.
*
* [callbackFlow] creates a bridge between a callback-based API and Kotlin flows.
*/
@ExperimentalCoroutinesApi
fun heartRateMeasureFlow(): Flow<MeasureMessage> = callbackFlow {
val callback = object : MeasureCallback {
override fun onAvailabilityChanged(dataType: DeltaDataType<*, *>, availability: Availability) {
// Only send back DataTypeAvailability (not LocationAvailability)
if (availability is DataTypeAvailability) {
trySendBlocking(MeasureMessage.MeasureAvailability(availability))
}
}
override fun onDataReceived(data: DataPointContainer) {
val heartRateBpm = data.getData(DataType.HEART_RATE_BPM)
Log.d(TAG, "💓 Received heart rate: ${heartRateBpm.first().value}")
trySendBlocking(MeasureMessage.MeasureData(heartRateBpm))
}
}
Log.d(TAG, "⌛ Registering for data...")
measureClient.registerMeasureCallback(DataType.HEART_RATE_BPM, callback)
awaitClose {
Log.d(TAG, "👋 Unregistering for data")
runBlocking {
measureClient.unregisterMeasureCallback(DataType.HEART_RATE_BPM, callback)
}
}
}
}
sealed class MeasureMessage {
class MeasureAvailability(val availability: DataTypeAvailability) : MeasureMessage()
class MeasureData(val data: List<SampleDataPoint<Double>>) : MeasureMessage()
}
Sau khi bạn tạo mô-đun Hilt để quản lý, hãy sử dụng đoạn mã sau:
@Module
@InstallIn(SingletonComponent::class)
internal object DataModule {
@Provides
@Singleton
fun provideHealthServices(@ApplicationContext context: Context): HealthServicesManager = HealthServicesManager(context)
}
bạn có thể chèn HealthServicesManager
như bất kỳ phần phụ thuộc Hilt nào khác.
HealthServicesManager
mới cung cấp phương thức heartRateMeasureFlow()
đăng ký một trình nghe cho thiết bị theo dõi tim và phát ra dữ liệu đã nhận.
Cho phép cập nhật dữ liệu trên thiết bị đeo
Việc cập nhật dữ liệu liên quan đến hoạt động thể dục cần có quyền BODY_SENSORS
. Nếu bạn
chưa thực hiện, hãy khai báo quyền BODY_SENSORS
trong
tệp kê khai của ứng dụng. Sau đó, hãy yêu cầu cấp quyền như trong đoạn mã sau:
val permissionState = rememberPermissionState(
permission = Manifest.permission.BODY_SENSORS,
onPermissionResult = { granted -> /* do something */ }
)
[...]
if (permissionState.status.isGranted) {
// do something
} else {
permissionState.launchPermissionRequest()
}
Nếu bạn kiểm thử ứng dụng trên một thiết bị thực, thì dữ liệu sẽ bắt đầu cập nhật.
Kể từ Wear OS 4, trình mô phỏng cũng tự động hiển thị dữ liệu kiểm thử. Trên trang trước các phiên bản, bạn có thể mô phỏng luồng dữ liệu từ cảm biến. Trong thiết bị đầu cuối hãy chạy lệnh ADB này:
adb shell am broadcast \
-a "whs.USE_SYNTHETIC_PROVIDERS" \
com.google.android.wearable.healthservices
Để xem nhiều giá trị tần số tim, hãy thử mô phỏng nhiều bài tập thể dục. Lệnh này mô phỏng hoạt động đi bộ:
adb shell am broadcast \
-a "whs.synthetic.user.START_WALKING" \
com.google.android.wearable.healthservices
Lệnh này mô phỏng hoạt động chạy:
adb shell am broadcast \
-a "whs.synthetic.user.START_RUNNING" \
com.google.android.wearable.healthservices
Để dừng mô phỏng dữ liệu, hãy chạy lệnh sau:
adb shell am broadcast -a \
"whs.USE_SENSOR_PROVIDERS" \
com.google.android.wearable.healthservices
Đọc dữ liệu tần số tim
Khi cấp quyền BODY_SENSORS
, bạn có thể đọc nhịp tim của người dùng
(heartRateMeasureFlow()
) trong HealthServicesManager
. Trong ứng dụng Wear OS
Giao diện người dùng, giá trị tần số tim hiện tại xuất hiện, được đo bằng cảm biến trên
thiết bị đeo của bạn.
Trong ViewModel
, hãy bắt đầu thu thập dữ liệu bằng cách sử dụng đối tượng luồng nhịp tim.
như minh hoạ trong đoạn mã sau:
val hr: MutableState<Double> = mutableStateOf(0.0)
[...]
healthServicesManager
.heartRateMeasureFlow()
.takeWhile { enabled.value }
.collect { measureMessage ->
when (measureMessage) {
is MeasureData -> {
val latestHeartRateValue = measureMessage.data.last().value
hr.value = latestHeartRateValue
}
is MeasureAvailability -> availability.value =
measureMessage.availability
}
}
Sử dụng đối tượng có thể kết hợp tương tự như sau để hiển thị dữ liệu trực tiếp trong giao diện người dùng của ứng dụng:
val heartRate by viewModel.hr
Text(
text = "Heart Rate: $heartRate",
style = MaterialTheme.typography.display1
)
Gửi dữ liệu đến một thiết bị cầm tay
Để gửi dữ liệu sức khoẻ và hoạt động thể dục đến một thiết bị cầm tay, hãy sử dụng DataClient
trong Dịch vụ sức khoẻ. Đoạn mã sau đây minh hoạ cách gửi trái tim
mà ứng dụng của bạn thu thập trước đây:
class HealthServicesManager(context: Context) {
private val dataClient by lazy { Wearable.getDataClient(context) }
[...]
suspend fun sendToHandheldDevice(heartRate: Int) {
try {
val result = dataClient
.putDataItem(PutDataMapRequest
.create("/heartrate")
.apply { dataMap.putInt("heartrate", heartRate) }
.asPutDataRequest()
.setUrgent())
.await()
Log.d(TAG, "DataItem saved: $result")
} catch (cancellationException: CancellationException) {
throw cancellationException
} catch (exception: Exception) {
Log.d(TAG, "Saving DataItem failed: $exception")
}
}
}
Nhận dữ liệu trên điện thoại
Để nhận dữ liệu trên điện thoại, hãy tạo một
WearableListenerService
:
@AndroidEntryPoint
class DataLayerListenerService : WearableListenerService() {
@Inject
lateinit var heartRateMonitor: HeartRateMonitor
override fun onDataChanged(dataEvents: DataEventBuffer) {
dataEvents.forEach { event ->
when (event.type) {
DataEvent.TYPE_CHANGED -> {
event.dataItem.run {
if (uri.path?.compareTo("/heartrate") == 0) {
val heartRate = DataMapItem.fromDataItem(this)
.dataMap.getInt(HR_KEY)
Log.d("DataLayerListenerService",
"New heart rate value received: $heartRate")
heartRateMonitor.send(heartRate)
}
}
}
DataEvent.TYPE_DELETED -> {
// DataItem deleted
}
}
}
}
}
Sau khi hoàn tất bước này, hãy lưu ý một vài chi tiết thú vị:
- Chú giải
@AndroidEntryPoint
cho phép chúng ta sử dụng Hilt trong lớp này @Inject lateinit var heartRateMonitor: HeartRateMonitor
thực sự sẽ chèn phần phụ thuộc vào lớp này- Lớp này sẽ triển khai
onDataChanged()
và nhận một tập hợp các sự kiện bạn có thể phân tích cú pháp và sử dụng
Logic HeartRateMonitor
sau đây cho phép bạn gửi nhịp tim nhận được
vào một phần khác trong cơ sở mã của ứng dụng:
class HeartRateMonitor {
private val datapoints = MutableSharedFlow<Int>(extraBufferCapacity = 10)
fun receive(): SharedFlow<Int> = datapoints.asSharedFlow()
fun send(hr: Int) {
datapoints.tryEmit(hr)
}
}
Xe buýt dữ liệu nhận các sự kiện từ phương thức onDataChanged()
và tạo các sự kiện đó
có sẵn cho trình quan sát dữ liệu bằng SharedFlow
.
Bit cuối cùng là khai báo Service
trong ứng dụng điện thoại
AndroidManifest.xml
:
<service
android:name=".DataLayerListenerService"
android:exported="true">
<intent-filter>
<!-- listeners receive events that match the action and data filters -->
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<data
android:host="*"
android:pathPrefix="/heartrate"
android:scheme="wear" />
</intent-filter>
</service>
Hiển thị dữ liệu theo thời gian thực trên thiết bị cầm tay
Trong phần ứng dụng chạy trên một thiết bị cầm tay, hãy chèn
HeartRateMonitor
vào hàm khởi tạo của mô hình khung hiển thị. HeartRateMonitor
này
đối tượng sẽ quan sát dữ liệu tần số tim và gửi nội dung cập nhật giao diện người dùng khi cần.