fix: NetworkManager连接超时机制重写

- 移除旧的30秒刚性超时(60次x500ms轮询),改为两阶段连接策略
- Phase 1: 10秒快速轮询(20次x500ms)检测即时连接
- Phase 2: 后台监控器观察Socket.IO内部重连最长2分钟
- 后台监控超时后触发forceReconnect而非宣告连接失败
- 重连机制增加冷却期(30秒)和指数退避
- 配置读取方法从assets读取server_config.json
- 所有日志移除emoji符号
- 完整重写消除之前的重复方法和缺失闭合括号问题
This commit is contained in:
wdvipa
2026-02-15 18:18:23 +08:00
parent 5b2ddd7730
commit 4ac6078ca3

View File

@@ -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
)
}