Uniapp 串口通信原生插件开发指南(零基础版)
开发前的准备工作
首先,你需要安装以下工具:
- HBuilderX - 这是开发 Uniapp 应用和原生插件的主要工具
- Android Studio - 用于编写和编译 Android 原生代码
- Java JDK - 确保安装 JDK 8 或以上版本
- Android SDK - 通过 Android Studio 安装
创建插件项目
1. 在 HBuilderX 中创建原生插件项目
- 打开 HBuilderX,选择菜单:文件 -> 新建 -> 原生插件
- 填写插件信息:
- 插件名称:UniSerialPort
- 插件ID:UniSerialPort
- 选择"仅Android平台"
- 点击"创建",项目将被创建在 HBuilderX 的项目目录中
2. 导入项目到 Android Studio
- 打开 Android Studio
- 选择"Open an existing Android Studio project"
- 导航到 HBuilderX 创建的插件项目目录,选择
android
文件夹 - 等待 Android Studio 导入并构建项目
实现串口通信功能
1. 添加 Android 串口库
首先,我们需要添加 Android 串口通信库到项目中:
- 在 Android Studio 中,打开
android/build.gradle
文件 - 在
dependencies
块中添加以下依赖:
dependencies {implementation 'com.github.licheedev:AndroidSerialPort:1.3.1'
}
2. 创建串口管理类
在 Android Studio 中,创建以下类文件:
- SerialPortManager.java - 负责串口的打开、关闭、读写操作
// SerialPortManager.java
package com.uniserialport;import com.licheedev.serialworker.core.SerialConfig;
import com.licheedev.serialworker.core.SerialWorker;
import com.licheedev.serialworker.worker.ByteDataListener;
import com.licheedev.serialworker.worker.Worker;
import java.util.HashMap;
import java.util.Map;public class SerialPortManager {private static Map<String, Worker> workers = new HashMap<>();// 打开串口public static Worker openSerialPort(String path, int baudrate, int dataBits, int stopBits, String parity, int flowControl, int delay) throws Exception {if (workers.containsKey(path)) {closeSerialPort(path);}// 配置串口参数SerialConfig config = new SerialConfig(path, baudrate);config.setDataBits(dataBits);config.setStopBits(stopBits);// 设置校验位switch (parity.toLowerCase()) {case "odd":config.setParity(android_serialport_api.SerialPort.PARITY_ODD);break;case "even":config.setParity(android_serialport_api.SerialPort.PARITY_EVEN);break;default:config.setParity(android_serialport_api.SerialPort.PARITY_NONE);}// 设置流控config.setFlowControl(flowControl);// 创建串口WorkerWorker worker = new SerialWorker(config);worker.setReceiveDelay(delay); // 设置粘包处理延迟workers.put(path, worker);return worker;}// 关闭串口public static void closeSerialPort(String path) {Worker worker = workers.get(path);if (worker != null) {worker.release();workers.remove(path);}}// 发送数据public static void sendData(String path, byte[] data) throws Exception {Worker worker = workers.get(path);if (worker != null) {worker.send(data);}}// 开始监听数据public static void startListening(String path, DataCallback callback) {Worker worker = workers.get(path);if (worker != null) {worker.setDataListener(new ByteDataListener() {@Overridepublic void onDataReceived(byte[] data) {if (callback != null) {callback.onDataReceived(data);}}});}}// 数据回调接口public interface DataCallback {void onDataReceived(byte[] data);}
}
3. 创建 JS 接口模块
创建 SerialPortModule.java 文件,这是插件与 JS 交互的桥梁:
// SerialPortModule.java
package com.uniserialport;import com.alibaba.fastjson.JSONObject;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;public class SerialPortModule extends UniModule {// 打开串口@UniJSMethod(uiThread = false)public void open(Map<String, Object> options, UniJSCallback callback) {try {String path = (String) options.get("path");int baudrate = (Integer) options.get("baudrate");int dataBits = (Integer) options.getOrDefault("dataBits", 8);int stopBits = (Integer) options.getOrDefault("stopBits", 1);String parity = (String) options.getOrDefault("parity", "none");int flowControl = (Integer) options.getOrDefault("flowControl", 0);int delay = (Integer) options.getOrDefault("delay", 20);SerialPortManager.openSerialPort(path, baudrate, dataBits, stopBits, parity, flowControl, delay);callback.invokeSuccess();} catch (Exception e) {callback.invokeError(e.getMessage());}}// 关闭串口@UniJSMethod(uiThread = false)public void close(String path, UniJSCallback callback) {try {SerialPortManager.closeSerialPort(path);callback.invokeSuccess();} catch (Exception e) {callback.invokeError(e.getMessage());}}// 发送数据@UniJSMethod(uiThread = false)public void send(Map<String, Object> options, UniJSCallback callback) {try {String path = (String) options.get("path");String data = (String) options.get("data");byte[] bytes = data.getBytes("UTF-8");SerialPortManager.sendData(path, bytes);callback.invokeSuccess();} catch (Exception e) {callback.invokeError(e.getMessage());}}// 开始监听数据@UniJSMethod(uiThread = false)public void startListening(String path, final UniJSCallback callback) {try {SerialPortManager.startListening(path, new SerialPortManager.DataCallback() {@Overridepublic void onDataReceived(byte[] data) {try {String hexData = bytesToHexString(data);JSONObject result = new JSONObject();result.put("data", hexData);callback.invoke(result);} catch (Exception e) {e.printStackTrace();}}});callback.invokeSuccess();} catch (Exception e) {callback.invokeError(e.getMessage());}}// 工具方法:字节数组转16进制字符串private String bytesToHexString(byte[] bytes) {StringBuilder sb = new StringBuilder();for (byte b : bytes) {String hex = String.format("%02X", b);sb.append(hex);}return sb.toString();}
}
4. 配置插件信息
编辑 package.json 文件,配置插件信息:
{"name": "UniSerialPort","id": "UniSerialPort","description": "Uniapp 串口通信插件","version": "1.0.0","platforms": ["android"],"modules": {"SerialPort": {"class": "com.uniserialport.SerialPortModule","methods": ["open", "close", "send", "startListening"]}},"android": {"permissions": ["<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>","<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>","<uses-permission android:name=\"android.permission.INTERNET\"/>"]}
}
打包和使用插件
1. 打包插件
- 在 HBuilderX 中,右键点击插件项目
- 选择"原生插件" -> “制作本地插件”
- 选择 Android 平台
- 点击"制作"按钮,HBuilderX 将自动调用 Android Studio 编译插件
- 编译完成后,插件将被打包到
nativeplugins/UniSerialPort/dist
目录
2. 在 Uniapp 项目中使用插件
- 在你的 Uniapp 项目中,创建
nativeplugins/UniSerialPort
目录 - 将刚才打包好的插件文件复制到该目录
- 在
manifest.json
中配置插件:
{"app-plus": {"nvueStyleCompiler": "uni-app","usingComponents": true,"nativePlugins": {"UniSerialPort": {"version": "1.0.0","provider": "local"}}}
}
3. 在 JS 代码中调用插件
// 引入插件
const serialPort = uni.requireNativePlugin('UniSerialPort');// 打开串口
serialPort.open({path: '/dev/ttyS1', // 串口设备路径baudrate: 9600, // 波特率dataBits: 8, // 数据位stopBits: 1, // 停止位parity: 'none', // 校验位:none, odd, evenflowControl: 0, // 流控delay: 50 // 粘包处理延迟时间(ms)
}, (res) => {if (res.code === 0) {console.log('串口打开成功');// 开始监听数据startListening();} else {console.error('串口打开失败:', res.message);}
});// 监听数据
function startListening() {serialPort.startListening('/dev/ttyS1', (res) => {console.log('收到数据:', res.data);// 处理接收到的16进制数据});
}// 发送数据
function sendData() {serialPort.send({path: '/dev/ttyS1',data: 'Hello, Serial Port!'}, (res) => {if (res.code === 0) {console.log('数据发送成功');} else {console.error('数据发送失败:', res.message);}});
}// 关闭串口
function closeSerialPort() {serialPort.close('/dev/ttyS1', (res) => {if (res.code === 0) {console.log('串口关闭成功');} else {console.error('串口关闭失败:', res.message);}});
}
常见问题和解决方案
-
找不到串口设备:
- 确认设备上串口的实际路径
- 检查应用是否有访问串口的权限
-
无法打开串口:
- 检查串口是否被其他应用占用
- 确认串口参数配置正确
-
接收数据不完整:
- 调整粘包处理的延迟时间
- 检查数据解析逻辑
-
应用崩溃:
- 检查 AndroidManifest.xml 中是否添加了必要的权限
- 确保所有的串口操作都在 try-catch 块中进行
通过以上步骤,你就可以完成一个功能完整的 Uniapp 串口通信原生插件。即使你没有安卓原生开发经验,按照这个指南一步步操作,也能够成功实现这个插件。