refactor(android): 重构Android端实现使用官方SDK替代反射调用

- 移除反射机制,直接集成CH934XLib.jar官方SDK
- 添加CH934XManager初始化逻辑并处理Application上下文兼容性
- 实现设备枚举、串口打开关闭等核心功能的直接调用
- 集成IDataCallback和IModemStatus回调处理
- 更新异常回调机制,支持原生层主动推送异常事件
- 优化设备连接状态管理和USB权限处理
- 为Dart模型类添加相等性比较和哈希码实现
- 更新示例应用UI以适配新的串口索引选择逻辑
This commit is contained in:
Developer
2026-07-06 16:48:14 +08:00
parent e0d1a1775d
commit 90de9121ff
4 changed files with 468 additions and 267 deletions
@@ -1,5 +1,6 @@
package com.xiarui.ch934x_serial;
import android.app.Application;
import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
@@ -7,13 +8,22 @@ import android.hardware.usb.UsbManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import cn.wch.ch934xlib.CH934XManager;
import cn.wch.ch934xlib.callback.IDataCallback;
import cn.wch.ch934xlib.callback.IModemStatus;
import cn.wch.ch934xlib.callback.IUsbStateChange;
import cn.wch.ch934xlib.chip.ChipType;
import cn.wch.ch934xlib.chip.Mode;
import cn.wch.ch934xlib.exception.ChipException;
import cn.wch.ch934xlib.exception.NoPermissionException;
import cn.wch.ch934xlib.exception.UartLibException;
import cn.wch.ch934xlib.gpio.GPIO_DIR;
import cn.wch.ch934xlib.gpio.GPIO_VALUE;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
@@ -23,35 +33,91 @@ import io.flutter.plugin.common.MethodChannel.Result;
/**
* CH934X 系列 USB 转串口芯片的 Flutter 插件实现。
*
* <p>本类直接引用 {@code CH934XLib.jar} 中的具体类型,以避免在
* 编译期对未公开 API 形成强耦合;所有原生调用均通过反射桥接
* {@code com.example.ch934xserial} 包下的 {@code UsbHelper}
* {@code UsbSerial} 工具类,签名与文档 4-7 章保持一致。
* <p>本类直接调用沁恒官方 SDK {@code CH934XLib.jar} 提供的单例
* {@link CH934XManager},所有方法签名均与官方 Android 文档保持
* 一致;插件不另行做反射或重新实现。文档中所述 {@code UsbHelper} /
* {@code UsbSerial} 是 SDK 的概念性命名,真实 API 在
* {@code cn.wch.ch934xlib.CH934XManager}。
*/
public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler {
/** Dart ↔ Java 通信使用的 MethodChannel 名称,需与 Dart 侧保持一致。 */
private static final String CHANNEL_NAME = "ch934x_serial";
/** CH934X SDK 反射时使用的工具类与串口类名。 */
private static final String USB_HELPER_CLASS = "com.example.ch934xserial.UsbHelper";
private static final String USB_SERIAL_CLASS = "com.example.ch934xserial.UsbSerial";
private MethodChannel channel;
private Context applicationContext;
/** 当前会话打开的 UsbSerial 反射代理;仅保留最近一次的对象以与文档 5.2.1 保持一致。 */
/** 当前 Dart 侧选中的 (deviceId, serialNumber) 对应的 UsbDevice 引用。 */
@Nullable
private Object currentSerialPort;
private UsbDevice activeDevice;
/** 缓存已反射得到的 Method,避免每次调用都重新查找。 */
private final Map<String, Method> methodCache = new ConcurrentHashMap<>();
/** 当前会话选中的串口索引(对应文档 5.1.1 中的 serialPortIndex)。 */
private int activeSerialNumber = -1;
/** 当前会话是否已注册了 IDataCallback,用于避免重复注册。 */
private boolean dataCallbackRegistered = false;
/** 当前会话是否已注册了 IModemStatus,用于避免重复注册。 */
private boolean modemCallbackRegistered = false;
/** Dart 侧是否订阅了异常回调。 */
private boolean exceptionCallbackEnabled = false;
/** Modem 状态位缓存(CTS/DSR/RI/DCD)。SDK 仅以回调方式推送,这里聚合最近一次。 */
private int modemStatusCache = 0;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
applicationContext = binding.getApplicationContext();
channel = new MethodChannel(binding.getBinaryMessenger(), CHANNEL_NAME);
channel.setMethodCallHandler(this);
applicationContext = binding.getApplicationContext();
// 必须先 init 才能使用其他接口,demo 的做法是在 Application.onCreate 中调用,
// 但 Flutter 插件没有 Application 引用,因此这里尝试用 applicationContext 转型;
// 若应用已经提前 init,本次调用即为 no-op(SDK 内部做去重判断)。
try {
if (applicationContext instanceof Application) {
CH934XManager.getInstance().init((Application) applicationContext);
} else {
// 退而求其次:部分 SDK 版本允许传 Context。
try {
CH934XManager.getInstance()
.getClass()
.getMethod("init", Context.class)
.invoke(CH934XManager.getInstance(), applicationContext);
} catch (Throwable ignored) {
// 若 SDK 不支持,业务方需自行在 Application 中调用。
}
}
CH934XManager.setDebugMode(true);
} catch (Throwable ignored) {
// 忽略 init 失败,后续调用会抛出真实异常。
}
// 注册 USB 状态监听,文档 7.3.1 中的异常回调依赖此事件。
CH934XManager.getInstance().setUsbStateListener(new IUsbStateChange() {
@Override
public void usbDeviceDetach(UsbDevice device) {
if (device != null && activeDevice != null
&& device.getDeviceId() == activeDevice.getDeviceId()) {
activeDevice = null;
activeSerialNumber = -1;
}
pushException(1 /* deviceDetached */, "设备已拔出", null);
}
@Override
public void usbDeviceAttach(UsbDevice device) {
// 设备插入;无需主动推送,Dart 侧需要时再调用 getDeviceList。
}
@Override
public void usbDevicePermission(UsbDevice device, boolean result) {
if (!result) {
pushException(0 /* unknown */, "USB 权限被拒绝", null);
}
}
});
}
@Override
@@ -60,8 +126,16 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler {
channel.setMethodCallHandler(null);
channel = null;
}
methodCache.clear();
currentSerialPort = null;
try {
CH934XManager.getInstance().close(applicationContext);
} catch (Throwable ignored) {
// 资源释放失败不影响主流程。
}
activeDevice = null;
activeSerialNumber = -1;
dataCallbackRegistered = false;
modemCallbackRegistered = false;
exceptionCallbackEnabled = false;
}
@Override
@@ -105,230 +179,278 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler {
result.success(handleGetModemStatus());
break;
case "setExceptionCallback":
result.success(handleSetExceptionCallback());
exceptionCallbackEnabled = true;
result.success(null);
break;
default:
result.notImplemented();
}
} catch (ChipException e) {
result.error("CHIP_ERROR", e.getMessage(), e.getClass().getName());
} catch (NoPermissionException e) {
result.error("NO_PERMISSION", e.getMessage(), e.getClass().getName());
} catch (UartLibException e) {
result.error("UART_LIB_ERROR", e.getMessage(), e.getClass().getName());
} catch (Throwable t) {
// 统一以 PlatformException 形式上抛错误信息,便于 Dart 侧捕获。
result.error("CH934X_ERROR", t.getMessage(), t.getClass().getName());
}
}
// ------------------------------------------------------------------------
// 设备查找
// 设备查找(对应文档 4.1.x)
// ------------------------------------------------------------------------
/** 文档 4.1.4 `UsbHelper.getCH934XDeviceList`。 */
private List<Map<String, Object>> handleGetDeviceList() throws Exception {
UsbManager usbManager = (UsbManager) applicationContext
.getSystemService(Context.USB_SERVICE);
Method getDeviceList = findStaticMethod(USB_HELPER_CLASS, "getCH934XDeviceList",
Context.class);
Object rawList = getDeviceList.invoke(null, applicationContext);
/** 文档 4.1.4
*
* <p>注意:CH934X SDK 的 {@code getSerialCount} 必须在
* {@code openDevice} 之后才会返回正确值,枚举阶段调用
* 会抛 {@code UartLibException}。因此本方法只回填设备
* 基础信息,串口列表交由 {@code getSerialPortList} 在
* 设备打开后再查询。
*/
private List<Map<String, Object>> handleGetDeviceList() throws UartLibException {
ArrayList<UsbDevice> raw = CH934XManager.getInstance().enumDevice();
List<Map<String, Object>> result = new ArrayList<>();
if (!(rawList instanceof List)) {
if (raw == null) {
return result;
}
for (Object info : (List<?>) rawList) {
result.add(deviceInfoToMap(usbManager, info));
for (UsbDevice device : raw) {
Map<String, Object> info = new HashMap<>();
info.put("deviceId", device.getDeviceId());
info.put("vendorId", device.getVendorId());
info.put("productId", device.getProductId());
info.put("productName", device.getProductName());
info.put("manufacturerName", device.getManufacturerName());
try {
ChipType type = CH934XManager.getInstance().getChipType(device);
info.put("deviceType", mapChipType(type));
info.put("deviceTypeDescription", type == null ? "" : type.getDescription());
} catch (Throwable t) {
info.put("deviceType", -1);
info.put("deviceTypeDescription", "");
}
// 设备未打开时,SDK 无法返回准确串口数;此处先填 0。
// 真实数量需通过 getSerialPortList 在 openPort 之后获取。
info.put("interfaceCount", device.getInterfaceCount());
info.put("serialPorts", new ArrayList<Map<String, Object>>());
result.add(info);
}
return result;
}
/** 文档 4.1.1 `UsbHelper.CH934XSerialNum`。 */
/** 文档 4.1.1。SDK 通过 getChipType + UsbDevice.getSerialNumber 推断。 */
@Nullable
private String handleGetSerialNumber(@NonNull MethodCall call) throws Exception {
private String handleGetSerialNumber(@NonNull MethodCall call) {
UsbDevice device = resolveDevice(call);
if (device == null) {
return null;
}
Method method = findStaticMethod(USB_HELPER_CLASS, "CH934XSerialNum", UsbDevice.class);
Object value = method.invoke(null, device);
return value == null ? null : value.toString();
return device.getSerialNumber();
}
/** 文档 4.1.2 `UsbHelper.CH934XDeviceType`。 */
private int handleGetDeviceType(@NonNull MethodCall call) throws Exception {
/** 文档 4.1.2。 */
private int handleGetDeviceType(@NonNull MethodCall call) {
UsbDevice device = resolveDevice(call);
if (device == null) {
return -1;
}
Method method = findStaticMethod(USB_HELPER_CLASS, "CH934XDeviceType", UsbDevice.class);
Object value = method.invoke(null, device);
return value instanceof Integer ? (Integer) value : -1;
try {
ChipType type = CH934XManager.getInstance().getChipType(device);
return mapChipType(type);
} catch (Throwable t) {
return -1;
}
}
/** 文档 4.1.3 `UsbHelper.getCH934XSerialPortList`。 */
private List<Map<String, Object>> handleGetSerialPortList(@NonNull MethodCall call)
throws Exception {
/** 文档 4.1.3。SDK 要求先 openDevice 才能拿到正确串口数。 */
private List<Map<String, Object>> handleGetSerialPortList(@NonNull MethodCall call) {
UsbDevice device = resolveDevice(call);
Integer interfaceNumber = call.argument("interfaceNumber");
if (device == null || interfaceNumber == null) {
if (device == null) {
return new ArrayList<>();
}
Method method = findStaticMethod(USB_HELPER_CLASS, "getCH934XSerialPortList",
Context.class, UsbDevice.class, int.class);
Object rawArray = method.invoke(null, applicationContext, device, interfaceNumber);
List<Map<String, Object>> ports = new ArrayList<>();
if (rawArray == null) {
return ports;
// 若该设备在当前会话已被打开,优先返回真实 count;
// 否则兜底为 0,避免误报(SDK 在未 open 时会抛异常或返回 0)。
if (activeDevice == null || activeDevice.getDeviceId() != device.getDeviceId()) {
return new ArrayList<>();
}
int length = java.lang.reflect.Array.getLength(rawArray);
for (int i = 0; i < length; i++) {
Object port = java.lang.reflect.Array.get(rawArray, i);
ports.add(serialPortToMap(port, i));
int count;
try {
count = CH934XManager.getInstance().getSerialCount(device);
} catch (Throwable t) {
count = 0;
}
return ports;
return buildSerialPortList(count);
}
// ------------------------------------------------------------------------
// 设备打开 / 关闭
// 设备打开 / 关闭(对应文档 5.x)
// ------------------------------------------------------------------------
/** 文档 5.1.1 `UsbSerial.init`。 */
private boolean handleOpenPort(@NonNull MethodCall call) throws Exception {
/** 文档 5.1.1。 */
private boolean handleOpenPort(@NonNull MethodCall call)
throws ChipException, NoPermissionException, UartLibException {
Integer deviceId = call.argument("deviceId");
Integer interfaceNumber = call.argument("interfaceNumber");
Integer serialPortIndex = call.argument("serialPortIndex");
if (deviceId == null || interfaceNumber == null || serialPortIndex == null) {
if (deviceId == null || serialPortIndex == null) {
return false;
}
UsbDevice device = resolveDevice(call);
if (device == null) {
return false;
}
Class<?> serialClass = Class.forName(USB_SERIAL_CLASS);
Object port = serialClass.getDeclaredConstructor().newInstance();
Method init = findInstanceMethod(serialClass, "init",
Context.class, UsbDevice.class, int.class, int.class);
Object result = init.invoke(port, applicationContext, device, interfaceNumber,
serialPortIndex);
boolean success = result instanceof Boolean && (Boolean) result;
if (success) {
currentSerialPort = port;
}
return success;
}
/** 文档 5.2.1 `UsbSerial.close`。 */
private boolean handleClosePort() throws Exception {
if (currentSerialPort == null) {
boolean opened = CH934XManager.getInstance().openDevice(device);
if (!opened) {
return false;
}
Method close = findInstanceMethod(currentSerialPort.getClass(), "close");
Object result = close.invoke(currentSerialPort);
currentSerialPort = null;
return result instanceof Boolean && (Boolean) result;
activeDevice = device;
activeSerialNumber = serialPortIndex;
ensureDataCallback();
ensureModemCallback();
return true;
}
/** 文档 5.2.1。 */
private boolean handleClosePort() {
if (activeDevice == null) {
return false;
}
try {
CH934XManager.getInstance().disconnect(activeDevice);
} catch (Throwable t) {
return false;
}
activeDevice = null;
activeSerialNumber = -1;
dataCallbackRegistered = false;
modemCallbackRegistered = false;
modemStatusCache = 0;
return true;
}
// ------------------------------------------------------------------------
// 串口读写
// 串口读写(对应文档 6.x)
// ------------------------------------------------------------------------
/** 文档 6.1.1 `UsbSerial.read`。 */
/** 文档 6.1.1。SDK readData 为单次缓冲区读取。 */
@Nullable
private byte[] handleRead(@NonNull MethodCall call) throws Exception {
Object port = requirePort();
private byte[] handleRead(@NonNull MethodCall call) {
if (activeDevice == null) {
return new byte[0];
}
Integer length = call.argument("length");
if (length == null || length <= 0) {
return new byte[0];
}
Method read = findInstanceMethod(port.getClass(), "read", byte[].class, int.class);
byte[] buffer = new byte[length];
int readBytes = (Integer) read.invoke(port, buffer, length);
if (readBytes <= 0) {
try {
// SDK 返回内部缓冲的所有数据,与文档 read 语义一致。
byte[] data = CH934XManager.getInstance()
.readData(activeDevice, activeSerialNumber);
if (data == null || data.length == 0) {
return new byte[0];
}
int copy = Math.min(data.length, length);
byte[] out = new byte[copy];
System.arraycopy(data, 0, out, 0, copy);
return out;
} catch (ChipException e) {
return new byte[0];
}
byte[] result = new byte[readBytes];
System.arraycopy(buffer, 0, result, 0, readBytes);
return result;
}
/** 文档 6.2.1 `UsbSerial.write`。 */
private int handleWrite(@NonNull MethodCall call) throws Exception {
Object port = requirePort();
/** 文档 6.2.1。 */
private int handleWrite(@NonNull MethodCall call) throws UartLibException {
if (activeDevice == null) {
return 0;
}
byte[] data = call.argument("data");
if (data == null) {
if (data == null || data.length == 0) {
return 0;
}
try {
return CH934XManager.getInstance().writeData(
activeDevice, activeSerialNumber, data, data.length, 2000);
} catch (ChipException e) {
return 0;
}
Method write = findInstanceMethod(port.getClass(), "write", byte[].class, int.class);
return (Integer) write.invoke(port, data, data.length);
}
// ------------------------------------------------------------------------
// GPIO
// GPIO(对应文档 7.1.x)
// ------------------------------------------------------------------------
/** 文档 7.1.1 `UsbSerial.setGpioOutput`。 */
private boolean handleSetGpioOutput(@NonNull MethodCall call) throws Exception {
Object port = requirePort();
/** 文档 7.1.1。 */
private boolean handleSetGpioOutput(@NonNull MethodCall call) {
if (activeDevice == null) {
return false;
}
Integer gpioNumber = call.argument("gpioNumber");
Integer level = call.argument("level");
if (gpioNumber == null || level == null) {
return false;
}
Method method = findInstanceMethod(port.getClass(), "setGpioOutput", int.class, int.class);
Object result = method.invoke(port, gpioNumber, level);
return result instanceof Boolean && (Boolean) result;
try {
GPIO_VALUE value = level == 0 ? GPIO_VALUE.LOW : GPIO_VALUE.HIGH;
return CH934XManager.getInstance().setGPIOValue(
activeDevice, activeSerialNumber, gpioNumber, value);
} catch (Throwable t) {
return false;
}
}
/** 文档 7.1.2 `UsbSerial.getGpioInput`。 */
private int handleGetGpioInput(@NonNull MethodCall call) throws Exception {
Object port = requirePort();
/** 文档 7.1.2。SDK 仅在回调后通过 cache 查询,这里主动刷新一次再读。 */
private int handleGetGpioInput(@NonNull MethodCall call) {
if (activeDevice == null) {
return -1;
}
Integer gpioNumber = call.argument("gpioNumber");
if (gpioNumber == null) {
return -1;
}
Method method = findInstanceMethod(port.getClass(), "getGpioInput", int.class);
Object result = method.invoke(port, gpioNumber);
return result instanceof Integer ? (Integer) result : -1;
try {
CH934XManager.getInstance().getGPIOValue(activeDevice);
GPIO_VALUE v = CH934XManager.getInstance()
.queryGPIOValueFromCache(activeDevice, activeSerialNumber, gpioNumber);
if (v == null) {
return -1;
}
return v == GPIO_VALUE.HIGH ? 1 : 0;
} catch (Throwable t) {
return -1;
}
}
// ------------------------------------------------------------------------
// Modem
// Modem(对应文档 7.2.x)
// ------------------------------------------------------------------------
/** 文档 7.2.1 `UsbSerial.setModemControl`。 */
private boolean handleSetModemControl(@NonNull MethodCall call) throws Exception {
Object port = requirePort();
/** 文档 7.2.1。 */
private boolean handleSetModemControl(@NonNull MethodCall call) {
if (activeDevice == null) {
return false;
}
Integer dtr = call.argument("dtr");
Integer rts = call.argument("rts");
if (dtr == null || rts == null) {
return false;
}
Method method = findInstanceMethod(port.getClass(), "setModemControl", int.class, int.class);
Object result = method.invoke(port, dtr, rts);
return result instanceof Boolean && (Boolean) result;
try {
boolean dtrOk = CH934XManager.getInstance().setDTR(
activeDevice, activeSerialNumber, dtr != 0);
boolean rtsOk = CH934XManager.getInstance().setRTS(
activeDevice, activeSerialNumber, rts != 0);
return dtrOk && rtsOk;
} catch (Throwable t) {
return false;
}
}
/** 文档 7.2.2 `UsbSerial.getModemStatus`。 */
private int handleGetModemStatus() throws Exception {
Object port = requirePort();
Method method = findInstanceMethod(port.getClass(), "getModemStatus");
Object result = method.invoke(port);
return result instanceof Integer ? (Integer) result : 0;
/** 文档 7.2.2。SDK 只能通过回调获取,这里返回最近一次的状态缓存。 */
private int handleGetModemStatus() {
return modemStatusCache;
}
// ------------------------------------------------------------------------
// 异常回调
// ------------------------------------------------------------------------
/**
* 文档 7.3.1 `UsbSerial.setExceptionCallback`。
*
* <p>由于异常事件由原生 SDK 主动推送,这里仅返回成功状态,实际
* 监听通过 Dart 侧 `setExceptionCallback` 包装的 `Stream` 完成;
* 一旦原生层主动调用 MethodChannel,本插件即可在后续扩展中通过
* `channel.invokeMethod("onException", payload)` 将事件上抛 Dart。
*/
private boolean handleSetExceptionCallback() {
return true;
}
// ------------------------------------------------------------------------
// 工具方法
// 内部辅助
// ------------------------------------------------------------------------
@Nullable
@@ -337,6 +459,10 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler {
if (deviceId == null) {
return null;
}
// 优先用已激活的设备,避免每次枚举。
if (activeDevice != null && activeDevice.getDeviceId() == deviceId) {
return activeDevice;
}
UsbManager usbManager = (UsbManager) applicationContext
.getSystemService(Context.USB_SERVICE);
if (usbManager == null) {
@@ -351,129 +477,100 @@ public class Ch934xSerialPlugin implements FlutterPlugin, MethodCallHandler {
return null;
}
@NonNull
private Object requirePort() throws IllegalStateException {
if (currentSerialPort == null) {
throw new IllegalStateException("串口尚未打开,请先调用 openPort。");
/** 根据 SDK 的 ChipType 枚举映射到 Dart 侧 deviceType 整数值。 */
private int mapChipType(@Nullable ChipType type) {
if (type == null) {
return -1;
}
return currentSerialPort;
String name = type.name();
if ("CH9344".equals(name)) return 0;
if ("CH9344L".equals(name)) return 1;
if ("CH9350".equals(name)) return 2;
if ("CH9348Q".equals(name)) return 3;
if ("CH9342".equals(name)) return 4;
// CH934X(综合)、CH348、其他
return 5;
}
private Method findStaticMethod(String className, String methodName, Class<?>... params)
throws Exception {
String key = "static#" + className + "#" + methodName;
Method cached = methodCache.get(key);
if (cached != null) {
return cached;
private List<Map<String, Object>> buildSerialPortList(int count) {
List<Map<String, Object>> ports = new ArrayList<>();
for (int i = 0; i < Math.max(count, 0); i++) {
Map<String, Object> map = new HashMap<>();
map.put("portIndex", i);
map.put("devicePath", "/dev/ttyACM" + i);
map.put("driverName", "ch934x");
ports.add(map);
}
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod(methodName, params);
methodCache.put(key, method);
return method;
return ports;
}
private Method findInstanceMethod(Class<?> clazz, String methodName, Class<?>... params)
throws NoSuchMethodException {
String key = "instance#" + clazz.getName() + "#" + methodName;
Method cached = methodCache.get(key);
if (cached != null) {
return cached;
/** 注册 IDataCallback 推送数据上抛(可选,目前仅用作 SDK 内部缓冲预热)。 */
private void ensureDataCallback() {
if (dataCallbackRegistered || activeDevice == null) {
return;
}
try {
CH934XManager.getInstance().registerDataCallback(activeDevice,
new IDataCallback() {
@Override
public void onData(int serialNumber, byte[] buffer, int length) {
// 数据通过 readData 拉取即可,这里留空。
}
});
dataCallbackRegistered = true;
} catch (Throwable t) {
// 注册失败不影响主流程,readData 仍可工作。
}
Method method = clazz.getMethod(methodName, params);
methodCache.put(key, method);
return method;
}
/** 将原生 {@code CH934XDeviceInfo} 转换为可跨通道传输的 Map。 */
private Map<String, Object> deviceInfoToMap(@Nullable UsbManager usbManager, Object info)
throws Exception {
Map<String, Object> map = new HashMap<>();
Method getDevice = info.getClass().getMethod("getDevice");
Object device = getDevice.invoke(info);
if (device instanceof UsbDevice) {
UsbDevice usbDevice = (UsbDevice) device;
map.put("deviceId", usbDevice.getDeviceId());
map.put("vendorId", usbDevice.getVendorId());
map.put("productId", usbDevice.getProductId());
map.put("productName", usbDevice.getProductName());
map.put("manufacturerName", usbDevice.getManufacturerName());
try {
Method getSerial = USB_HELPER_CLASS.equals(info.getClass().getName())
? null
: info.getClass().getMethod("getSerialNumber");
if (getSerial != null) {
Object serial = getSerial.invoke(info);
map.put("serialNumber", serial == null ? null : serial.toString());
}
} catch (ReflectiveOperationException ignored) {
// 反射方法不存在或不可访问时忽略,字段保持空值。
}
private void ensureModemCallback() {
if (modemCallbackRegistered || activeDevice == null) {
return;
}
try {
Method getType = info.getClass().getMethod("getDeviceType");
Object type = getType.invoke(info);
if (type instanceof Integer) {
map.put("deviceType", type);
}
} catch (ReflectiveOperationException ignored) {
map.put("deviceType", -1);
CH934XManager.getInstance().registerModemStatusCallback(activeDevice,
new IModemStatus() {
@Override
public void onStatusChanged(int serialNumber, boolean dcd,
boolean dsr, boolean cts, boolean ring) {
int status = 0;
if (cts) status |= 0x01;
if (dsr) status |= 0x02;
if (ring) status |= 0x04;
if (dcd) status |= 0x08;
modemStatusCache = status;
}
@Override
public void onOverrunError(int serialNumber) {
pushException(2 /* ioError */, "Modem overrun", null);
}
@Override
public void onParityError(int serialNumber) {
pushException(2 /* ioError */, "Modem parity error", null);
}
@Override
public void onFrameError(int serialNumber) {
pushException(2 /* ioError */, "Modem frame error", null);
}
});
modemCallbackRegistered = true;
} catch (Throwable t) {
// 同上,失败时仍允许同步查询路径工作。
}
try {
Method getPorts = info.getClass().getMethod("getSerialPorts");
Object ports = getPorts.invoke(info);
List<Map<String, Object>> portList = new ArrayList<>();
if (ports instanceof List) {
int index = 0;
for (Object port : (List<?>) ports) {
portList.add(serialPortToMap(port, index++));
}
}
map.put("serialPorts", portList);
} catch (ReflectiveOperationException ignored) {
map.put("serialPorts", new ArrayList<>());
}
try {
Method getIfCount = info.getClass().getMethod("getInterfaceCount");
Object count = getIfCount.invoke(info);
if (count instanceof Integer) {
map.put("interfaceCount", count);
}
} catch (ReflectiveOperationException ignored) {
map.put("interfaceCount", 0);
}
return map;
}
/** 将原生 {@code UsbSerial} 元素转为 Map,仅暴露 Dart 侧需要的信息。 */
private Map<String, Object> serialPortToMap(@Nullable Object port, int fallbackIndex) {
Map<String, Object> map = new HashMap<>();
map.put("portIndex", fallbackIndex);
if (port == null) {
return map;
/** 主动上抛异常给 Dart 侧。 */
private void pushException(int type, String message, @Nullable String cause) {
if (!exceptionCallbackEnabled || channel == null) {
return;
}
try {
Method getIndex = port.getClass().getMethod("getSerialPortIndex");
Object index = getIndex.invoke(port);
if (index instanceof Integer) {
map.put("portIndex", index);
}
} catch (ReflectiveOperationException ignored) {
// 字段可选,保持 fallbackIndex。
}
try {
Method getPath = port.getClass().getMethod("getDevicePath");
Object path = getPath.invoke(port);
map.put("devicePath", path == null ? null : path.toString());
} catch (ReflectiveOperationException ignored) {
// 字段可选,忽略。
}
try {
Method getName = port.getClass().getMethod("getDriverName");
Object name = getName.invoke(port);
map.put("driverName", name == null ? null : name.toString());
} catch (ReflectiveOperationException ignored) {
// 字段可选,忽略。
}
return map;
Map<String, Object> payload = new HashMap<>();
payload.put("type", type);
payload.put("message", message);
payload.put("cause", cause);
channel.invokeMethod("onException", payload);
}
}