上一篇聊了 ViewBinding 与 DataBinding,这篇进入一个相对小众但正在增长的领域——Wear OS。智能手表 App 的开发跟手机 App 差异不小,屏幕小、交互方式不同、性能限制大。这篇梳理 Wear OS 开发的核心要点。
Wear OS 的特殊性
屏幕小:通常 1.2-1.9 英寸,分辨率 326-454 PPI
圆形屏幕:大量设备是圆形表盘,UI 要适配圆形
输入方式:以触控和旋转表圈为主,没有传统键盘
电量受限:电池容量通常 300-400mAh,省电是第一优先级
始终显示(AOD):屏幕一直亮着,但功耗极低
独立 App:Wear OS 3+ 支持脱离手机独立运行
开发环境搭建
// build.gradle
dependencies {
implementation("androidx.wear:wear:1.3.0")
implementation("androidx.wear.compose:compose-material:1.4.0")
implementation("androidx.wear.compose:compose-foundation:1.4.0")
implementation("androidx.wear:wear-input:1.2.0-alpha02") // 旋转输入
}
Wear OS 有独立的 Compose 库,组件跟手机版不一样。
圆形屏幕适配
最基本的要求:UI 元素不能超出圆形区域。用 BoxWithConstraints 检测屏幕形状:
@Composable
fun WearApp() {
AppScaffold {
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
val isRound = this.constraints.let {
// 圆形设备检测
resources.configuration.screenLayout and Configuration.SCREENLAYOUT_ROUND_MASK
}
val padding = if (isRound) 32.dp else 8.dp
// 根据 isRound 调整间距
}
}
}
或者用 Modifier.fillMaxSize().wrapContentSize(align = Alignment.Center) 确保内容居中不超出圆形区域。
Compose for Wear OS
Wear OS 的 Compose 组件跟手机版不同:
@Composable
fun WatchScreen() {
// Wear OS 的 Scaffold
WearScaffold {
// Wear OS 专属组件
ScalingLazyColumn {
// 相当于 LazyColumn,但会缩放边缘 item
item { Chip(onClick = { /*...*/ }) { Text("设置") } }
item { Chip(onClick = { /*...*/ }) { Text("运动") } }
item { ToggleChip(
checked = true,
onCheckedChange = {},
label = { Text("蓝牙") }
) {} }
}
}
}
Wear OS 专属组件:
ScalingLazyColumn:列表组件,边缘 item 缩放,适配圆形屏幕Chip: Wear OS 的按钮,比 Material Button 更适合触摸ToggleChip:开关TimeText:顶部时间显示PositionIndicator:滚动指示器Vignette:圆形屏幕边缘渐变效果
旋转输入
很多 Wear OS 设备有物理表圈或旋转按钮:
val focusRequester = remember { FocusRequester() }
val rotaryModifier = Modifier.onRotaryScrollEvent {
// 处理旋转
val scrollAmount = it.verticalScrollPixels
// 转换为滚动距离
true // 消费事件
}.focusable().focusRequester(focusRequester)
LaunchedEffect(Unit) {
focusRequester.requestFocus() // 必须有焦点才能接收旋转事件
}
旋转输入配合 ScalingLazyColumn 的 rememberScalingLazyListState() 使用,可以让列表跟随旋转滚动。
始终显示(AOD)
AOD 模式下屏幕亮度极低,只显示时间和基本信息。需要监听 AOD 状态:
val ambientModeSupport = remember { AmbientModeSupport.AmbientController(...) }
@Composable
fun AmbientScreen() {
val isAmbient by ambientModeSupport.isAmbient.collectAsState()
if (isAmbient) {
// AOD 模式:只显示时间,用低对比度
Text(
text = timeText,
color = Color.Gray,
modifier = Modifier.fillMaxSize()
)
} else {
// 正常模式:完整 UI
NormalScreen()
}
}
AOD 的关键:降低刷新率(从 60fps 降到 1fps),减少绘制元素,颜色降为灰度。
省电策略
减少唤醒频率。用
WorkManager或AlarmManager做定时任务,不要持续后台运行传感器采样降频。心率传感器 5 秒一次就够了,不需要连续采样
网络请求合并。手表网络模块功耗大,批量请求比逐条请求省电
避免动画。Wear OS 上动画是最费电的操作之一
善用 AOD。App 进入后台时立即切到 AOD 模式
与手机通信
Wear OS App 可以通过 MessageClient 和 DataClient 与手机端 App 通信:
// 发送消息
val messageClient = Wearable.getMessageClient(context)
messageClient.sendMessage(
nodeId, "/sync_data", data.toByteArray()
).addOnSuccessListener {
// 发送成功
}
// 接收消息
class MessageReceiver : MessageClient.OnMessageReceivedListener {
override fun onMessageReceived(event: MessageEvent) {
when (event.path) {
"/sync_data" -> {
val data = String(event.data)
// 处理
}
}
}
}
// DataClient 同步数据(持久化)
val dataClient = Wearable.getDataClient(context)
val putDataReq = PutDataMapRequest.create("/config").apply {
dataMap.putString("key", "value")
}.asPutDataRequest().setUrgent()
dataClient.putDataItem(putDataReq)
手机和手表需要同一个 Application ID(在 Google Play Console 注册),通过 Google Play Services 的 Wearable API 通信。
独立 App
Wear OS 3+ 支持完全独立的 App,不需要配对手机。独立 App 的注意点:
网络请求:手表可以直接用 WiFi 或 LTE(如果有)
认证:手表上做登录体验差,建议通过手机端授权后同步 token 到手表
存储:手表存储空间有限(通常 4-8GB),注意清理缓存
权限:手表上申请权限要更克制,频繁弹窗体验极差
测试和调试
# 创建 Wear OS 模拟器
# AVD Manager → Wear OS → Round 或 Square
真实设备调试时,手表通过 WiFi 或蓝牙连接。蓝牙调试延迟高,WiFi 好一些。
小结
Wear OS 开发的核心是适配小屏和圆形屏幕 + 省电。Wear Compose 库提供了专用组件,不要用手机版的 Compose 组件。旋转输入和 AOD 是两个 Wear OS 独有的特性,不做适配的话用户体验会差很多。
下一篇聊聊 Android 逆向工程实战。