You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Kotlin开发移动端应用:寻求运动手环心率等数据的通用获取方案

通用运动手环心率数据获取实现(Kotlin Android)

由于没有跨品牌的通用官方API,绝大多数运动手环都基于**BLE(蓝牙低功耗)**传输健康数据,这是实现跨设备兼容的核心方向。以下是具体的Kotlin实现步骤:

一、权限配置与申请

首先在AndroidManifest.xml中声明必要权限:

<!-- 基础蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Android 12+ 新增权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- 声明BLE支持 -->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

在代码中动态申请权限(针对Android 6.0+):

private const val PERMISSION_REQUEST_CODE = 1001

val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT)
} else {
    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.BLUETOOTH_ADMIN)
}

if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE)
}

二、扫描并识别BLE手环

使用BluetoothLeScanner扫描周边BLE设备,可通过设备名称前缀或通用服务UUID过滤:

val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val bluetoothAdapter = bluetoothManager.adapter

val scanner = bluetoothAdapter.bluetoothLeScanner
val scanFilters = listOf(ScanFilter.Builder().setDeviceNamePrefix("Band").build())
val scanSettings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build()

scanner.startScan(scanFilters, scanSettings, scanCallback)

private val scanCallback = object : ScanCallback() {
    override fun onScanResult(callbackType: Int, result: ScanResult) {
        super.onScanResult(callbackType, result)
        // 找到目标手环,停止扫描并发起连接
        scanner.stopScan(this)
        connectToDevice(result.device)
    }
}

三、连接手环并订阅心率数据

通过GATT连接设备,发现服务后定位到通用心率特征,开启通知接收实时数据:

private lateinit var gatt: BluetoothGatt

private fun connectToDevice(device: BluetoothDevice) {
    gatt = device.connectGatt(this, false, gattCallback)
}

private val gattCallback = object : BluetoothGattCallback() {
    override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
        super.onConnectionStateChange(gatt, status, newState)
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            // 连接成功,发现服务
            gatt.discoverServices()
        }
    }

    override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
        super.onServicesDiscovered(gatt, status)
        // 获取心率服务(通用UUID:0000180d-0000-1000-8000-00805f9b34fb)
        val heartRateService = gatt.getService(UUID.fromString("0000180d-0000-1000-8000-00805f9b34fb"))
        heartRateService?.let { service ->
            // 获取心率特征(通用UUID:00002a37-0000-1000-8000-00805f9b34fb)
            val heartRateChar = service.getCharacteristic(UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb"))
            // 开启通知
            gatt.setCharacteristicNotification(heartRateChar, true)
            val descriptor = heartRateChar.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))
            descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
            gatt.writeDescriptor(descriptor)
        }
    }

    override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
        super.onCharacteristicChanged(gatt, characteristic)
        // 解析并处理心率数据
        val heartRate = parseHeartRateData(characteristic.value)
        runOnUiThread {
            // 更新UI或业务逻辑
        }
    }
}

四、解析心率原始数据

按BLE标准格式解析手环返回的字节数组:

private fun parseHeartRateData(data: ByteArray): Int {
    // 第一个字节为标志位,判断心率数据格式
    val flags = data[0].toInt()
    val is16BitFormat = (flags and 0x01) != 0
    return if (is16BitFormat) {
        // 16位格式,取前两个字节拼接
        ((data[1].toInt() and 0xFF) or (data[2].toInt() shl 8))
    } else {
        // 8位格式,取第二个字节
        data[1].toInt() and 0xFF
    }
}

关键注意事项

  • 部分厂商会自定义BLE特征UUID或对数据加密,通用方案可能失效,需结合厂商逆向文档或适配官方SDK核心逻辑。
  • Android后台环境下,BLE扫描和连接会受系统限制,需将应用加入电池优化白名单,并用前台服务维持连接。
  • 不同手环的心率推送频率差异较大,需根据设备实际情况调整数据处理逻辑。

内容的提问来源于stack exchange,提问作者aptech

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 06:25:31