馃摗 Network Event Bus

KNETNetworkEventBus to centralna magistrala zdarze艅 sieciowych. Umo偶liwia reagowanie na zdarzenia HTTP w ca艂ej aplikacji.

馃挕 Kiedy u偶ywa膰?
  • Globalne logowanie request贸w
  • Analytics i metryki
  • Centralna obs艂uga b艂臋d贸w
  • UI loading indicators
  • Token refresh przy 401
  • Toast/Snackbar przy b艂臋dach

馃摝 Import

import rip.nerd.kitsunenet.util.KNETNetworkEventBus
import rip.nerd.kitsunenet.util.KNETNetworkEventBus.NetworkEvent
import rip.nerd.kitsunenet.util.collectIn

馃殌 Szybki start

val eventBus = KNETNetworkEventBus.create()

// Dodaj jako interceptor
val client = KNETClient.builder()
    .addInterceptor(eventBus.interceptor())
    .build()

// Obserwuj zdarzenia (Flow)
lifecycleScope.launch {
    eventBus.events.collect { event ->
        when (event) {
            is NetworkEvent.RequestStarted -> showLoading()
            is NetworkEvent.RequestCompleted -> {
                hideLoading()
                if (!event.isSuccess) showError()
            }
            is NetworkEvent.RequestFailed -> {
                hideLoading()
                showError(event.error.message)
            }
            else -> {}
        }
    }
}

馃搳 Typy zdarze艅

sealed class NetworkEvent {
    // Request rozpocz臋ty
    data class RequestStarted(
        val id: Long,
        val timestamp: Long,
        val request: KNETRequest
    )

    // Request zako艅czony
    data class RequestCompleted(
        val id: Long,
        val timestamp: Long,
        val request: KNETRequest,
        val response: KNETResponse,
        val durationMs: Long
    ) {
        val statusCode: Int
        val isSuccess: Boolean
    }

    // Request nieudany (wyj膮tek)
    data class RequestFailed(
        val id: Long,
        val timestamp: Long,
        val request: KNETRequest,
        val error: Throwable,
        val durationMs: Long
    )

    // Wolny request
    data class SlowRequest(
        val id: Long,
        val timestamp: Long,
        val request: KNETRequest,
        val response: KNETResponse?,
        val durationMs: Long,
        val thresholdMs: Long
    )
}

馃幆 Filtered Flows

// Tylko errors
lifecycleScope.launch {
    eventBus.errors.collect { event ->
        analytics.logError(event.error)
    }
}

// Tylko completed
lifecycleScope.launch {
    eventBus.requestsCompleted.collect { event ->
        metrics.recordLatency(event.durationMs)
    }
}

// Tylko slow requests
lifecycleScope.launch {
    eventBus.slowRequests.collect { event ->
        Log.w("Perf", "Slow: ${event.request.url} (${event.durationMs}ms)")
    }
}

馃敂 Callback Listeners

// Na konkretny status code
val unsubscribe401 = eventBus.onStatusCode(401) { event ->
    // Token wygas艂 - refresh
    authManager.refreshToken()
}

// Na 401 Unauthorized (shortcut)
eventBus.onUnauthorized { event ->
    navigateToLogin()
}

// Na 403 Forbidden
eventBus.onForbidden { event ->
    showAccessDenied()
}

// Na 404 Not Found
eventBus.onNotFound { event ->
    showNotFound()
}

// Na wszystkie server errors (5xx)
eventBus.onServerError { event ->
    showServerError()
}

// Na wszystkie b艂臋dy (4xx i 5xx)
eventBus.onError { event ->
    if (event.statusCode in 500..599) {
        showServerError()
    } else {
        showClientError()
    }
}

// Anuluj subskrypcj臋
unsubscribe401()

馃敡 Konfiguracja

// Custom slow threshold
val eventBus = KNETNetworkEventBus.create()
val interceptor = eventBus.interceptor(slowThresholdMs = 2000)

// Singleton
val eventBus = KNETNetworkEventBus.instance

馃搳 Statystyki

val stats = eventBus.getStats()

println("Total requests: ${stats.totalRequests}")
println("Successful: ${stats.totalSuccessful}")
println("Failed: ${stats.totalFailed}")
println("Slow: ${stats.totalSlowRequests}")
println("Success rate: ${stats.successRate}%")
println("Active listeners: ${stats.activeListeners}")

// Reset
eventBus.resetStats()

馃挕 Praktyczne przyk艂ady

Global Loading Indicator

class LoadingManager(private val eventBus: KNETNetworkEventBus) {

