开发前的准备工作
首先,你需要安装以下工具:
- 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);
// 创建串口Worker
Worker 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() {
@Override
public 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() {
@Override
public 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, even
flowControl: 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 串口通信原生插件。即使你没有安卓原生开发经验,按照这个指南一步步操作,也能够成功实现这个插件。