fix:优化连接

This commit is contained in:
SL 2025-05-24 17:17:03 +08:00
parent 61dd373418
commit 16c6ca8d57
31 changed files with 533 additions and 57 deletions

View File

@ -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) 中处理:
- 连接异常
- 连接超时
- 心跳超时
- 空闲超时

View File

@ -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
- 连接确认消息
- 踢下线消息
- 同步完成消息

View File

@ -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全局配置
- 协议版本
- 同步消息模式
- 网络状态
- 数据库配置

1
.gitignore vendored
View File

@ -13,5 +13,4 @@
.externalNativeBuild
.cxx
local.properties
/repository
.cursor

View File

@ -0,0 +1 @@
2a8fa0fa06d069610eed09005b343994

View File

@ -0,0 +1 @@
34a3a10b015c40e16a76fe75cb974d675a9cbda6

View File

@ -0,0 +1 @@
eb0a4ee07731a3ef38f606b2978d16169a6f6388e3f33fa3a10c82f3b0c17bb0

View File

@ -0,0 +1 @@
d1a0aa32976653fa04696f6d42edbe8152b59fdaf0135350cdefa14d571212571e85412c8f026a46e43069aadad3603358b63e262635a3716c948dab7b056750

View File

@ -0,0 +1 @@
195544eb25003c80879e6390c389f4b5

View File

@ -0,0 +1 @@
d7a3b759fd22ae6bb0cc7710e87c9f01b197afd8

View File

@ -0,0 +1 @@
2731b93ebd3aa8f457aa1dd2ac9634de1fb4641facabbc6683bc2b8575076c28

View File

@ -0,0 +1 @@
67d5a21983e722346be407c14a7d0dab5de051789cf7764dfaacd686e770e75a9f8377b49af1e5570e29d6a7ae38fedaec5af4c6b17f631116d665570b92fc5d

View File

@ -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"
}
]
}
]
}

View File

@ -0,0 +1 @@
18f875f433f4b46c3a8f82d2102b4efd

View File

@ -0,0 +1 @@
5e3e6e61712a9bee911ec2191da0faef05f93430

View File

@ -0,0 +1 @@
b5c54156b3a952972c75cfed1f5dd52419426a975df9a863efa8eb36f1feae12

View File

@ -0,0 +1 @@
0b3a1ba410c00e2566f0052b66121737164e4edc5defe64fd2aa7fd6e02ba05ccd9b2e0e13b603911d53a34ec9c10ee51fcd4c05719e19440c5da726484b45e3

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<groupId>com.xinbida.wukongim</groupId>
<artifactId>WKIMLib_local</artifactId>
<version>1.0.7</version>
<packaging>aar</packaging>
<name>WKIMLib</name>
<description>WuKong IM Android Library</description>
<licenses>
<license>
<name>The Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-bom</artifactId>
<version>1.9.22</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.9.22</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.9.22</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.support</groupId>
<artifactId>multidex</artifactId>
<version>1.0.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.zetetic</groupId>
<artifactId>android-database-sqlcipher</artifactId>
<version>4.5.4</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>androidx.sqlite</groupId>
<artifactId>sqlite-ktx</artifactId>
<version>2.5.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.whispersystems</groupId>
<artifactId>curve25519-android</artifactId>
<version>0.5.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.whispersystems</groupId>
<artifactId>signal-protocol-android</artifactId>
<version>2.8.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1 @@
ddc59ca3508344a630f0330f9c75801e

View File

@ -0,0 +1 @@
b351753e161fb1fde554f02adc966908b5d835b6

View File

@ -0,0 +1 @@
67367cbfa088cc2f2c57a66720e040865b70aa718b6f9487eeae174688b311f4

View File

@ -0,0 +1 @@
0ca8950dcf1398e81df06fb2c44015e10dac0d0ccc4d8db4e1e5eda302ee79c2727460d576f855c31bf8db5e669cdbb5d32897dce308009d069bc9e6d85126d9

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>com.xinbida.wukongim</groupId>
<artifactId>WKIMLib_local</artifactId>
<versioning>
<latest>1.0.7</latest>
<release>1.0.7</release>
<versions>
<version>1.0.7</version>
</versions>
<lastUpdated>20250524090841</lastUpdated>
</versioning>
</metadata>

View File

@ -0,0 +1 @@
2867162497a0539e3e3b0a1716f9feaa

View File

@ -0,0 +1 @@
e431ad1afdad3aff298476f1949e6dcb73bf52d2

View File

@ -0,0 +1 @@
aa7f10d602787f0faf2fc1a55c04fd089c55313b1d22c30cf1aa24c24ab9a216

View File

@ -0,0 +1 @@
3c7de45f238ee5d29b5c331b4c90ef396a87cc7a36550eeba7aa475e86f77da8d20e3373d177d46845e8e8d26c1ec106d2855ee1298c0763788a2583325b638f

View File

@ -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<WKSyncMsg> 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();
}
}
}

View File

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