From b3fb02c2703639fd8c6259766e90df0759e4a37a Mon Sep 17 00:00:00 2001 From: km2023 Date: Wed, 20 May 2026 14:08:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(sdk):=20=E5=90=8C=E6=AD=A5=20iOS=20?= =?UTF-8?q?=E2=80=94=20=E5=B8=A7=E9=97=B4=E9=9A=94=20frameIntervalMs=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=8C=96=EF=BC=88=E9=BB=98=E8=AE=A4=2035ms?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1:1 复刻 iOS:DuooomiBleConfig 加 frameIntervalMs,BleProtocolService 构造期注入,sendFrames 改用 coerceIn(0,500)。清掉 FRAME_INTERVAL_MS 常量。 demo 加 ConfigField + SharedPreferences 持久化。 越界 warn 字符串与 iOS 逐字一致。 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../kotlin/com/duooomi/ble/demo/DemoScreen.kt | 6 ++++-- .../kotlin/com/duooomi/ble/demo/MainActivity.kt | 11 ++++++++--- .../kotlin/com/duooomi/ble/DuooomiBleConfig.kt | 15 ++++++++++++++- .../main/kotlin/com/duooomi/ble/DuooomiBleSDK.kt | 2 +- .../com/duooomi/ble/protocol/ProtocolConstants.kt | 1 - .../duooomi/ble/services/BleProtocolService.kt | 8 ++++++-- 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/demo/src/main/kotlin/com/duooomi/ble/demo/DemoScreen.kt b/demo/src/main/kotlin/com/duooomi/ble/demo/DemoScreen.kt index ae2ec73..5957f2d 100644 --- a/demo/src/main/kotlin/com/duooomi/ble/demo/DemoScreen.kt +++ b/demo/src/main/kotlin/com/duooomi/ble/demo/DemoScreen.kt @@ -69,6 +69,7 @@ fun DemoScreen( var configBrand by remember(sdk) { mutableStateOf(config.brand) } var configApiKey by remember(sdk) { mutableStateOf(config.apiKey) } var configScanPrefix by remember(sdk) { mutableStateOf(config.scanPrefix) } + var configFrameIntervalMs by remember(sdk) { mutableStateOf(config.frameIntervalMs) } // Local state var userId by remember { mutableStateOf("Cjgex9kTTTJq9u9RMOnc5hiIZTvrOOq3") } @@ -137,11 +138,12 @@ fun DemoScreen( ConfigField("brand", configBrand) { configBrand = it } ConfigField("apiKey", configApiKey) { configApiKey = it } ConfigField("scanPrefix", configScanPrefix) { configScanPrefix = it } + ConfigField("frameIntervalMs", configFrameIntervalMs) { configFrameIntervalMs = it } Button( onClick = { - onReload(DemoConfig(configBrand, configApiKey, configScanPrefix)) + onReload(DemoConfig(configBrand, configApiKey, configScanPrefix, configFrameIntervalMs)) log( - "SDK reloaded with brand=$configBrand, prefix=$configScanPrefix", + "SDK reloaded with brand=$configBrand, prefix=$configScanPrefix, frameMs=$configFrameIntervalMs", LogLevel.SUCCESS ) }, diff --git a/demo/src/main/kotlin/com/duooomi/ble/demo/MainActivity.kt b/demo/src/main/kotlin/com/duooomi/ble/demo/MainActivity.kt index 8f72742..293ab25 100644 --- a/demo/src/main/kotlin/com/duooomi/ble/demo/MainActivity.kt +++ b/demo/src/main/kotlin/com/duooomi/ble/demo/MainActivity.kt @@ -38,7 +38,8 @@ class MainActivity : ComponentActivity() { brand = prefs.getString(KEY_BRAND, DemoSecrets.BRAND) ?: DemoSecrets.BRAND, apiKey = prefs.getString(KEY_API_KEY, DemoSecrets.API_KEY) ?: DemoSecrets.API_KEY, scanPrefix = prefs.getString(KEY_SCAN_PREFIX, DemoSecrets.SCAN_NAME_PREFIX) - ?: DemoSecrets.SCAN_NAME_PREFIX + ?: DemoSecrets.SCAN_NAME_PREFIX, + frameIntervalMs = prefs.getString(KEY_FRAME_INTERVAL_MS, "35") ?: "35" ) setContent { @@ -70,7 +71,8 @@ class MainActivity : ComponentActivity() { config = DuooomiBleConfig( apiKey = c.apiKey, brand = c.brand, - scanNamePrefix = c.scanPrefix + scanNamePrefix = c.scanPrefix, + frameIntervalMs = c.frameIntervalMs.toIntOrNull() ?: 35 ) ) @@ -79,6 +81,7 @@ class MainActivity : ComponentActivity() { .putString(KEY_BRAND, c.brand) .putString(KEY_API_KEY, c.apiKey) .putString(KEY_SCAN_PREFIX, c.scanPrefix) + .putString(KEY_FRAME_INTERVAL_MS, c.frameIntervalMs) .apply() } @@ -104,11 +107,13 @@ class MainActivity : ComponentActivity() { const val KEY_BRAND = "demo.config.brand" const val KEY_API_KEY = "demo.config.apiKey" const val KEY_SCAN_PREFIX = "demo.config.scanPrefix" + const val KEY_FRAME_INTERVAL_MS = "demo.config.frameIntervalMs" } } data class DemoConfig( val brand: String, val apiKey: String, - val scanPrefix: String + val scanPrefix: String, + val frameIntervalMs: String ) diff --git a/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleConfig.kt b/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleConfig.kt index f8285b1..021d9c5 100644 --- a/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleConfig.kt +++ b/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleConfig.kt @@ -1,5 +1,7 @@ package com.duooomi.ble +import com.duooomi.ble.utils.BleLog + /** * SDK 全局配置,在初始化时一次性传入。 */ @@ -19,8 +21,19 @@ data class DuooomiBleConfig( /** ANI 转换高度(像素) */ val aniHeight: Int = 360, /** ANI 转换帧率 */ - val aniFps: String = "24" + val aniFps: String = "24", + /** + * 协议帧之间的发送间隔(毫秒)。合法区间 `[0, 500]`,越界仅 warn,使用点会 clamp。 + * 集成方一般无需调整;调高用于排查接收端丢帧/拥塞。默认 35。 + */ + val frameIntervalMs: Int = 35 ) { + init { + if (frameIntervalMs !in 0..500) { + BleLog.w("frameIntervalMs=$frameIntervalMs out of [0,500], will clamp at use site", "Config") + } + } + val normalizedCdnHost: String get() = if (cdnHost.endsWith("/")) cdnHost else "$cdnHost/" } diff --git a/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleSDK.kt b/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleSDK.kt index a2b77c8..173fe85 100644 --- a/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleSDK.kt +++ b/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleSDK.kt @@ -71,7 +71,7 @@ class DuooomiBleSDK(context: Context, val config: DuooomiBleConfig) { // MARK: - Internal Services private val bleClient = BleClient(context) - private val protocolService = BleProtocolService(bleClient) + private val protocolService = BleProtocolService(bleClient, config.frameIntervalMs) private val timeProvider = BeijingTimeProvider() private val deviceInfoService = DeviceInfoService(protocolService, timeProvider) private val fileTransferService = FileTransferService(protocolService) diff --git a/sdk/src/main/kotlin/com/duooomi/ble/protocol/ProtocolConstants.kt b/sdk/src/main/kotlin/com/duooomi/ble/protocol/ProtocolConstants.kt index 3fe0cab..3ae3f40 100644 --- a/sdk/src/main/kotlin/com/duooomi/ble/protocol/ProtocolConstants.kt +++ b/sdk/src/main/kotlin/com/duooomi/ble/protocol/ProtocolConstants.kt @@ -20,7 +20,6 @@ object FrameConstants { const val MAX_DATA_SIZE = 496 const val HEADER_SIZE = 8 const val FOOTER_SIZE = 1 - const val FRAME_INTERVAL_MS = 35L const val SCREEN_SIZE = 360 } diff --git a/sdk/src/main/kotlin/com/duooomi/ble/services/BleProtocolService.kt b/sdk/src/main/kotlin/com/duooomi/ble/services/BleProtocolService.kt index 4d68ea0..e646736 100644 --- a/sdk/src/main/kotlin/com/duooomi/ble/services/BleProtocolService.kt +++ b/sdk/src/main/kotlin/com/duooomi/ble/services/BleProtocolService.kt @@ -9,7 +9,10 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import org.json.JSONObject -internal class BleProtocolService(private val client: BleClient) { +internal class BleProtocolService( + private val client: BleClient, + private val frameIntervalMs: Int +) { private data class FragmentSession( val total: Int, @@ -69,9 +72,10 @@ internal class BleProtocolService(private val client: BleClient) { val total = frames.size BleLog.d("Sending frames: total=$total, maxChunk=$safeMaxDataSize", "Protocol") + val effectiveInterval = frameIntervalMs.coerceIn(0, 500).toLong() for ((index, frame) in frames.withIndex()) { client.writeWithoutResponse(frame) - delay(FrameConstants.FRAME_INTERVAL_MS) + delay(effectiveInterval) onProgress?.invoke((index + 1).toDouble() / total.toDouble()) } BleLog.i("Frames sent: total=$total", "Protocol")