perf(#281): fix PERF-13/14/15/16 — main-thread settings, 100Hz polling, leaked scope, WS disconnect (#359)

* perf(#281): fix PERF-13/14/15/16 — main-thread settings load, 100Hz polling, leaked scope, WS disconnect

* fix(#281): fix SettingsViewModelTest failures from async init dispatcher

---------

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-06-25 20:39:52 +03:00
1 parent 9297413b2a
commit 3399880621
5 files changed
+81 -46

No files matched your search

@@ -9,7 +9,11 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.RemoteInput
import com.m57.hermescontrol.R
import com.m57.hermescontrol.data.ws.HermesWsClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout
open class NotificationReplyReceiver : BroadcastReceiver() {
companion object {
@@ -17,6 +21,10 @@ open class NotificationReplyReceiver : BroadcastReceiver() {
const val EXTRA_SESSION_ID = "extra_session_id"
}
// Reusable scope for async reply processing — avoids creating a new
// unmanaged CoroutineScope per broadcast fire. (PERF-15)
private val replyScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
/**
* Test-friendly wrapper for [BroadcastReceiver.goAsync] which is `final`
* (Java) and cannot be mocked or overridden directly. Tests override this
@@ -49,9 +57,9 @@ open class NotificationReplyReceiver : BroadcastReceiver() {
if (!replyText.isNullOrBlank() && !sessionId.isNullOrBlank()) {
val pendingResult = goAsyncCompat()
kotlinx.coroutines.CoroutineScope(kotlinx.coroutines.Dispatchers.IO).launch {
replyScope.launch {
try {
kotlinx.coroutines.withTimeout(5000L) {
withTimeout(5000L) {
val db =
com.m57.hermescontrol.data.local.HermesDatabase
.get(context)