类结构
Java层
SerialPortManager:对外提供单例类
public class SerialPortManager {
NativeInterface nativeInterface;
private SerialPortManager() {}
private static class InstanceHolder {
private static SerialPortManager instance = new SerialPortManager();
}
public static SerialPortManager getInstance() {
return InstanceHolder.instance;
}
public void setPortCallback(SerialPortCallback callback) {
nativeInterface = new NativeInterface(callback);
}
public String[] getDevices() {
SerialPortFinder finder = new SerialPortFinder();
return finder.getAllDevicesPath();
}
public boolean open(String strDeviceName, int baudRate) {
if (nativeInterface != null) {
if (nativeInterface.open(strDeviceName, baudRate)) {
nativeInterface.read();
return true;
}
}
return false;
}
public int writeBytes(byte[] bytes) {
if (nativeInterface != null) {
return nativeInterface.write(bytes);
}
return 0;
}
public void close() {
if (nativeInterface != null) {
nativeInterface.close();
}
}
}
NativeInterface:加载native层lib
public class NativeInterface {
SerialPortCallback mCallback;
static {
System.loadLibrary("serialport-lib");
}
public NativeInterface() {}
public NativeInterface(SerialPortCallback callback) {
mCallback = callback;
}
public void readFromTTY (byte[] bytes) {
Log.d("NativeInterface", "read bytes size:" + bytes.length);
if (mCallback != null) {
mCallback.onPortCallback(bytes);
}
}
public native boolean open(String strDeviceName, int baudRate);
public native int write(byte[] bytes);
public native void read();
public native void close();
}
SerialPortCallback:读取串口数据后,回调interface
public interface SerialPortCallback {
void onPortCallback(byte[] bytes);
}
C++层
#include <jni.h>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <pthread.h>
#include "android/log.h"
const char *TAG = "serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
JavaVM* gJVM;
jobject gJObj;
int tty_fd = -1;
static volatile int gIsThreadExit = 0;
speed_t getBaudRate(jint baudRate) {
switch (baudRate) {
case 0: return B0;
case 50: return B50;
case 75: return B75;
case 110: return B110;
case 150: return B150;
case 200: return B200;
case 300: return B300;
case 600: return B600;
case 1200: return B1200;
case 1800: return B1800;
case 2400: return B2400;
case 4800: return B4800;
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
default: return -1;
}
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_jdt_serialport_NativeInterface_open(
JNIEnv* env,
jobject thiz, jstring deviceName, jint baudRate) {
env->GetJavaVM(&gJVM);
gJObj = env->NewGlobalRef(thiz);
speed_t speed;
if ((speed = getBaudRate(baudRate)) == -1) {
LOGE("Invalid baud rate");
return false;
}
jboolean isCopy;
const char *pDeviceName = env->GetStringUTFChars(deviceName, &isCopy);
LOGD("Opening serial port %s with baud rate %d", pDeviceName, baudRate);
tty_fd = open(pDeviceName, O_RDWR );
env->ReleaseStringUTFChars(deviceName, pDeviceName);
if (tty_fd < 0) {
LOGE("Open failed : %s", pDeviceName);
return false;
}
struct termios newtio, oldtio;
bzero(&oldtio, sizeof(oldtio));
if (tcgetattr(tty_fd, &oldtio) != 0) {
LOGE("Open failed : get attr failed");
close(tty_fd);
return false;
}
bzero(&newtio, sizeof(newtio));
cfsetispeed(&newtio, speed);
cfsetospeed(&newtio, speed);
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8;
newtio.c_cflag &= ~PARENB;
newtio.c_cflag &= ~CSTOPB;
newtio.c_cc[VTIME] = 2;
newtio.c_cc[VMIN] = 6;
newtio.c_lflag &= ~ICANON;
tcflush(tty_fd, TCIFLUSH);
if (tcsetattr(tty_fd, TCSANOW, &newtio) != 0) {
LOGE("Open failed : set attr failed");
close(tty_fd);
return false;
}
LOGD("Open tty fd:%d", tty_fd);
return true;
}
extern "C" JNIEXPORT void JNICALL
Java_com_jdt_serialport_NativeInterface_close(
JNIEnv* env,
jobject ) {
gIsThreadExit = 1;
close(tty_fd);
LOGD("native read thread stop");
}
static void* native_thread_exec(void* arg) {
JNIEnv *env;
gJVM->AttachCurrentThread(&env, nullptr);
jclass jclz = env->GetObjectClass(gJObj);
if (jclz == nullptr) {
LOGE("Failed to find java class");
return nullptr;
}
jmethodID methodId = env->GetMethodID(jclz, "readFromTTY", "([B)V");
if (methodId == nullptr) {
LOGE("Failed to get java method id");
return nullptr;
}
int rv;
u_char r_buf[128];
fd_set rset;
while (!gIsThreadExit) {
FD_ZERO(&rset);
FD_SET(tty_fd, &rset);
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
LOGD("Select tty fd:%d", tty_fd);
rv = select(tty_fd + 1, &rset, NULL, NULL, &tv);
if (rv < 0) {
LOGE("select failed:%s", strerror(errno));
break;
}
if (0 == rv) {
LOGE("select time out");
if (1 == gIsThreadExit) {
LOGD("select time out, read thread exit");
break;
}
continue;
}
memset(r_buf, 0, sizeof(r_buf));
rv = read(tty_fd, r_buf, 128);
if (rv < 0) {
LOGE("read failed:%s", strerror(errno));
break;
}
LOGD("read len: %d", rv);
jbyteArray buffer = env->NewByteArray(rv);
env->SetByteArrayRegion(buffer, 0, rv, (jbyte*)r_buf);
env->CallVoidMethod(gJObj, methodId, buffer);
}
gJVM->DetachCurrentThread();
return nullptr;
}
extern "C" JNIEXPORT void JNICALL
Java_com_jdt_serialport_NativeInterface_read(
JNIEnv* env,
jobject ) {
gIsThreadExit = 0;
pthread_t threadId;
if (pthread_create(&threadId, NULL, native_thread_exec, NULL) != 0) {
LOGE("pthread create failed");
return;
}
}
extern "C" JNIEXPORT jint JNICALL
Java_com_jdt_serialport_NativeInterface_write(
JNIEnv* env,
jobject , jbyteArray arrayData) {
jbyte *pBytes = env->GetByteArrayElements(arrayData, 0);
jsize len = env->GetArrayLength(arrayData);
int count = write(tty_fd, pBytes, len);
if (count == len) {
return count;
} else {
LOGE("write error");
tcflush(tty_fd, TCOFLUSH);
return -1;
}
}
使用
获取单例类对象
SerialPortManager mManager = SerialPortManager.getInstance();
设置串口数据listener
mManager.setPortCallback(this);
@Override
public void onPortCallback(byte[] bytes) {
String strCallbackData = ByteUtil.bytes2HexStr(bytes);
Log.d("Devices", "callback data: " + strCallbackData);
}
查找串口
String[] devices = mManager.getDevices();
打开串口
boolean opened = mManager.open("/dev/tty1", 9600);
发送数据
byte[] bytes = {(byte)0x80, 0x00, 0x15, (byte)0xA0, (byte)0x01, (byte)0x05, (byte)0x31};
mManager.writeBytes(bytes);
关闭串口
SerialPortManager.getInstance().close();