diff --git a/app/src/main/java/com/ismart/ib86/app/MainActivity.java b/app/src/main/java/com/ismart/ib86/app/MainActivity.java
index 55737b9..96053b5 100644
--- a/app/src/main/java/com/ismart/ib86/app/MainActivity.java
+++ b/app/src/main/java/com/ismart/ib86/app/MainActivity.java
@@ -2,6 +2,7 @@ package com.ismart.ib86.app;
import android.Manifest;
import android.content.pm.PackageManager;
+import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.view.Window;
@@ -10,6 +11,7 @@ import android.view.WindowInsetsController;
import android.os.Build;
import android.view.View;
import android.util.Log;
+import android.widget.RelativeLayout;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
@@ -24,10 +26,11 @@ import com.ismart.ib86.feature.serial.SerialPort.ASR5515DeviceManager;
import com.ismart.ib86.feature.serial.SerialPort.ASR5515Protocol;
import com.ismart.ib86.common.utils.LogManager;
import com.ismart.ib86.feature.gpio.GpioTest;
+import com.ismart.ib86.view.RobotEyesView;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
- private static final String DEVICE_PATH = "/dev/ttyS2";
+ private static final String DEVICE_PATH = "/dev/ttyS3";
private static final int BAUD_RATE = 921600;
// 权限请求码
@@ -40,15 +43,38 @@ public class MainActivity extends AppCompatActivity {
private static final int BT_POWER_EN = 75;
private ASR5515DeviceManager deviceManager;
- private Handler handler = new Handler();
- private GpioTest gpioTest;
+ // 使用弱引用避免内存泄漏
+ private Handler handler = new Handler(new Handler.Callback() {
+ @Override
+ public boolean handleMessage(@NonNull android.os.Message msg) {
+ return false;
+ }
+ });
+ private Runnable wearDetectionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // 发送佩戴检测请求
+ deviceManager.sendWearDetectionRequest();
+
+ // 1秒后再次执行
+ handler.postDelayed(this, 1000);
+ }
+};
private GpioManager gpioManager;
+ private RelativeLayout mainLayout;
+ private RobotEyesView robotEyesView;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+ mainLayout = findViewById(R.id.main_layout);
+ mainLayout.setBackgroundColor(Color.BLACK);
+ robotEyesView = new RobotEyesView(this);
+ mainLayout.addView(robotEyesView);
+
// 检查并请求必要的权限
checkAndRequestPermissions();
@@ -210,39 +236,50 @@ public class MainActivity extends AppCompatActivity {
// deviceManager.queryBtVersion();
// }
// }, 2000);
- deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.HR);
- deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.SPO2);
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- deviceManager.startGH3220Measure(ASR5515Protocol.GH3220MeasureType.SPO2);
- }
- }, 1000);
-
+// deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.HR);
+// deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.SPO2);
// handler.postDelayed(new Runnable() {
// @Override
// public void run() {
-// deviceManager.sendWearDetectionRequest();
+// deviceManager.startGH3220Measure(ASR5515Protocol.GH3220MeasureType.SPO2);
// }
-// }, 4000);
+// }, 1000);
+
+ handler.postDelayed(wearDetectionRunnable, 1000);
}
@Override
protected void onDestroy() {
super.onDestroy();
- handler.removeCallbacksAndMessages(null);
+ // 移除所有Handler消息和回调,避免内存泄漏
+ if (handler != null) {
+ handler.removeCallbacksAndMessages(null);
+ handler = null;
+ }
// 移至后台线程执行耗时操作,避免ANR
new Thread(() -> {
-// if (deviceManager != null) {
-// deviceManager.syncHostStatus(false);
-// deviceManager.close();
-// }
-
- // 释放GPIO测试资源
- if (gpioTest != null) {
- gpioTest.release();
- gpioTest = null;
+ try {
+ // 停止所有测量
+ if (deviceManager != null) {
+ deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.HR);
+ deviceManager.stopGH3220Measure(ASR5515Protocol.GH3220MeasureType.SPO2);
+ deviceManager.syncHostStatus(false);
+ deviceManager.close();
+ deviceManager = null;
+ }
+
+ // 关闭GPIO电源
+ if (gpioManager != null) {
+ try {
+ gpioManager.setValue(BT_POWER_EN, GpioManager.VALUE_LOW);
+ } catch (Exception e) {
+ LogManager.e(TAG, "关闭GPIO电源失败: " + e.getMessage());
+ }
+ gpioManager = null;
+ }
+ } catch (Exception e) {
+ LogManager.e(TAG, "资源释放过程中发生错误: " + e.getMessage());
}
}).start();
}
diff --git a/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515DeviceManager.java b/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515DeviceManager.java
index 76860d8..a7a7279 100644
--- a/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515DeviceManager.java
+++ b/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515DeviceManager.java
@@ -13,11 +13,47 @@ import com.ismart.ib86.feature.gpio.GpioManager;
*/
public class ASR5515DeviceManager {
private static final String TAG = "ASR5515DeviceManager";
+
+ // 设备状态标志
+ private volatile boolean isDeviceOpen = false;
+ private final Object sendLock = new Object(); // 发送数据的同步锁
+
+ /**
+ * 检查设备是否就绪
+ * @return true 如果设备已打开且就绪
+ */
+ private boolean isDeviceReady() {
+ return isDeviceOpen && serialPortHelper != null;
+ }
+
+ /**
+ * 安全发送数据的方法
+ * @param data 要发送的数据
+ * @return true 如果发送成功
+ */
+ protected boolean safeSendData(byte[] data) {
+ if (!isDeviceReady()) {
+ LogManager.e(TAG, "设备未就绪,无法发送数据");
+ return false;
+ }
+
+ synchronized (sendLock) {
+ try {
+ serialPortHelper.sendAsync(data);
+ return true;
+ } catch (Exception e) {
+ LogManager.e(TAG, "发送数据失败: " + e.getMessage());
+ return false;
+ }
+ }
+ }
private static final int GPIO_BT_WAKE = 33; // 蓝牙唤醒GPIO
private final SerialPortHelper serialPortHelper;
private final Handler mainHandler;
private final GpioManager gpioManager;
+ private final Object gpioLock = new Object(); // GPIO操作的同步锁
+ private volatile boolean isInitialized = false; // 初始化状态标志
// 细分回调实例
private DeviceCheckListener deviceCheckListener;
private DeviceControlListener deviceControlListener;
@@ -106,16 +142,36 @@ public class ASR5515DeviceManager {
* @param context 上下文
* @param devicePath 设备路径
* @param baudRate 波特率
+ * @throws IllegalStateException 如果串口打开失败
*/
public ASR5515DeviceManager(Context context, String devicePath, int baudRate) {
- serialPortHelper = new SerialPortHelper(devicePath, baudRate);
- mainHandler = new Handler(Looper.getMainLooper());
- gpioManager = GpioManager.getInstance();
- serialPortHelper.setSimulationMode(false);
- // 初始化串口
- boolean result = serialPortHelper.open();
- if (!result) {
- LogManager.e(TAG, "Failed to open serial port: " + devicePath);
+ synchronized (sendLock) {
+ try {
+ serialPortHelper = new SerialPortHelper(devicePath, baudRate);
+ mainHandler = new Handler(Looper.getMainLooper());
+ gpioManager = GpioManager.getInstance();
+ serialPortHelper.setSimulationMode(false);
+
+ // 初始化串口
+ boolean result = serialPortHelper.open();
+ if (!result) {
+ String errorMsg = "Failed to open serial port: " + devicePath;
+ LogManager.e(TAG, errorMsg);
+ updateDeviceState(false);
+ isInitialized = false;
+ // 记录错误但不抛出异常,允许应用继续运行
+ // 在实际使用时会检查isDeviceOpen状态
+ } else {
+ updateDeviceState(true);
+ isInitialized = true;
+ LogManager.i(TAG, "Serial port opened successfully: " + devicePath);
+ }
+ } catch (Exception e) {
+ LogManager.e(TAG, "Device initialization failed: " + e.getMessage());
+ updateDeviceState(false);
+ isInitialized = false;
+ throw new IllegalStateException("Failed to initialize device manager", e);
+ }
}
// 设置设备状态回调
@@ -346,28 +402,72 @@ public class ASR5515DeviceManager {
}
/**
- * 检查设备状态
+ * 检查设备是否已打开且可用
+ * @return true 如果设备已打开且可用
*/
- public void checkDevice() {
- wakeupDevice();
- serialPortHelper.checkDevice();
+ public boolean isDeviceAvailable() {
+ return isDeviceReady();
+ }
+
+ /**
+ * 检查设备状态
+ * @return true 如果命令发送成功
+ */
+ public boolean checkDevice() {
+ if (!isDeviceReady()) {
+ LogManager.e(TAG, "设备未就绪,无法检查设备状态");
+ return false;
+ }
+
+ try {
+ wakeupDevice();
+ serialPortHelper.checkDevice();
+ return true;
+ } catch (Exception e) {
+ LogManager.e(TAG, "检查设备状态失败: " + e.getMessage());
+ return false;
+ }
}
/**
* 重启设备
+ * @return true 如果命令发送成功
*/
- public void restartDevice() {
- wakeupDevice();
- serialPortHelper.restartDevice();
+ public boolean restartDevice() {
+ if (!isDeviceReady()) {
+ LogManager.e(TAG, "设备未就绪,无法重启设备");
+ return false;
+ }
+
+ try {
+ wakeupDevice();
+ serialPortHelper.restartDevice();
+ return true;
+ } catch (Exception e) {
+ LogManager.e(TAG, "重启设备失败: " + e.getMessage());
+ return false;
+ }
}
/**
* 控制日志输出
* @param enable 是否启用
+ * @return true 如果命令发送成功
*/
- public void setLogControl(boolean enable) {
- wakeupDevice();
- serialPortHelper.setLogControl(enable);
+ public boolean setLogControl(boolean enable) {
+ if (!isDeviceReady()) {
+ LogManager.e(TAG, "设备未就绪,无法控制日志输出");
+ return false;
+ }
+
+ try {
+ wakeupDevice();
+ serialPortHelper.setLogControl(enable);
+ return true;
+ } catch (Exception e) {
+ LogManager.e(TAG, "控制日志输出失败: " + e.getMessage());
+ return false;
+ }
}
/**
@@ -396,10 +496,31 @@ public class ASR5515DeviceManager {
}
/**
- * 关闭设备
+ * 设备状态变化的回调接口
*/
- public void close() {
- serialPortHelper.close();
+ public interface DeviceStateCallback {
+ void onDeviceStateChanged(boolean isOpen);
+ }
+
+ private DeviceStateCallback deviceStateCallback;
+
+ /**
+ * 设置设备状态变化回调
+ * @param callback 回调接口
+ */
+ public void setDeviceStateCallback(DeviceStateCallback callback) {
+ this.deviceStateCallback = callback;
+ }
+
+ /**
+ * 更新设备状态并通知回调
+ * @param newState 新的设备状态
+ */
+ private void updateDeviceState(boolean newState) {
+ isDeviceOpen = newState;
+ if (deviceStateCallback != null) {
+ mainHandler.post(() -> deviceStateCallback.onDeviceStateChanged(newState));
+ }
}
/**
@@ -473,24 +594,85 @@ public class ASR5515DeviceManager {
/**
* 唤醒设备
* 通过GPIO33唤醒蓝牙,ASR休眠时会自动发送'w'字符
+ * 使用同步锁确保GPIO操作的线程安全
*/
public void wakeupDevice() {
- try {
- // 配置GPIO33为输出模式
- gpioManager.exportGpio(GPIO_BT_WAKE);
- gpioManager.setDirection(GPIO_BT_WAKE, GpioManager.DIRECTION_OUT);
-
- // 拉低GPIO33
- gpioManager.setValue(GPIO_BT_WAKE, 0);
-
- // 等待30ms
- Thread.sleep(30);
-
- // 拉高GPIO33
- gpioManager.setValue(GPIO_BT_WAKE, 1);
-
- } catch (Exception e) {
- LogManager.e(TAG, "Failed to wakeup bluetooth: " + e.getMessage());
+ synchronized (gpioLock) {
+ try {
+ // 配置GPIO33为输出模式
+ gpioManager.exportGpio(GPIO_BT_WAKE);
+ gpioManager.setDirection(GPIO_BT_WAKE, GpioManager.DIRECTION_OUT);
+
+ // 拉低GPIO33
+ gpioManager.setValue(GPIO_BT_WAKE, 0);
+
+ // 等待30ms
+ Thread.sleep(30);
+
+ // 拉高GPIO33
+ gpioManager.setValue(GPIO_BT_WAKE, 1);
+
+ //LogManager.d(TAG, "设备唤醒成功");
+ } catch (InterruptedException e) {
+ //LogManager.e(TAG, "设备唤醒被中断: " + e.getMessage());
+ Thread.currentThread().interrupt(); // 重置中断状态
+ } catch (Exception e) {
+ //LogManager.e(TAG, "设备唤醒失败: " + e.getMessage());
+ // 尝试恢复GPIO状态
+ try {
+ gpioManager.setValue(GPIO_BT_WAKE, 1); // 确保GPIO处于高电平
+ } catch (Exception ex) {
+ //LogManager.e(TAG, "恢复GPIO状态失败: " + ex.getMessage());
+ }
+ }
}
}
+ /**
+ * 关闭设备,释放所有资源
+ */
+ public void close() {
+ synchronized (sendLock) {
+ try {
+ // 清除所有回调,避免内存泄漏
+ clearAllListeners();
+
+ // 关闭串口
+ if (serialPortHelper != null) {
+ serialPortHelper.close();
+ }
+
+ // 清除所有Handler消息和回调
+ if (mainHandler != null) {
+ mainHandler.removeCallbacksAndMessages(null);
+ }
+
+ // 更新设备状态
+ updateDeviceState(false);
+ isInitialized = false;
+ } catch (Exception e) {
+ LogManager.e(TAG, "Error closing device manager: " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * 清除所有监听器,避免内存泄漏
+ */
+ private void clearAllListeners() {
+ deviceCheckListener = null;
+ deviceControlListener = null;
+ logControlListener = null;
+ btVersionListener = null;
+ btUpgradeListener = null;
+ hostStatusListener = null;
+ bootBinCheckListener = null;
+ collectFreqSetListener = null;
+ hrBpBoAutoMeasureListener = null;
+ hrBpBoManualMeasureListener = null;
+ dynamicMeasureListener = null;
+ wearDetectionListener = null;
+ gh3220MeasureListener = null;
+ algoResultListener = null;
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/SerialPortHelper.java b/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/SerialPortHelper.java
index 408a00c..f2d9043 100644
--- a/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/SerialPortHelper.java
+++ b/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/SerialPortHelper.java
@@ -200,15 +200,6 @@ public class SerialPortHelper {
this.algoResultCallback = callback;
}
- /**
- * 发送佩戴检测查询请求
- */
- public void sendWearDetectionRequest() {
- byte[] data = ASR5515Protocol.createWearDetectionRequest();
- sendData(data, null);
- }
-
- // ASR-5515协议命令发送方法
public void checkDevice() {
byte[] data = ASR5515Protocol.createDeviceCheckRequest();
sendData(data, null);
@@ -306,6 +297,14 @@ public class SerialPortHelper {
sendData(data, null);
}
+ /**
+ * 发送佩戴检测查询请求
+ */
+ public void sendWearDetectionRequest() {
+ byte[] data = ASR5515Protocol.createWearDetectionRequest();
+ sendData(data, null);
+ }
+
public boolean open() {
try {
LogManager.d(TAG, "Opening serial port: " + mPortName + " with baud rate: " + mBaudRate);
diff --git a/app/src/main/java/com/ismart/ib86/view/RobotEyesView.java b/app/src/main/java/com/ismart/ib86/view/RobotEyesView.java
new file mode 100644
index 0000000..e03f239
--- /dev/null
+++ b/app/src/main/java/com/ismart/ib86/view/RobotEyesView.java
@@ -0,0 +1,78 @@
+package com.ismart.ib86.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+public class RobotEyesView extends View {
+
+ private Paint paint;
+ private float eyeWidth = 100f; // Increased size for eyes
+ private float eyeHeight = 100f; // Increased size for eyes
+ private float borderRadius = 25f; // Adjusted border radius for larger eyes
+ private float blinkDuration = 3000f; // 3 seconds in milliseconds
+
+ public RobotEyesView(Context context) {
+ super(context);
+ init();
+ }
+
+ public RobotEyesView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public RobotEyesView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ private void init() {
+ paint = new Paint();
+ paint.setColor(Color.rgb(26, 249, 248));
+ paint.setStyle(Paint.Style.FILL);
+
+ startBlinkAnimation();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ float centerXLeft = getWidth() / 4f;
+ float centerXRight = getWidth() * 3 / 4f;
+ float centerY = getHeight() / 3f; // Set eyes to one-third of screen height
+
+ drawEye(canvas, centerXLeft, centerY);
+ drawEye(canvas, centerXRight, centerY);
+ }
+
+ private void drawEye(Canvas canvas, float centerX, float centerY) {
+ canvas.drawRoundRect(centerX - eyeWidth / 2, centerY - eyeHeight / 2,
+ centerX + eyeWidth / 2, centerY + eyeHeight / 2,
+ borderRadius, borderRadius, paint);
+ }
+
+ private void startBlinkAnimation() {
+ final Animation animation = new Animation() {
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ if (interpolatedTime >= 0.2 && interpolatedTime < 0.25) {
+ eyeHeight = 20f; // Adjusted size for blinking effect
+ } else if (interpolatedTime >= 0.25 && interpolatedTime < 0.3) {
+ eyeHeight = 100f; // Reset to original size
+ }
+ invalidate();
+ }
+ };
+
+ animation.setDuration((long) blinkDuration);
+ animation.setRepeatCount(Animation.INFINITE);
+ startAnimation(animation);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_asr5515_demo.xml b/app/src/main/res/layout/activity_asr5515_demo.xml
deleted file mode 100644
index 5c7b45d..0000000
--- a/app/src/main/res/layout/activity_asr5515_demo.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index ea7185c..bb0316c 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,23 +1,12 @@
-
-
-
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_wear_detection.xml b/app/src/main/res/layout/activity_wear_detection.xml
deleted file mode 100644
index 459179c..0000000
--- a/app/src/main/res/layout/activity_wear_detection.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file