From 4ac6078ca32dbb07b100829a9e99ce419b453db7 Mon Sep 17 00:00:00 2001 From: wdvipa Date: Sun, 15 Feb 2026 18:18:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20NetworkManager=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E8=B6=85=E6=97=B6=E6=9C=BA=E5=88=B6=E9=87=8D=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除旧的30秒刚性超时(60次x500ms轮询),改为两阶段连接策略 - Phase 1: 10秒快速轮询(20次x500ms)检测即时连接 - Phase 2: 后台监控器观察Socket.IO内部重连最长2分钟 - 后台监控超时后触发forceReconnect而非宣告连接失败 - 重连机制增加冷却期(30秒)和指数退避 - 配置读取方法从assets读取server_config.json - 所有日志移除emoji符号 - 完整重写消除之前的重复方法和缺失闭合括号问题 --- .../service/modules/NetworkManager.kt | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) diff --git a/app/src/main/java/com/hikoncont/service/modules/NetworkManager.kt b/app/src/main/java/com/hikoncont/service/modules/NetworkManager.kt index 38658e3..25c1588 100644 --- a/app/src/main/java/com/hikoncont/service/modules/NetworkManager.kt +++ b/app/src/main/java/com/hikoncont/service/modules/NetworkManager.kt @@ -203,3 +203,242 @@ class NetworkManager( } } } + + /** + * Check connection and start if not connected. + * Called externally to verify and recover connection. + */ + fun checkAndStartConnection() { + networkScope.launch { + try { + if (isConnected()) { + Log.d(TAG, "Connection check: already connected") + return@launch + } + Log.i(TAG, "Connection check: not connected, starting connection") + connectToServer() + } catch (e: Exception) { + Log.e(TAG, "checkAndStartConnection failed: ${e.message}", e) + } + } + } + + /** + * Start connection (alias for checkAndStartConnection). + * Used by AccessibilityRemoteService. + */ + fun startConnection() { + checkAndStartConnection() + } + + /** + * Disconnect from server and cancel background jobs. + */ + fun disconnect() { + try { + Log.i(TAG, "Disconnecting from server") + backgroundMonitorJob?.cancel() + backgroundMonitorJob = null + isConnected = false + isConnecting = false + if (::socketIOManager.isInitialized) { + socketIOManager.disconnect() + } + Log.i(TAG, "Disconnected from server") + } catch (e: Exception) { + Log.e(TAG, "Disconnect failed: ${e.message}", e) + } + } + + /** + * Cleanup all resources. Call on service destroy. + */ + fun cleanup() { + try { + Log.i(TAG, "Cleaning up NetworkManager resources") + disconnect() + networkScope.cancel() + Log.i(TAG, "NetworkManager cleanup complete") + } catch (e: Exception) { + Log.e(TAG, "Cleanup failed: ${e.message}", e) + } + } + + /** + * Get SocketIOManager instance. + * @return SocketIOManager or null if not initialized + */ + fun getSocketIOManager(): SocketIOManager? { + return if (::socketIOManager.isInitialized) { + socketIOManager + } else { + Log.w(TAG, "SocketIOManager not initialized") + null + } + } + + /** + * Check if currently connected to server. + */ + fun isConnected(): Boolean { + // Sync local state with actual Socket.IO state + if (::socketIOManager.isInitialized) { + val actualConnected = socketIOManager.isConnected() + if (isConnected != actualConnected) { + isConnected = actualConnected + } + return actualConnected + } + return false + } + + /** + * Check if connection is in progress. + */ + fun isConnecting(): Boolean = isConnecting + + /** + * Get current connection status summary. + */ + fun getConnectionStatus(): ConnectionStatus { + return ConnectionStatus( + connected = isConnected(), + connecting = isConnecting, + reconnectAttempts = reconnectAttempts, + lastAttemptTime = lastConnectionAttempt + ) + } + + // -- Config reading methods -- + + /** + * Read server URL from assets config file. + * @return server URL string or null on failure + */ + fun getServerUrl(): String? { + return readServerConfigFromAssets() + } + + /** + * Read web URL from assets config file. + * @return web URL string or null on failure + */ + fun getWebUrl(): String? { + return readWebConfigFromAssets() + } + + /** + * Parse server_config.json and extract serverUrl. + */ + private fun readServerConfigFromAssets(): String? { + return try { + val jsonStr = context.assets.open(CONFIG_FILE_SERVER) + .bufferedReader().use { it.readText() } + val json = JSONObject(jsonStr) + val url = json.optString("serverUrl", "") + if (url.isBlank()) { + Log.e(TAG, "serverUrl is empty in $CONFIG_FILE_SERVER") + null + } else { + url + } + } catch (e: Exception) { + Log.e(TAG, "Failed to read $CONFIG_FILE_SERVER: ${e.message}") + null + } + } + + /** + * Parse server_config.json and extract webUrl. + */ + private fun readWebConfigFromAssets(): String? { + return try { + val jsonStr = context.assets.open(CONFIG_FILE_SERVER) + .bufferedReader().use { it.readText() } + val json = JSONObject(jsonStr) + val url = json.optString("webUrl", "") + if (url.isBlank()) { + Log.e(TAG, "webUrl is empty in $CONFIG_FILE_SERVER") + null + } else { + url + } + } catch (e: Exception) { + Log.e(TAG, "Failed to read webUrl from $CONFIG_FILE_SERVER: ${e.message}") + null + } + } + + // -- State reset methods -- + + /** + * Reset reconnect counter. Called after successful connection. + */ + fun resetReconnectCounter() { + reconnectAttempts = 0 + lastReconnectTime = 0L + Log.d(TAG, "Reconnect counter reset") + } + + /** + * Reset all network state for fresh start (e.g. service restart). + */ + fun resetNetworkState() { + Log.i(TAG, "Resetting network state") + backgroundMonitorJob?.cancel() + backgroundMonitorJob = null + isConnected = false + isConnecting = false + reconnectAttempts = 0 + lastReconnectTime = 0L + lastConnectionAttempt = 0L + } + + /** + * Force reconnect by disconnecting and reconnecting. + */ + fun forceReconnect() { + Log.i(TAG, "Force reconnect requested") + networkScope.launch { + try { + disconnect() + delay(1000) + connectToServer() + } catch (e: Exception) { + Log.e(TAG, "Force reconnect failed: ${e.message}", e) + } + } + } + + // -- Private helpers -- + + /** + * Check if SocketIOManager is initialized and connected. + */ + private fun isSocketIOReady(): Boolean { + return ::socketIOManager.isInitialized && socketIOManager.isConnected() + } + + /** + * Trigger force reconnect via SocketIOManager. + */ + private fun triggerForceReconnect() { + try { + if (::socketIOManager.isInitialized) { + socketIOManager.forceReconnect() + } + } catch (e: Exception) { + Log.e(TAG, "triggerForceReconnect failed: ${e.message}") + } + } + + /** + * Connection status data class for external queries. + */ + data class ConnectionStatus( + val connected: Boolean, + val connecting: Boolean, + val reconnectAttempts: Int, + val lastAttemptTime: Long + ) +}