fix: Thread→coroutine, PlacementGuideView 메인스레드 보장
- #2: PiezoMonitoringView raw Thread → measureScope.launch + Dispatchers.Default withContext(Main)으로 state 변경 보장, composable 떠나면 자동 취소 - #22: PlacementGuideView onMultiChannelComplete 콜백 → handler.post 감싸기 BLE Binder 스레드에서 Compose state 직접 변경 → 메인 스레드 보장 - nested handler.post 제거, throttle 시 직접 state 리셋 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-9
@@ -106,6 +106,8 @@ fun PiezoMonitoringView(appState: AppState) {
|
||||
return sorted.subList(1, sorted.size - 1).average()
|
||||
}
|
||||
|
||||
val measureScope = rememberCoroutineScope()
|
||||
|
||||
fun measure() {
|
||||
if (isMeasuring) {
|
||||
Log.w("PiezoMonitor", "measure() called while already measuring — SKIPPED")
|
||||
@@ -129,11 +131,12 @@ fun PiezoMonitoringView(appState: AppState) {
|
||||
bleManager.piezoCollector.onMultiChannelComplete = { channels ->
|
||||
Log.d("PiezoMonitor", "RECEIVED ${channels.size} channels: ${channels.map { "CH${it.channel}(${it.buffer.size})" }}")
|
||||
// 분석을 백그라운드에서 수행 후 UI 업데이트는 메인 스레드로
|
||||
Thread {
|
||||
measureScope.launch {
|
||||
kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.Default) {
|
||||
val analysisStart = System.currentTimeMillis()
|
||||
// channels를 로컬 복사 (Thread safety)
|
||||
val channelsCopy = channels.toList()
|
||||
handler.post {
|
||||
kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.Main) {
|
||||
lastMultiChannelResult.clear()
|
||||
lastMultiChannelResult.addAll(channelsCopy)
|
||||
}
|
||||
@@ -303,14 +306,17 @@ fun PiezoMonitoringView(appState: AppState) {
|
||||
}
|
||||
|
||||
// allWalls를 UI state에 저장 (ChannelPanel에서 사용)
|
||||
handler.post { lastAllWalls = allWalls.toList() }
|
||||
val wallsCopy = allWalls.toList()
|
||||
kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.Main) {
|
||||
lastAllWalls = wallsCopy
|
||||
}
|
||||
val finalRawVol = rawVolumeMl
|
||||
val finalTotalWalls = totalWallCount
|
||||
val analysisMs = System.currentTimeMillis() - analysisStart
|
||||
Log.d("PiezoMonitor", "Analysis took ${analysisMs}ms")
|
||||
|
||||
// UI 업데이트는 메인 스레드에서
|
||||
handler.post {
|
||||
kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.Main) {
|
||||
if (finalRawVol != null && finalRawVol > 0) {
|
||||
if (isAutoMeasuring) {
|
||||
if (allChannelsReceived) {
|
||||
@@ -346,16 +352,14 @@ fun PiezoMonitoringView(appState: AppState) {
|
||||
measureRetryCount = 0
|
||||
try { measureDoneSignal.value.complete(Unit) } catch (_: Exception) {}
|
||||
}
|
||||
}.start()
|
||||
} }
|
||||
}
|
||||
bleManager.piezoCollector.onLog = { msg -> Log.d("PiezoCollector", msg) }
|
||||
|
||||
if (!bleManager.sendChannelsOnly()) {
|
||||
// Throttle됨 — isMeasuring 리셋 (deadlock 방지)
|
||||
handler.post {
|
||||
isMeasuring = false
|
||||
try { measureDoneSignal.value.complete(Unit) } catch (_: Exception) {}
|
||||
}
|
||||
isMeasuring = false
|
||||
try { measureDoneSignal.value.complete(Unit) } catch (_: Exception) {}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -525,6 +525,8 @@ fun PlacementGuideView(appState: AppState) {
|
||||
|
||||
fun sendMeasureCommand() {
|
||||
bleManager.piezoCollector.onMultiChannelComplete = { channels ->
|
||||
// 메인 스레드에서 처리 (BLE 콜백은 Binder 스레드)
|
||||
handler.post {
|
||||
isScanning = false
|
||||
scanCount++
|
||||
|
||||
@@ -682,6 +684,7 @@ fun PlacementGuideView(appState: AppState) {
|
||||
direction = directionHint
|
||||
)
|
||||
}
|
||||
} // handler.post
|
||||
bleManager.piezoCollector.onLog = { msg -> Log.d("PlacementGuide", msg) }
|
||||
if (!bleManager.sendAllChannels()) {
|
||||
// Throttle됨 — isScanning 리셋 (deadlock 방지)
|
||||
|
||||
Reference in New Issue
Block a user