一、概述
在AR眼鏡開發中,設備連接是構建完整交互體驗的基礎環節。Rokid通過其CXR_M SDK提供了完整的藍牙和Wi-Fi連接解決方案,本文將深入解析其實現原理和代碼架構。
1.1 連接架構總覽
Rokid設備連接採用分層設計:
二、藍牙連接實現詳解
2.1 設備發現機制
設備發現是整個連接流程的起點,Rokid通過標準的Android Bluetooth API進行設備掃描,同時使用特定UUID過濾Rokid設備。
2.1.1 權限管理
class BluetoothHelper(
val context: AppCompatActivity,
val initStatus: (INIT_STATUS) -> Unit,
val deviceFound: () -> Unit) {
companion object {
// 權限定義
private val REQUIRED_PERMISSIONS = mutableListOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
add(Manifest.permission.BLUETOOTH_SCAN)
add(Manifest.permission.BLUETOOTH_CONNECT)
}
}.toTypedArray()
}
// 權限檢查入口
fun checkPermissions() {
initStatus.invoke(INIT_STATUS.NotStart)
context.requestPermissions(REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
context.registerReceiver(
bluetoothStateListener,
IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
)
}}
權限管理流程:
2.1.2 設備掃描實現
// 開始掃描@SuppressLint("MissingPermission")@RequiresPermission(Manifest.permission.BLUETOOTH_SCAN)fun startScan() {
scanResultMap.clear()
// 獲取已連接設備
val connectedList = getConnectedDevices()
for (device in connectedList) {
device.name?.let {
if (it.contains("Glasses", false)) {
bondedDeviceMap[it] = device
deviceFound.invoke()
}
}
}
// 獲取已配對設備
adapter?.bondedDevices?.forEach { d ->
d.name?.let {
if (it.contains("Glasses", false)) {
if (bondedDeviceMap[it] == null) {
bondedDeviceMap[it] = d
}
}
deviceFound.invoke()
}
}
// 開始BLE掃描
try {
scanner.startScan(
listOf<ScanFilter>(
ScanFilter.Builder()
.setServiceUuid(ParcelUuid.fromString("00009100-0000-1000-8000-00805f9b34fb"))
.build()
), ScanSettings.Builder().build(),
scanListener
)
} catch (e: Exception) {
Toast.makeText(context, "Scan Failed ${e.message}", Toast.LENGTH_SHORT).show()
}}
2.2 藍牙初始化與連接
2.2.1 初始化流程
/**
Init Bluetooth
*/fun initDevice(context: Context, device: BluetoothDevice){
CxrApi.getInstance().initBluetooth(context, device, object : BluetoothStatusCallback{
override fun onConnectionInfo(
socketUuid: String?,
macAddress: String?,
rokidAccount: String?,
glassesType: Int
) {
socketUuid?.let { uuid ->
macAddress?.let { address->
connect(context, uuid, address)
}?:run {
Log.e(TAG, "macAddress is null")
}
}?:run{
Log.e(TAG, "socketUuid is null")
}
}
override fun onConnected() {
// 連接成功處理
}
override fun onDisconnected() {
// 連接斷開處理
}
override fun onFailed(p0: ValueUtil.CxrBluetoothErrorCode?) {
// 錯誤處理
}
})}
2.2.2 連接建立
/**
Connect Bluetooth
*/fun connect(context: Context, socketUuid: String, macAddress: String){
CxrApi.getInstance().connectBluetooth(context, socketUuid, macAddress, object : BluetoothStatusCallback{
override fun onConnectionInfo(
socketUuid: String?,
macAddress: String?,
rokidAccount: String?,
glassesType: Int
) {
// 連接信息更新
}
override fun onConnected() {
Log.d(TAG, "Connected")
}
override fun onDisconnected() {
Log.d(TAG, "Disconnected")
}
override fun onFailed(p0: ValueUtil.CxrBluetoothErrorCode?) {
Log.e(TAG, "Failed")
}
})}
藍牙連接狀態機:
2.3 連接狀態管理
2.3.1 狀態查詢
/**
Get Connection Status
*/fun getConnectionStatus(): Boolean{
return CxrApi.getInstance().isBluetoothConnected}
2.3.2 狀態監聽
// Bluetooth State Listenerval bluetoothStateListener = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val action = intent?.action
if (action == BluetoothAdapter.ACTION_STATE_CHANGED) {
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
when (state) {
BluetoothAdapter.STATE_OFF -> {
initStatus.invoke(INIT_STATUS.NotStart)
bluetoothEnabled.postValue(false)
}
}
}
}}
2.4 資源管理與重連機制
2.4.1 資源釋放
/**
DeInit Bluetooth
*/fun deInit(){
CxrApi.getInstance().deinitBluetooth()}
// Release@SuppressLint("MissingPermission")fun release() {
context.unregisterReceiver(bluetoothStateListener)
stopScan()
permissionResult.postValue(false)
bluetoothEnabled.postValue(false)}
2.4.2 重連機制
重連使用與初始連接相同的API,但需要保存之前的連接參數:
/**
重連實現
*/fun reconnect(context: Context, savedSocketUuid: String, savedMacAddress: String) {
connect(context, savedSocketUuid, savedMacAddress)}
三、Wi-Fi連接實現詳解
Wi-Fi連接建立在藍牙連接基礎之上,主要用於高速數據傳輸。由於Wi-Fi模塊功耗較高,建議僅在需要傳輸大量數據時開啓。
3.1 Wi-Fi初始化
3.1.1 初始化流程
/**
Init Wifi
*/fun initWifi(): ValueUtil.CxrStatus?{
return CxrApi.getInstance().initWifiP2P(object : WifiP2PStatusCallback{
override fun onConnected() {
Log.d(TAG, "onConnected")
}
override fun onDisconnected() {
Log.d(TAG, "onDisconnected")
}
override fun onFailed(errorCode: ValueUtil.CxrWifiErrorCode?) {
// 錯誤處理
}
})}
Wi-Fi連接狀態圖:
3.2 Wi-Fi狀態管理
3.2.1 連接狀態查詢
/**
Get Wifi Connection Status
*/fun getWiFiConnectionStatus(): Boolean{
return CxrApi.getInstance().isWifiP2PConnected}
3.2.2 資源釋放
/**
Deinit Wifi
*/private fun deinitWifi(){
CxrApi.getInstance().deinitWifiP2P()}
四、完整連接流程與最佳實踐
4.1 完整連接時序圖
4.2 錯誤處理策略
4.2.1 藍牙錯誤處理
override fun onFailed(errorCode: ValueUtil.CxrBluetoothErrorCode?) {
when (errorCode) {
ValueUtil.CxrBluetoothErrorCode.PARAM_INVALID -> {
Log.e(TAG, "Invalid parameters provided")
}
ValueUtil.CxrBluetoothErrorCode.BLE_CONNECT_FAILED -> {
Log.e(TAG, "BLE connection failed")
}
ValueUtil.CxrBluetoothErrorCode.SOCKET_CONNECT_FAILED -> {
Log.e(TAG, "Socket connection failed")
}
ValueUtil.CxrBluetoothErrorCode.UNKNOWN -> {
Log.e(TAG, "Unknown bluetooth error")
}
else -> {
Log.e(TAG, "Unhandled error code: $errorCode")
}
}}
4.2.2 連接重試機制
建議實現指數退避的重試機制:
class ConnectionManager {
private var retryCount = 0
private val maxRetries = 3
private val baseDelay = 1000L // 1秒
fun connectWithRetry(context: Context, socketUuid: String, macAddress: String) {
connect(context, socketUuid, macAddress, object : BluetoothStatusCallback {
override fun onFailed(errorCode: ValueUtil.CxrBluetoothErrorCode?) {
if (retryCount < maxRetries) {
retryCount++
val delay = baseDelay * (2 pow retryCount) // 指數退避
Handler(Looper.getMainLooper()).postDelayed({
connectWithRetry(context, socketUuid, macAddress)
}, delay)
} else {
notifyConnectionFailed()
}
}
override fun onConnected() {
retryCount = 0
}
})
}}
4.3 性能優化建議
連接策略對比:
五、總結
Rokid CXR_M SDK提供了完整的設備連接解決方案,通過清晰的API設計和回調機制,讓開發者能夠快速實現藍牙和Wi-Fi連接功能。