diff --git a/Assets/Scripts/Bluetooth/BLECommunicationManager.cs b/Assets/Scripts/Bluetooth/BLECommunicationManager.cs
index 216b7d1..c28844d 100644
--- a/Assets/Scripts/Bluetooth/BLECommunicationManager.cs
+++ b/Assets/Scripts/Bluetooth/BLECommunicationManager.cs
@@ -2449,7 +2449,7 @@ namespace Kill.Bluetooth
if (!CheckConnection())
{
LogError($"[BLE-DEBUG] 蓝牙未就绪,无法发送指令: {description}");
- callback?.Invoke(new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR });
+ callback?.Invoke(new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR, Data = new byte[0] });
return;
}
@@ -2465,7 +2465,7 @@ namespace Kill.Bluetooth
if (!CheckConnection())
{
LogError("[BLE-DEBUG] CheckConnection failed, invoking callback with error");
- callback?.Invoke(new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR });
+ callback?.Invoke(new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR, Data = new byte[0] });
return;
}
@@ -2516,7 +2516,7 @@ namespace Kill.Bluetooth
{
LogError($"发送帧异常: {ex.Message}");
IsWaitingResponse = false;
- callback?.Invoke(new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR });
+ callback?.Invoke(new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR, Data = new byte[0] });
}
}
@@ -2963,7 +2963,9 @@ namespace Kill.Bluetooth
var timeoutResponse = new BLEResponse
{
- Status = BLEConstants.STATUS_TIMEOUT
+ Status = BLEConstants.STATUS_TIMEOUT,
+ // 超时无数据,给空数组避免回调里 BitConverter.ToString(response.Data) 抛 ArgumentNullException
+ Data = new byte[0]
};
_pendingCallback?.Invoke(timeoutResponse);
diff --git a/Assets/Scripts/Bluetooth/BluetoothManager.cs b/Assets/Scripts/Bluetooth/BluetoothManager.cs
index 14f973c..0438bca 100644
--- a/Assets/Scripts/Bluetooth/BluetoothManager.cs
+++ b/Assets/Scripts/Bluetooth/BluetoothManager.cs
@@ -202,6 +202,18 @@ namespace Kill.Bluetooth
return;
}
+#if UNITY_ANDROID && !UNITY_EDITOR
+ // 安卓:蓝牙已被系统关闭时直接静默跳过。
+ // 断开清理/自动重连等后台流程会反复触发初始化,若继续走到 CheckAndEnableBluetooth
+ // 会弹"蓝牙未开启"Toast 并广播 OnError,造成关蓝牙后一连串误报。
+ // 用户主动触发的初始化走 ForceInitialize,仍能得到提示。
+ if (!CheckIfBluetoothIsOn())
+ {
+ Log("蓝牙未开启,跳过蓝牙初始化");
+ return;
+ }
+#endif
+
Log("正在初始化蓝牙...");
// 先检查蓝牙是否开启,如果没有则尝试启用
@@ -218,7 +230,10 @@ namespace Kill.Bluetooth
public void ForceInitialize()
{
_userDeniedBluetooth = false;
- Initialize();
+ // 用户主动触发:不走 Initialize 的静默跳过守卫,
+ // 蓝牙未开启时由 CheckAndEnableBluetooth 给出提示
+ Log("正在初始化蓝牙...");
+ CheckAndEnableBluetooth(DoInitialize);
}
///
@@ -476,7 +491,8 @@ namespace Kill.Bluetooth
}
if (IsConnected)
{
- Disconnect();
+ // 蓝牙已被系统关闭,0x70 协议告别帧发不出去,跳过直接清理本地状态
+ Disconnect(sendProtocolCommand: false);
}
if (IsScanning)
{
@@ -595,6 +611,13 @@ namespace Kill.Bluetooth
}
else
{
+ // 蓝牙已被系统关闭时,插件所有操作(写入/扫描/断开)的失败都会进入这个全局错误回调,
+ // 此刻并非初始化失败,状态已由 MonitorBluetoothState 清理过,只记录不广播,避免误报
+ if (!CheckIfBluetoothIsOn())
+ {
+ Log($"蓝牙已关闭,忽略蓝牙错误: {error}");
+ return;
+ }
Log($"蓝牙初始化失败: {error}");
_enableBtCoroutine = null;
OnError?.Invoke($"初始化失败: {error}");
@@ -676,6 +699,14 @@ namespace Kill.Bluetooth
/// 扫描超时时间(秒),默认10秒
public void StartScan(float timeout = 999f)
{
+ // 蓝牙已被系统关闭时直接跳过:此时 BluetoothLeScanner 为 null,
+ // 插件扫描调用会抛 NullPointerException(曾导致主页 Init 中断、设备状态不刷新)
+ if (!CheckIfBluetoothIsOn())
+ {
+ Log("蓝牙未开启,跳过扫描");
+ return;
+ }
+
if (!IsInitialized)
{
Log("蓝牙未初始化,自动触发初始化流程...");
@@ -693,6 +724,12 @@ namespace Kill.Bluetooth
///
private void DoStartScan(float timeout)
{
+ if (!CheckIfBluetoothIsOn())
+ {
+ Log("蓝牙未开启,跳过扫描");
+ return;
+ }
+
if (IsScanning)
{
Log("已经在扫描中,先停止当前扫描...");
@@ -907,7 +944,7 @@ namespace Kill.Bluetooth
/// 断开当前连接(带协议通知)
/// 先发送0x70断开命令给设备,再断开BLE连接
///
- public void Disconnect(bool backHome=false)
+ public void Disconnect(bool backHome=false, bool sendProtocolCommand=true)
{
needBackHome=backHome;
if (!IsConnected)
@@ -933,21 +970,23 @@ namespace Kill.Bluetooth
}
// 先通过BLE协议发送断开命令(0x70)
- Log($"检查BLECommunicationManager: Instance={(BLECommunicationManager.Instance != null ? "存在" : "null")}");
- if (BLECommunicationManager.Instance != null)
+ if (sendProtocolCommand && BLECommunicationManager.Instance != null)
{
Log("发送断开命令(0x70)到设备...");
BLECommunicationManager.Instance.Disconnect((success) =>
{
- Log($"断开命令发送结果: {(success ? "成功" : "失败")}");
+ // 0x70 设备不会回复,success 仅表示已提交插件写入,不代表设备已收到
+ Log($"断开命令已提交: {(success ? "是" : "否")}");
// 无论命令是否成功,都执行BLE断开
PerformBLEDisconnect(addressToDisconnect);
});
}
else
{
- Log("BLECommunicationManager.Instance 为 null,跳过0x70命令,直接断开BLE");
- // 如果没有BLE通信管理器,直接断开BLE
+ Log(!sendProtocolCommand
+ ? "蓝牙不可用,跳过0x70命令,直接断开BLE"
+ : "BLECommunicationManager.Instance 为 null,跳过0x70命令,直接断开BLE");
+ // 如果没有BLE通信管理器或蓝牙不可用,直接断开BLE
PerformBLEDisconnect(addressToDisconnect);
}
}
@@ -1000,7 +1039,9 @@ namespace Kill.Bluetooth
// ChangeAimMac 切换设备时是 Disconnect + StartScan 同步调用,
// 本次清理会停掉刚启动的扫描,这里若仍有连接目标则自动重新扫描,避免连接流程中断
- if (!string.IsNullOrEmpty(aimMac) && !IsConnected)
+ // 蓝牙已被系统关闭时不重扫(扫描只会触发一轮失败的初始化),
+ // 等蓝牙重新打开后由 MonitorBluetoothState 的自动初始化流程接管
+ if (!string.IsNullOrEmpty(aimMac) && !IsConnected && CheckIfBluetoothIsOn())
{
StartScan(10);
}
diff --git a/Assets/Scripts/Bluetooth/OTAManager.cs b/Assets/Scripts/Bluetooth/OTAManager.cs
index dc0e0d6..ac0861d 100644
--- a/Assets/Scripts/Bluetooth/OTAManager.cs
+++ b/Assets/Scripts/Bluetooth/OTAManager.cs
@@ -199,7 +199,7 @@ namespace Kill.Bluetooth
private IEnumerator StartUpgradeCoroutineInternal(BLEFrame frame, Action callback)
{
- BLEResponse response = new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR };
+ BLEResponse response = new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR, Data = new byte[0] };
bool completed = false;
SendFrame(frame, (resp) =>
@@ -495,7 +495,7 @@ namespace Kill.Bluetooth
private IEnumerator SendFrameCoroutine(BLEFrame frame, Action callback)
{
bool completed = false;
- BLEResponse response = new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR };
+ BLEResponse response = new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR, Data = new byte[0] };
SendFrame(frame, (resp) =>
{
diff --git a/Assets/Scripts/Bluetooth/Protocol/BLEProtocolStructs.cs b/Assets/Scripts/Bluetooth/Protocol/BLEProtocolStructs.cs
index 622e137..7bbd248 100644
--- a/Assets/Scripts/Bluetooth/Protocol/BLEProtocolStructs.cs
+++ b/Assets/Scripts/Bluetooth/Protocol/BLEProtocolStructs.cs
@@ -494,6 +494,8 @@ namespace Kill.Bluetooth.Protocol
public static BLEResponse FromFrame(BLEFrame frame)
{
var response = new BLEResponse();
+ // 无载荷的响应(如写ACK)也保证 Data 非空,避免回调中 BitConverter.ToString(null) 崩溃
+ response.Data = new byte[0];
if (frame.Data != null && frame.Data.Length > 0)
{
response.Status = frame.Data[0];
diff --git a/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs b/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs
index 5fb591a..614860c 100644
--- a/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs
+++ b/Assets/Scripts/UI/Pages/HomePage/HomePageCtrl.cs
@@ -389,6 +389,7 @@ namespace Kill.UI.Pages
if(bluetoothDeviceInfoCoroutine!=null)
{
StopCoroutine(bluetoothDeviceInfoCoroutine);
+ bluetoothDeviceInfoCoroutine = null;
}
// 设备断开重新加载主页(保持原行为,当前在主页也会重新加载)
// 但切换设备(ChangeAimMac)导致的断开不重新加载,避免主页被销毁重建导致并发初始化
@@ -1004,6 +1005,24 @@ namespace Kill.UI.Pages
}
public async Task Init()
+ {
+ // Init 在 async 链中抛异常会被静默吞掉:LoadingUI 引用计数无法归零(表现为卡 loading),
+ // 且页面状态刷新(UpdateDeviceState)不会执行。这里统一兜底:记录完整异常并保证 Loading 收尾。
+ try
+ {
+ await InitInternal();
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"[HomePageCtrl] Init 异常: {ex}");
+ }
+ finally
+ {
+ LoadingUI.Hide();
+ }
+ }
+
+ private async Task InitInternal()
{
if (DataManager.Instance.userInfo.device_count == 0 || (OwnedDevices.Count == 0 && SharedDevices.Count == 0))