diff --git a/.cursor/rules/connection-management.mdc b/.cursor/rules/connection-management.mdc index b93c988..6cae9f9 100644 --- a/.cursor/rules/connection-management.mdc +++ b/.cursor/rules/connection-management.mdc @@ -3,3 +3,42 @@ description: globs: alwaysApply: false --- +# 连接管理机制 + +## 连接状态流转 +连接状态在 [WKConnection.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/WKConnection.java) 中管理,遵循以下规则: + +### 有效的状态转换 +- 失败状态 -> 连接中/成功 +- 连接中 -> 成功/失败/无网络 +- 成功 -> 同步消息/被踢/失败 +- 同步消息 -> 成功/失败 +- 无网络 -> 连接中/失败 + +### 关键同步点 +所有状态转换都需要通过 `connectionLock` 同步锁保护: +```java +synchronized (connectionLock) { + // 状态转换操作 +} +``` + +## 重连机制 +重连策略采用指数退避算法: +- 基础延迟:1秒 +- 最大延迟:32秒 +- 最大重试次数:5次 + +## 连接关闭处理 +连接关闭流程: +1. 使用 `isClosing` 原子变量防止重复关闭 +2. 设置连接标记防止重连 +3. 清理连接资源 +4. 触发超时强制关闭 + +## 异常处理 +主要在 [ConnectionClient.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/ConnectionClient.java) 中处理: +- 连接异常 +- 连接超时 +- 心跳超时 +- 空闲超时 diff --git a/.cursor/rules/message-handling.mdc b/.cursor/rules/message-handling.mdc index b93c988..fdf526c 100644 --- a/.cursor/rules/message-handling.mdc +++ b/.cursor/rules/message-handling.mdc @@ -3,3 +3,36 @@ description: globs: alwaysApply: false --- +# 消息处理机制 + +## 消息发送流程 +在 [WKConnection.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/WKConnection.java) 中实现: + +### 发送前检查 +- 检查连接状态 +- 验证消息完整性 +- 处理附件(图片、视频等) + +### 消息队列管理 +使用 `sendingMsgHashMap` 管理发送中的消息: +- 消息重发机制 +- 发送超时处理 +- 发送状态追踪 + +### 消息同步 +两种同步模式: +- WRITE模式:完整消息同步 +- READ模式:仅同步会话列表 + +## 消息接收处理 +主要在 ConnectionClient 的 onData 方法中处理: +- 消息解析 +- 重复消息过滤 +- 消息排序 +- 存储和回调 + +## 特殊消息处理 +- 心跳消息(ping/pong) +- 连接确认消息 +- 踢下线消息 +- 同步完成消息 diff --git a/.cursor/rules/project-structure.mdc b/.cursor/rules/project-structure.mdc index b93c988..7d09bbf 100644 --- a/.cursor/rules/project-structure.mdc +++ b/.cursor/rules/project-structure.mdc @@ -3,3 +3,38 @@ description: globs: alwaysApply: false --- +# WuKongIM Android SDK 项目结构 + +WuKongIM Android SDK 是一个即时通讯SDK,主要包含以下核心组件: + +## 核心连接管理 +主要连接管理类在 [WKConnection.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/WKConnection.java) 中实现,负责: +- Socket连接的建立和维护 +- 消息的发送和接收 +- 重连机制 +- 心跳管理 + +## 连接状态处理 +连接状态定义在 [WKConnectStatus.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/type/WKConnectStatus.java) 中,包括: +- 连接成功(1) +- 连接失败(0) +- 被踢下线(2) +- 同步消息中(3) +- 连接中(4) +- 无网络(5) +- 同步完成(6) + +## 客户端连接处理 +[ConnectionClient.java](mdc:wkim/src/main/java/com/xinbida/wukongim/message/ConnectionClient.java) 处理具体的连接事件: +- 连接建立回调 +- 数据接收 +- 连接断开处理 +- 超时处理 +- 异常处理 + +## 应用配置 +[WKIMApplication.java](mdc:wkim/src/main/java/com/xinbida/wukongim/WKIMApplication.java) 管理SDK全局配置: +- 协议版本 +- 同步消息模式 +- 网络状态 +- 数据库配置 diff --git a/.gitignore b/.gitignore index 17f5d55..8cfc606 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,4 @@ .externalNativeBuild .cxx local.properties -/repository .cursor diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar new file mode 100644 index 0000000..fd79d8c Binary files /dev/null and b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar differ diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.md5 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.md5 new file mode 100644 index 0000000..5fc2a75 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.md5 @@ -0,0 +1 @@ +2a8fa0fa06d069610eed09005b343994 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.sha1 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.sha1 new file mode 100644 index 0000000..3ddd0fe --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.sha1 @@ -0,0 +1 @@ +34a3a10b015c40e16a76fe75cb974d675a9cbda6 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.sha256 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.sha256 new file mode 100644 index 0000000..17791cf --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.sha256 @@ -0,0 +1 @@ +eb0a4ee07731a3ef38f606b2978d16169a6f6388e3f33fa3a10c82f3b0c17bb0 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.sha512 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.sha512 new file mode 100644 index 0000000..004aade --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7-sources.jar.sha512 @@ -0,0 +1 @@ +d1a0aa32976653fa04696f6d42edbe8152b59fdaf0135350cdefa14d571212571e85412c8f026a46e43069aadad3603358b63e262635a3716c948dab7b056750 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar new file mode 100644 index 0000000..51aeca4 Binary files /dev/null and b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar differ diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.md5 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.md5 new file mode 100644 index 0000000..ed5e3de --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.md5 @@ -0,0 +1 @@ +195544eb25003c80879e6390c389f4b5 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.sha1 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.sha1 new file mode 100644 index 0000000..7b8d373 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.sha1 @@ -0,0 +1 @@ +d7a3b759fd22ae6bb0cc7710e87c9f01b197afd8 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.sha256 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.sha256 new file mode 100644 index 0000000..a7a524d --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.sha256 @@ -0,0 +1 @@ +2731b93ebd3aa8f457aa1dd2ac9634de1fb4641facabbc6683bc2b8575076c28 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.sha512 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.sha512 new file mode 100644 index 0000000..d48c18e --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.aar.sha512 @@ -0,0 +1 @@ +67d5a21983e722346be407c14a7d0dab5de051789cf7764dfaacd686e770e75a9f8377b49af1e5570e29d6a7ae38fedaec5af4c6b17f631116d665570b92fc5d \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module new file mode 100644 index 0000000..e4a6251 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module @@ -0,0 +1,140 @@ +{ + "formatVersion": "1.1", + "component": { + "group": "com.xinbida.wukongim", + "module": "WKIMLib_local", + "version": "1.0.7", + "attributes": { + "org.gradle.status": "release" + } + }, + "createdBy": { + "gradle": { + "version": "8.9" + } + }, + "variants": [ + { + "name": "releaseApiElements-published", + "attributes": { + "org.gradle.category": "library", + "org.gradle.usage": "java-api", + "org.jetbrains.kotlin.platform.type": "androidJvm" + }, + "files": [ + { + "name": "WKIMLib_local-1.0.7.aar", + "url": "WKIMLib_local-1.0.7.aar", + "size": 619369, + "sha512": "67d5a21983e722346be407c14a7d0dab5de051789cf7764dfaacd686e770e75a9f8377b49af1e5570e29d6a7ae38fedaec5af4c6b17f631116d665570b92fc5d", + "sha256": "2731b93ebd3aa8f457aa1dd2ac9634de1fb4641facabbc6683bc2b8575076c28", + "sha1": "d7a3b759fd22ae6bb0cc7710e87c9f01b197afd8", + "md5": "195544eb25003c80879e6390c389f4b5" + } + ] + }, + { + "name": "releaseRuntimeElements-published", + "attributes": { + "org.gradle.category": "library", + "org.gradle.usage": "java-runtime", + "org.jetbrains.kotlin.platform.type": "androidJvm" + }, + "dependencies": [ + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-bom", + "version": { + "requires": "1.9.22" + }, + "attributes": { + "org.gradle.category": "platform" + }, + "endorseStrictVersions": true + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib", + "version": { + "requires": "1.9.22" + } + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib-jdk8", + "version": { + "requires": "1.9.22" + } + }, + { + "group": "com.android.support", + "module": "multidex", + "version": { + "requires": "1.0.3" + } + }, + { + "group": "net.zetetic", + "module": "android-database-sqlcipher", + "version": { + "requires": "4.5.4" + } + }, + { + "group": "androidx.sqlite", + "module": "sqlite-ktx", + "version": { + "requires": "2.5.0" + } + }, + { + "group": "org.whispersystems", + "module": "curve25519-android", + "version": { + "requires": "0.5.0" + } + }, + { + "group": "org.whispersystems", + "module": "signal-protocol-android", + "version": { + "requires": "2.8.1" + } + } + ], + "files": [ + { + "name": "WKIMLib_local-1.0.7.aar", + "url": "WKIMLib_local-1.0.7.aar", + "size": 619369, + "sha512": "67d5a21983e722346be407c14a7d0dab5de051789cf7764dfaacd686e770e75a9f8377b49af1e5570e29d6a7ae38fedaec5af4c6b17f631116d665570b92fc5d", + "sha256": "2731b93ebd3aa8f457aa1dd2ac9634de1fb4641facabbc6683bc2b8575076c28", + "sha1": "d7a3b759fd22ae6bb0cc7710e87c9f01b197afd8", + "md5": "195544eb25003c80879e6390c389f4b5" + } + ] + }, + { + "name": "releaseSourcesElements-published", + "attributes": { + "org.gradle.category": "documentation", + "org.gradle.dependency.bundling": "external", + "org.gradle.docstype": "sources", + "org.gradle.libraryelements": "jar", + "org.gradle.usage": "java-runtime", + "org.jetbrains.kotlin.platform.type": "androidJvm" + }, + "files": [ + { + "name": "WKIMLib_local-1.0.7-sources.jar", + "url": "WKIMLib_local-1.0.7-sources.jar", + "size": 328517, + "sha512": "d1a0aa32976653fa04696f6d42edbe8152b59fdaf0135350cdefa14d571212571e85412c8f026a46e43069aadad3603358b63e262635a3716c948dab7b056750", + "sha256": "eb0a4ee07731a3ef38f606b2978d16169a6f6388e3f33fa3a10c82f3b0c17bb0", + "sha1": "34a3a10b015c40e16a76fe75cb974d675a9cbda6", + "md5": "2a8fa0fa06d069610eed09005b343994" + } + ] + } + ] +} diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.md5 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.md5 new file mode 100644 index 0000000..e5e50ee --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.md5 @@ -0,0 +1 @@ +18f875f433f4b46c3a8f82d2102b4efd \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.sha1 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.sha1 new file mode 100644 index 0000000..7202ad1 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.sha1 @@ -0,0 +1 @@ +5e3e6e61712a9bee911ec2191da0faef05f93430 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.sha256 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.sha256 new file mode 100644 index 0000000..c4bcd55 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.sha256 @@ -0,0 +1 @@ +b5c54156b3a952972c75cfed1f5dd52419426a975df9a863efa8eb36f1feae12 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.sha512 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.sha512 new file mode 100644 index 0000000..34b6a94 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.module.sha512 @@ -0,0 +1 @@ +0b3a1ba410c00e2566f0052b66121737164e4edc5defe64fd2aa7fd6e02ba05ccd9b2e0e13b603911d53a34ec9c10ee51fcd4c05719e19440c5da726484b45e3 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom new file mode 100644 index 0000000..0aa9dc0 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom @@ -0,0 +1,77 @@ + + + + + + + + 4.0.0 + com.xinbida.wukongim + WKIMLib_local + 1.0.7 + aar + WKIMLib + WuKong IM Android Library + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + org.jetbrains.kotlin + kotlin-bom + 1.9.22 + pom + import + + + + + + org.jetbrains.kotlin + kotlin-stdlib + 1.9.22 + runtime + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 1.9.22 + runtime + + + com.android.support + multidex + 1.0.3 + runtime + + + net.zetetic + android-database-sqlcipher + 4.5.4 + runtime + + + androidx.sqlite + sqlite-ktx + 2.5.0 + runtime + + + org.whispersystems + curve25519-android + 0.5.0 + runtime + + + org.whispersystems + signal-protocol-android + 2.8.1 + runtime + + + diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.md5 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.md5 new file mode 100644 index 0000000..1a86f75 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.md5 @@ -0,0 +1 @@ +ddc59ca3508344a630f0330f9c75801e \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.sha1 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.sha1 new file mode 100644 index 0000000..e1d94c7 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.sha1 @@ -0,0 +1 @@ +b351753e161fb1fde554f02adc966908b5d835b6 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.sha256 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.sha256 new file mode 100644 index 0000000..2751ebc --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.sha256 @@ -0,0 +1 @@ +67367cbfa088cc2f2c57a66720e040865b70aa718b6f9487eeae174688b311f4 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.sha512 b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.sha512 new file mode 100644 index 0000000..266e71f --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/1.0.7/WKIMLib_local-1.0.7.pom.sha512 @@ -0,0 +1 @@ +0ca8950dcf1398e81df06fb2c44015e10dac0d0ccc4d8db4e1e5eda302ee79c2727460d576f855c31bf8db5e669cdbb5d32897dce308009d069bc9e6d85126d9 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml b/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml new file mode 100644 index 0000000..ab3c9b6 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml @@ -0,0 +1,13 @@ + + + com.xinbida.wukongim + WKIMLib_local + + 1.0.7 + 1.0.7 + + 1.0.7 + + 20250524090841 + + diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.md5 b/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.md5 new file mode 100644 index 0000000..da8846e --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.md5 @@ -0,0 +1 @@ +2867162497a0539e3e3b0a1716f9feaa \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.sha1 b/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.sha1 new file mode 100644 index 0000000..a28480b --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.sha1 @@ -0,0 +1 @@ +e431ad1afdad3aff298476f1949e6dcb73bf52d2 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.sha256 b/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.sha256 new file mode 100644 index 0000000..17d5c72 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.sha256 @@ -0,0 +1 @@ +aa7f10d602787f0faf2fc1a55c04fd089c55313b1d22c30cf1aa24c24ab9a216 \ No newline at end of file diff --git a/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.sha512 b/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.sha512 new file mode 100644 index 0000000..6214cc3 --- /dev/null +++ b/repository/com/xinbida/wukongim/WKIMLib_local/maven-metadata.xml.sha512 @@ -0,0 +1 @@ +3c7de45f238ee5d29b5c331b4c90ef396a87cc7a36550eeba7aa475e86f77da8d20e3373d177d46845e8e8d26c1ec106d2855ee1298c0763788a2583325b638f \ No newline at end of file diff --git a/wkim/src/main/java/com/xinbida/wukongim/message/MessageHandler.java b/wkim/src/main/java/com/xinbida/wukongim/message/MessageHandler.java index 60d7f29..6a2daa3 100644 --- a/wkim/src/main/java/com/xinbida/wukongim/message/MessageHandler.java +++ b/wkim/src/main/java/com/xinbida/wukongim/message/MessageHandler.java @@ -40,6 +40,8 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.TimeUnit; /** * 5/21/21 11:25 AM @@ -102,20 +104,42 @@ public class MessageHandler { private volatile List receivedMsgList; private final Object receivedMsgListLock = new Object(); - private final Object cacheLock = new Object(); + private final ReentrantLock cacheLock = new ReentrantLock(true); // 使用公平锁 + private static final long LOCK_TIMEOUT = 2000; // 2秒超时 private byte[] cacheData = null; private int available_len; public void clearCacheData() { - synchronized (cacheLock) { - cacheData = null; - available_len = 0; + boolean locked = false; + try { + // 尝试获取锁,最多等待3秒 + locked = cacheLock.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS); + if (locked) { + cacheData = null; + available_len = 0; + } else { + WKLoggerUtils.getInstance().e(TAG, "获取锁超时,clearCacheData失败"); + } + } catch (InterruptedException e) { + WKLoggerUtils.getInstance().e(TAG, "clearCacheData等待锁被中断: " + e.getMessage()); + Thread.currentThread().interrupt(); + } finally { + if (locked) { + cacheLock.unlock(); + } } } synchronized void handlerOnlineBytes(INonBlockingConnection iNonBlockingConnection) { - synchronized (cacheLock) { + boolean locked = false; + try { + locked = cacheLock.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS); + if (!locked) { + WKLoggerUtils.getInstance().e(TAG, "获取锁超时,handlerOnlineBytes失败"); + return; + } + try { // 获取可用数据长度 available_len = iNonBlockingConnection.available(); @@ -160,12 +184,26 @@ public class MessageHandler { WKLoggerUtils.getInstance().e(TAG, "onData 中发生意外错误: " + e.getMessage()); clearCacheData(); } + } catch (InterruptedException e) { + WKLoggerUtils.getInstance().e(TAG, "handlerOnlineBytes等待锁被中断: " + e.getMessage()); + Thread.currentThread().interrupt(); + } finally { + if (locked) { + cacheLock.unlock(); + } } } synchronized void cutBytes(byte[] available_bytes, IReceivedMsgListener mIReceivedMsgListener) { - synchronized (cacheLock) { + boolean locked = false; + try { + locked = cacheLock.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS); + if (!locked) { + WKLoggerUtils.getInstance().e(TAG, "获取锁超时,cutBytes失败"); + return; + } + if (cacheData == null || cacheData.length == 0) cacheData = available_bytes; else { //如果上次还存在未解析完的消息将新数据追加到缓存数据中 @@ -265,6 +303,13 @@ public class MessageHandler { } } saveReceiveMsg(); + } catch (InterruptedException e) { + WKLoggerUtils.getInstance().e(TAG, "cutBytes等待锁被中断: " + e.getMessage()); + Thread.currentThread().interrupt(); + } finally { + if (locked) { + cacheLock.unlock(); + } } } diff --git a/wkim/src/main/java/com/xinbida/wukongim/message/WKConnection.java b/wkim/src/main/java/com/xinbida/wukongim/message/WKConnection.java index 817b2c3..82bce41 100644 --- a/wkim/src/main/java/com/xinbida/wukongim/message/WKConnection.java +++ b/wkim/src/main/java/com/xinbida/wukongim/message/WKConnection.java @@ -60,6 +60,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.locks.ReentrantLock; /** * 5/21/21 10:51 AM @@ -146,9 +147,9 @@ public class WKConnection { } }; - // 添加一个专门用于同步connection访问的锁对象 - public final Object connectionLock = new Object(); - + // 替换原有的 Object 锁 + public final ReentrantLock connectionLock = new ReentrantLock(true); // 使用公平锁 + private static final long LOCK_TIMEOUT = 3000; // 3秒超时 private final Handler mainHandler = new Handler(Looper.getMainLooper()); private static final long CONNECTION_CLOSE_TIMEOUT = 5000; // 5 seconds timeout @@ -548,15 +549,20 @@ public class WKConnection { //处理登录消息状态 private void handleLoginStatus(short status) { - WKLoggerUtils.getInstance().e(TAG, "Connection state transition: " + connectStatus + " -> " + status); - - synchronized (connectionLock) { + boolean locked = false; + try { + locked = tryLockWithTimeout(); + if (!locked) { + WKLoggerUtils.getInstance().e(TAG, "获取锁超时,handleLoginStatus失败"); + return; + } + + WKLoggerUtils.getInstance().e(TAG, "Connection state transition: " + connectStatus + " -> " + status); String reason = WKConnectReason.ConnectSuccess; if (status == WKConnectStatus.kicked) { reason = WKConnectReason.ReasonAuthFail; } - // Validate state transition if (!isValidStateTransition(connectStatus, status)) { WKLoggerUtils.getInstance().e(TAG, "Invalid state transition attempted: " + connectStatus + " -> " + status); return; @@ -566,22 +572,22 @@ public class WKConnection { WKIM.getInstance().getConnectionManager().setConnectionStatus(status, reason); if (status == WKConnectStatus.success) { - // Reset reconnection counters since we have a successful connection connCount = 0; isReConnecting = false; - - // Set to syncing state connectStatus = WKConnectStatus.syncMsg; WKIM.getInstance().getConnectionManager().setConnectionStatus(WKConnectStatus.syncMsg, WKConnectReason.SyncMsg); - - // Start timers and heartbeat before syncing startAll(); - // Handle message syncing based on mode if (WKIMApplication.getInstance().getSyncMsgMode() == WKSyncMsgMode.WRITE) { WKIM.getInstance().getMsgManager().setSyncOfflineMsg((isEnd, list) -> { if (isEnd) { - synchronized (connectionLock) { + boolean innerLocked = false; + try { + innerLocked = tryLockWithTimeout(); + if (!innerLocked) { + WKLoggerUtils.getInstance().e(TAG, "获取锁超时,setSyncOfflineMsg回调处理失败"); + return; + } if (connection != null && !isClosing.get()) { connectStatus = WKConnectStatus.success; MessageHandler.getInstance().saveReceiveMsg(); @@ -590,12 +596,22 @@ public class WKConnection { resendMsg(); WKIM.getInstance().getConnectionManager().setConnectionStatus(WKConnectStatus.success, WKConnectReason.ConnectSuccess); } + } finally { + if (innerLocked) { + connectionLock.unlock(); + } } } }); } else { WKIM.getInstance().getConversationManager().setSyncConversationListener(syncChat -> { - synchronized (connectionLock) { + boolean innerLocked = false; + try { + innerLocked = tryLockWithTimeout(); + if (!innerLocked) { + WKLoggerUtils.getInstance().e(TAG, "获取锁超时,setSyncConversationListener回调处理失败"); + return; + } if (connection != null && !isClosing.get()) { connectStatus = WKConnectStatus.success; WKIMApplication.getInstance().isCanConnect = true; @@ -603,6 +619,10 @@ public class WKConnection { resendMsg(); WKIM.getInstance().getConnectionManager().setConnectionStatus(WKConnectStatus.success, WKConnectReason.ConnectSuccess); } + } finally { + if (innerLocked) { + connectionLock.unlock(); + } } }); } @@ -612,13 +632,16 @@ public class WKConnection { WKIMApplication.getInstance().isCanConnect = false; stopAll(); } else { - // Only attempt reconnection if we're allowed to connect if (WKIMApplication.getInstance().isCanConnect) { reconnection(); } WKLoggerUtils.getInstance().e(TAG, "Login status: " + status); stopAll(); } + } finally { + if (locked) { + connectionLock.unlock(); + } } } @@ -655,36 +678,46 @@ public class WKConnection { public void sendMessage(WKBaseMsg mBaseMsg) { if (mBaseMsg == null) { - WKLoggerUtils.getInstance().w(TAG ,"sendMessage called with null mBaseMsg."); + WKLoggerUtils.getInstance().w(TAG, "sendMessage called with null mBaseMsg."); return; } - if (mBaseMsg.packetType != WKMsgType.CONNECT) { - if (connectStatus == WKConnectStatus.syncMsg) { - WKLoggerUtils.getInstance().i(TAG ," sendMessage: In syncMsg status, message not sent: " + mBaseMsg.packetType); + + boolean locked = false; + try { + locked = tryLockWithTimeout(); + if (!locked) { + WKLoggerUtils.getInstance().e(TAG, "获取锁超时,sendMessage失败"); return; } - if (connectStatus != WKConnectStatus.success) { - WKLoggerUtils.getInstance().w(TAG , " sendMessage: Not in success status (is " + connectStatus + "), attempting reconnection for: " + mBaseMsg.packetType); + + if (mBaseMsg.packetType != WKMsgType.CONNECT) { + if (connectStatus == WKConnectStatus.syncMsg) { + WKLoggerUtils.getInstance().i(TAG, " sendMessage: In syncMsg status, message not sent: " + mBaseMsg.packetType); + return; + } + if (connectStatus != WKConnectStatus.success) { + WKLoggerUtils.getInstance().w(TAG, " sendMessage: Not in success status (is " + connectStatus + "), attempting reconnection for: " + mBaseMsg.packetType); + reconnection(); + return; + } + } + + INonBlockingConnection currentConnection = this.connection; + if (currentConnection == null || !currentConnection.isOpen()) { + WKLoggerUtils.getInstance().w(TAG, " sendMessage: Connection is null or not open, attempting reconnection for: " + mBaseMsg.packetType); reconnection(); return; } - } - INonBlockingConnection currentConnection; - synchronized (connectionLock) { - currentConnection = this.connection; - } - - if (currentConnection == null || !currentConnection.isOpen()) { - WKLoggerUtils.getInstance().w(TAG ," sendMessage: Connection is null or not open, attempting reconnection for: " + mBaseMsg.packetType); - reconnection(); - return; - } - // Pass the local reference to MessageHandler - int status = MessageHandler.getInstance().sendMessage(currentConnection, mBaseMsg); - if (status == 0) { - WKLoggerUtils.getInstance().e(TAG, "发消息失败 (status 0 from MessageHandler), attempting reconnection for: " + mBaseMsg.packetType); - reconnection(); + int status = MessageHandler.getInstance().sendMessage(currentConnection, mBaseMsg); + if (status == 0) { + WKLoggerUtils.getInstance().e(TAG, "发消息失败 (status 0 from MessageHandler), attempting reconnection for: " + mBaseMsg.packetType); + reconnection(); + } + } finally { + if (locked) { + connectionLock.unlock(); + } } } private void removeSendingMsg() { @@ -846,8 +879,18 @@ public class WKConnection { } public boolean connectionIsNull() { - synchronized (connectionLock) { + boolean locked = false; + try { + locked = tryLockWithTimeout(); + if (!locked) { + WKLoggerUtils.getInstance().e(TAG, "获取锁超时,connectionIsNull检查失败"); + return true; // 保守起见,如果获取锁失败就认为连接为空 + } return connection == null || !connection.isOpen(); + } finally { + if (locked) { + connectionLock.unlock(); + } } } @@ -883,7 +926,14 @@ public class WKConnection { } public void stopAll() { - synchronized (connectionLock) { + boolean locked = false; + try { + locked = tryLockWithTimeout(); + if (!locked) { + WKLoggerUtils.getInstance().e(TAG, "获取锁超时,stopAll失败"); + return; + } + // 先设置连接状态为失败 WKIM.getInstance().getConnectionManager().setConnectionStatus(WKConnectStatus.fail, ""); // 清理连接相关资源 @@ -912,6 +962,7 @@ public class WKConnection { connAckTime = 0; lastMsgTime = 0; connCount = 0; + // 清空发送消息队列 if (sendingMsgHashMap != null) { sendingMsgHashMap.clear(); @@ -923,28 +974,38 @@ public class WKConnection { shutdownExecutor(); System.gc(); + } finally { + if (locked) { + connectionLock.unlock(); + } } } private void closeConnect() { final INonBlockingConnection connectionToCloseActual; - // 如果已经在关闭过程中,直接返回 if (!isClosing.compareAndSet(false, true)) { - WKLoggerUtils.getInstance().i(TAG , " Close operation already in progress"); + WKLoggerUtils.getInstance().i(TAG, " Close operation already in progress"); return; } - synchronized (connectionLock) { + boolean locked = false; + try { + locked = tryLockWithTimeout(); + if (!locked) { + WKLoggerUtils.getInstance().e(TAG, "获取锁超时,closeConnect失败"); + isClosing.set(false); + return; + } + if (connection == null) { isClosing.set(false); - WKLoggerUtils.getInstance().i(TAG , " closeConnect called but connection is already null."); + WKLoggerUtils.getInstance().i(TAG, " closeConnect called but connection is already null."); return; } connectionToCloseActual = connection; String connId = connectionToCloseActual.getId(); - // Mark connection for closure to prevent reconnection attempts try { connectionToCloseActual.setAttachment("closing_" + System.currentTimeMillis() + "_" + connId); } catch (Exception e) { @@ -953,7 +1014,11 @@ public class WKConnection { connection = null; connectionClient = null; - WKLoggerUtils.getInstance().i(TAG , " Connection object nulled, preparing for async close of: " + connId); + WKLoggerUtils.getInstance().i(TAG, " Connection object nulled, preparing for async close of: " + connId); + } finally { + if (locked) { + connectionLock.unlock(); + } } // Create a timeout handler to force close after timeout @@ -961,7 +1026,7 @@ public class WKConnection { try { if (connectionToCloseActual.isOpen()) { String connId = connectionToCloseActual.getId(); - WKLoggerUtils.getInstance().w(TAG , " Connection close timeout reached for: " + connId); + WKLoggerUtils.getInstance().w(TAG, " Connection close timeout reached for: " + connId); connectionToCloseActual.close(); } } catch (Exception e) { @@ -979,13 +1044,13 @@ public class WKConnection { try { if (connectionToCloseActual.isOpen()) { String connId = connectionToCloseActual.getId(); - WKLoggerUtils.getInstance().i(TAG , " Attempting to close connection: " + connId); + WKLoggerUtils.getInstance().i(TAG, " Attempting to close connection: " + connId); connectionToCloseActual.close(); // Remove the timeout handler since we closed successfully mainHandler.removeCallbacks(timeoutRunnable); - WKLoggerUtils.getInstance().i(TAG , " Successfully closed connection: " + connId); + WKLoggerUtils.getInstance().i(TAG, " Successfully closed connection: " + connId); } else { - WKLoggerUtils.getInstance().i(TAG , " Connection was already closed or not open when async close executed: " + connectionToCloseActual.getId()); + WKLoggerUtils.getInstance().i(TAG, " Connection was already closed or not open when async close executed: " + connectionToCloseActual.getId()); } } catch (IOException e) { WKLoggerUtils.getInstance().e(TAG, "IOException during async connection close for " + connectionToCloseActual.getId() + ": " + e.getMessage()); @@ -1008,4 +1073,14 @@ public class WKConnection { closeThread.setDaemon(true); closeThread.start(); } + + private boolean tryLockWithTimeout() { + try { + return connectionLock.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + WKLoggerUtils.getInstance().e(TAG, "获取锁被中断: " + e.getMessage()); + Thread.currentThread().interrupt(); + return false; + } + } } \ No newline at end of file