    private var activeRequests = AtomicInteger(0)
    val isLoading: StateFlow<Boolean> get() = _isLoading
    private val _isLoading = MutableStateFlow(false)

    init {
        CoroutineScope(Dispatchers.Main).launch {
            eventBus.events.collect { event ->
                when (event) {
                    is NetworkEvent.RequestStarted -> {
                        activeRequests.incrementAndGet()
                        _isLoading.value = true
                    }
                    is NetworkEvent.RequestCompleted,
                    is NetworkEvent.RequestFailed -> {
                        if (activeRequests.decrementAndGet() == 0) {
                            _isLoading.value = false
                        }
                    }
                    else -> {}
                }
            }
        }
    }
}

// W UI
loadingManager.isLoading.collect { isLoading ->
    progressBar.isVisible = isLoading
}

Centralized Error Handling

class ErrorHandler(
    private val eventBus: KNETNetworkEventBus,
    private val snackbarHost: SnackbarHostState
) {
    init {
        eventBus.onError { event ->
            val message = when (event.statusCode) {
                401 -> "Sesja wygas艂a. Zaloguj si臋 ponownie."
                403 -> "Brak dost臋pu do tego zasobu."
                404 -> "Nie znaleziono."
                in 500..599 -> "B艂膮d serwera. Spr贸buj p贸藕niej."
                else -> "Wyst膮pi艂 b艂膮d: ${event.statusCode}"
            }

            CoroutineScope(Dispatchers.Main).launch {
                snackbarHost.showSnackbar(message)
            }
        }

        eventBus.onUnauthorized {
            // Navigate to login
            navigationController.navigate("login")
        }
    }
}

Analytics

class NetworkAnalytics(
    private val eventBus: KNETNetworkEventBus,
    private val analytics: FirebaseAnalytics
) {
    init {
        CoroutineScope(Dispatchers.IO).launch {
            eventBus.requestsCompleted.collect { event ->
                analytics.logEvent("http_request") {
                    param("url", event.request.url)
                    param("method", event.request.method)
                    param("status", event.statusCode.toLong())
                    param("duration_ms", event.durationMs)
                    param("success", event.isSuccess.toString())
                }
            }
        }

        CoroutineScope(Dispatchers.IO).launch {
            eventBus.errors.collect { event ->
                analytics.logEvent("http_error") {
                    param("url", event.request.url)
                    param("error", event.error.message ?: "unknown")
                }
            }
        }
    }
}

Performance Monitoring

class PerformanceMonitor(private val eventBus: KNETNetworkEventBus) {

    init {
        eventBus.interceptor(slowThresholdMs = 3000)

        CoroutineScope(Dispatchers.IO).launch {
            eventBus.slowRequests.collect { event ->
                // Alert do monitoring
                monitoring.alert(
                    level = AlertLevel.WARNING,
                    title = "Slow HTTP Request",
                    message = "${event.request.method} ${event.request.url}",
                    data = mapOf(
                        "duration_ms" to event.durationMs,
                        "threshold_ms" to event.thresholdMs
                    )
                )
            }
        }
    }
}

ViewModel Integration

class MainViewModel(
    private val eventBus: KNETNetworkEventBus
) : ViewModel() {

    private val _networkState = MutableStateFlow<NetworkState>(NetworkState.Idle)
    val networkState: StateFlow<NetworkState> = _networkState

    init {
        eventBus.collectIn(viewModelScope) { event ->
            _networkState.value = when (event) {
                is NetworkEvent.RequestStarted -> NetworkState.Loading
                is NetworkEvent.RequestCompleted -> {
                    if (event.isSuccess) NetworkState.Success
                    else NetworkState.Error(event.statusCode)
                }
                is NetworkEvent.RequestFailed -> NetworkState.Error(0)
                else -> _networkState.value
            }
        }
    }
}

sealed class NetworkState {
    object Idle : NetworkState()
    object Loading : NetworkState()
    object Success : NetworkState()
    data class Error(val code: Int) : NetworkState()
}

馃敆 API Reference

KNETNetworkEventBus

Property/MetodaOpis
eventsFlow wszystkich zdarze艅
requestsStartedFlow RequestStarted
requestsCompletedFlow RequestCompleted
errorsFlow RequestFailed
slowRequestsFlow SlowRequest
interceptor(slowThresholdMs)Tworzy interceptor
onStatusCode(code, callback)Listener na status
onUnauthorized(callback)Listener na 401
onForbidden(callback)Listener na 403
onNotFound(callback)Listener na 404
onServerError(callback)Listener na 5xx
onError(callback)Listener na b艂臋dy
getStats()Statystyki
clearListeners()Usuwa listener贸w

馃摎 Zobacz te偶