蓝牙连接不稳定修复

This commit is contained in:
fengjignqi
2026-06-10 11:34:47 +08:00
parent 121c69fdfc
commit 2a933d09a4
6 changed files with 711 additions and 30 deletions

View File

@@ -36,6 +36,10 @@ public class EspProvisionManager {
private final Context context;
private final Handler mainHandler;
private final com.espressif.provisioning.ESPProvisionManager provisionManager;
private static final long CONNECT_COOLDOWN = 1200L;
private static final long QUICK_DISCONNECT_WINDOW = 3000L;
private static final long RETRY_DELAY = 1200L;
private static final int MAX_CONNECT_RETRY = 2;
private ESPDevice espDevice;
private ProvisionCallback callback;
@@ -47,12 +51,28 @@ public class EspProvisionManager {
private boolean expectDisconnected; // 标记是否预期断开(配网成功后)
private String currentDeviceName;
private String currentDeviceType = ProvisionConfig.TRANSPORT_BLE;
private BluetoothDevice pendingBluetoothDevice;
private String pendingDeviceName;
private String pendingServiceUuid;
private String pendingProofOfPossession;
private String pendingUserName;
private int pendingSecurityType;
private int connectRetryCount;
private long lastDisconnectTime;
private long connectStartTime;
private final Runnable connectionTimeoutTask = new Runnable() {
@Override
public void run() {
if (connecting && !connected) {
connecting = false;
disconnectDeviceOnly();
lastDisconnectTime = System.currentTimeMillis();
if (retryConnectIfAllowed()) {
return;
}
resetState(true);
clearPendingConnect();
notifyFailed(ProvisionConfig.ERROR_CONNECT_TIMEOUT, "蓝牙连接超时");
disconnectDeviceOnly();
}
@@ -68,6 +88,13 @@ public class EspProvisionManager {
}
};
private final Runnable connectRetryTask = new Runnable() {
@Override
public void run() {
startPendingConnect(false);
}
};
private EspProvisionManager(Context context) {
this.context = context.getApplicationContext();
this.mainHandler = new Handler(Looper.getMainLooper());
@@ -150,32 +177,91 @@ public class EspProvisionManager {
return;
}
String primaryServiceUuid = TextUtils.isEmpty(serviceUuid) ? ProvisionConfig.SERVICE_UUID : serviceUuid;
String displayName = !TextUtils.isEmpty(deviceName) ? deviceName : bluetoothDevice.getName();
if (connecting && pendingBluetoothDevice != null
&& TextUtils.equals(pendingBluetoothDevice.getAddress(), bluetoothDevice.getAddress())) {
return;
}
pendingBluetoothDevice = bluetoothDevice;
pendingDeviceName = deviceName;
pendingServiceUuid = TextUtils.isEmpty(serviceUuid) ? ProvisionConfig.SERVICE_UUID : serviceUuid;
pendingProofOfPossession = proofOfPossession == null ? "" : proofOfPossession;
pendingUserName = userName == null ? "" : userName;
pendingSecurityType = securityType;
connectRetryCount = 0;
startPendingConnect(true);
}
private void startPendingConnect(boolean respectCooldown) {
if (pendingBluetoothDevice == null) {
notifyFailed(ProvisionConfig.ERROR_INVALID_DEVICE, "钃濈墮璁惧鏃犳晥");
return;
}
if (provisioning) {
return;
}
mainHandler.removeCallbacks(connectRetryTask);
long elapsedSinceDisconnect = System.currentTimeMillis() - lastDisconnectTime;
if (respectCooldown && lastDisconnectTime > 0 && elapsedSinceDisconnect < CONNECT_COOLDOWN) {
mainHandler.postDelayed(connectRetryTask, CONNECT_COOLDOWN - elapsedSinceDisconnect);
return;
}
String displayName = !TextUtils.isEmpty(pendingDeviceName)
? pendingDeviceName : pendingBluetoothDevice.getName();
currentDeviceName = displayName;
currentDeviceType = ProvisionConfig.TRANSPORT_BLE;
ignoreNextDisconnectEvent = espDevice != null;
boolean hadActiveDevice = espDevice != null && (connecting || connected || provisioning);
ignoreNextDisconnectEvent = hadActiveDevice;
disconnectDeviceOnly();
ensureEventBusRegistered();
espDevice = provisionManager.createESPDevice(
ESPConstants.TransportType.TRANSPORT_BLE,
parseSecurityType(securityType)
parseSecurityType(pendingSecurityType)
);
espDevice.setBluetoothDevice(bluetoothDevice);
espDevice.setPrimaryServiceUuid(primaryServiceUuid);
espDevice.setBluetoothDevice(pendingBluetoothDevice);
espDevice.setPrimaryServiceUuid(pendingServiceUuid);
espDevice.setDeviceName(displayName);
espDevice.setProofOfPossession(proofOfPossession == null ? "" : proofOfPossession);
espDevice.setUserName(userName == null ? "" : userName);
espDevice.setProofOfPossession(pendingProofOfPossession);
espDevice.setUserName(pendingUserName);
connecting = true;
connected = false;
provisioning = false;
connectStartTime = System.currentTimeMillis();
mainHandler.removeCallbacks(connectionTimeoutTask);
mainHandler.postDelayed(connectionTimeoutTask, ProvisionConfig.CONNECTION_TIMEOUT);
notifyConnecting();
espDevice.connectBLEDevice(bluetoothDevice, primaryServiceUuid);
espDevice.connectBLEDevice(pendingBluetoothDevice, pendingServiceUuid);
}
private boolean retryConnectIfAllowed() {
if (pendingBluetoothDevice == null || provisioning || connected) {
return false;
}
if (connectRetryCount >= MAX_CONNECT_RETRY) {
return false;
}
connectRetryCount++;
resetState(true);
mainHandler.removeCallbacks(connectRetryTask);
mainHandler.postDelayed(connectRetryTask, RETRY_DELAY * connectRetryCount);
return true;
}
private void clearPendingConnect() {
pendingBluetoothDevice = null;
pendingDeviceName = null;
pendingServiceUuid = null;
pendingProofOfPossession = null;
pendingUserName = null;
pendingSecurityType = 0;
connectRetryCount = 0;
connectStartTime = 0;
}
@@ -241,8 +327,11 @@ public class EspProvisionManager {
public void disconnect() {
boolean shouldNotify = connected || connecting || provisioning;
mainHandler.removeCallbacks(connectRetryTask);
disconnectDeviceOnly();
resetState();
lastDisconnectTime = System.currentTimeMillis();
resetState(true);
clearPendingConnect();
if (shouldNotify) {
notifyDisconnected();
}
@@ -258,8 +347,10 @@ public class EspProvisionManager {
switch (event.getEventType()) {
case ESPConstants.EVENT_DEVICE_CONNECTED:
mainHandler.removeCallbacks(connectionTimeoutTask);
mainHandler.removeCallbacks(connectRetryTask);
connecting = false;
connected = true;
connectRetryCount = 0;
notifyConnected();
break;
@@ -270,7 +361,13 @@ public class EspProvisionManager {
connecting = false;
connected = false;
provisioning = false;
lastDisconnectTime = System.currentTimeMillis();
if (wasConnecting && retryConnectIfAllowed()) {
break;
}
if (wasConnecting) {
resetState(true);
clearPendingConnect();
notifyFailed(ProvisionConfig.ERROR_CONNECT_FAILED, "蓝牙连接失败");
} else {
notifyFailed(ProvisionConfig.ERROR_PROVISION_FAILED, "设备通信失败");
@@ -285,14 +382,27 @@ public class EspProvisionManager {
mainHandler.removeCallbacks(connectionTimeoutTask);
mainHandler.removeCallbacks(provisionTimeoutTask);
boolean shouldNotify = connected || connecting || provisioning;
resetState();
boolean expectedDisconnect = expectDisconnected;
boolean wasConnecting2 = connecting;
long connectDuration = System.currentTimeMillis() - connectStartTime;
lastDisconnectTime = System.currentTimeMillis();
resetState(true);
if (wasConnecting2 && !expectedDisconnect
&& connectDuration < QUICK_DISCONNECT_WINDOW
&& retryConnectIfAllowed()) {
break;
}
if (wasConnecting2 && !expectedDisconnect) {
clearPendingConnect();
notifyFailed(ProvisionConfig.ERROR_CONNECT_FAILED, "蓝牙连接失败");
break;
}
if (shouldNotify) {
// 如果是配网成功后的预期断开,不发送断开回调
// 因为 onSuccess 已经通知过了
if (!expectDisconnected) {
if (!expectedDisconnect) {
notifyDisconnected();
}
expectDisconnected = false; // 重置标志
}
break;
@@ -321,14 +431,17 @@ public class EspProvisionManager {
}
}
private void resetState() {
private void resetState(boolean clearDevice) {
mainHandler.removeCallbacks(connectionTimeoutTask);
mainHandler.removeCallbacks(provisionTimeoutTask);
connecting = false;
connected = false;
provisioning = false;
ignoreNextDisconnectEvent = false;
// expectDisconnected 保持不变,用于断开回调判断
expectDisconnected = false;
if (clearDevice) {
espDevice = null;
}
}
private ESPConstants.SecurityType parseSecurityType(int securityType) {