在Android串口通信:基本知识梳理(http://gqdy365.iteye.com/admin/blogs/2188846)的基础上,我结合我项目中使用串口的实例,进行总结;
Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化;
Google串口开源项目见:https://code.google.com/p/android-serialport-api/
下面是我项目中的相关代码及介绍:
1、SerialPort.cpp
在编译时注意修改const char* kClassName = "com/jerome/serialport/SerialPort";为你Java层与jni对应得包名;
2、Android.mk
如果要修改生成so文件的名称,请修改LOCAL_MODULE := serial_port
3、SerialPort.java
4、SerialPortUtil.java
5、使用方法:
a、配置ndk开发环境,具体百度一下;
b、工程根目录下新建jni文件夹,将Android.mk和SerialPort.cpp放进去;
c、ndk中进入jni目录,编译生成so文件,默认so生成在libs/armeabi下;
d、新建com.jerom.serialport目录,将SerialPort和SerialPortUtil放进去;
f、在你要使用的地方初始化SerialPortUtil,实现回调接口OnDataReceiveListener即可接受数据;
总结:
1、串口发送实质就是向串口设备(类似于文件操作)写入字节流,串口读取也是一样;
2、主要jni与Java native得对应;
Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化;
Google串口开源项目见:https://code.google.com/p/android-serialport-api/
下面是我项目中的相关代码及介绍:
1、SerialPort.cpp
- /*
- * Copyright 2009 Cedric Priscal
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <jni.h>
- #include <assert.h>
- #include <termios.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <string.h>
- #include <jni.h>
- #include "android/log.h"
- static 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)
- static speed_t getBaudrate(jint baudrate) {
- switch (baudrate) {
- case 0:
- return B0;
- case 50:
- return B50;
- case 75:
- return B75;
- case 110:
- return B110;
- case 134:
- return B134;
- 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;
- case 230400:
- return B230400;
- case 460800:
- return B460800;
- case 500000:
- return B500000;
- case 576000:
- return B576000;
- case 921600:
- return B921600;
- case 1000000:
- return B1000000;
- case 1152000:
- return B1152000;
- case 1500000:
- return B1500000;
- case 2000000:
- return B2000000;
- case 2500000:
- return B2500000;
- case 3000000:
- return B3000000;
- case 3500000:
- return B3500000;
- case 4000000:
- return B4000000;
- default:
- return -1;
- }
- }
- /*
- * Class: cedric_serial_SerialPort
- * Method: open
- * Signature: (Ljava/lang/String;)V
- */
- JNIEXPORT jobject JNICALL native_open(JNIEnv *env, jobject thiz, jstring path,jint baudrate) {
- int fd;
- speed_t speed;
- jobject mFileDescriptor;
- LOGD("init native Check arguments");
- /* Check arguments */
- {
- speed = getBaudrate(baudrate);
- if (speed == -1) {
- /* TODO: throw an exception */
- LOGE("Invalid baudrate");
- return NULL;
- }
- }
- LOGD("init native Opening device!");
- /* Opening device */
- {
- jboolean iscopy;
- const char *path_utf = env->GetStringUTFChars(path, &iscopy);
- LOGD("Opening serial port %s", path_utf);
- // fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);
- fd = open(path_utf, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
- LOGD("open() fd = %d", fd);
- env->ReleaseStringUTFChars(path, path_utf);
- if (fd == -1) {
- /* Throw an exception */
- LOGE("Cannot open port %d",baudrate);
- /* TODO: throw an exception */
- return NULL;
- }
- }
- LOGD("init native Configure device!");
- /* Configure device */
- {
- struct termios cfg;
- if (tcgetattr(fd, &cfg)) {
- LOGE("Configure device tcgetattr() failed 1");
- close(fd);
- return NULL;
- }
- cfmakeraw(&cfg);
- cfsetispeed(&cfg, speed);
- cfsetospeed(&cfg, speed);
- if (tcsetattr(fd, TCSANOW, &cfg)) {
- LOGE("Configure device tcsetattr() failed 2");
- close(fd);
- /* TODO: throw an exception */
- return NULL;
- }
- }
- /* Create a corresponding file descriptor */
- {
- jclass cFileDescriptor = env->FindClass("java/io/FileDescriptor");
- jmethodID iFileDescriptor = env->GetMethodID(cFileDescriptor,"<init>", "()V");
- jfieldID descriptorID = env->GetFieldID(cFileDescriptor,"descriptor", "I");
- mFileDescriptor = env->NewObject(cFileDescriptor,iFileDescriptor);
- env->SetIntField(mFileDescriptor, descriptorID, (jint) fd);
- }
- return mFileDescriptor;
- }
- /*
- * Class: cedric_serial_SerialPort
- * Method: close
- * Signature: ()V
- */
- JNIEXPORT jint JNICALL native_close(JNIEnv * env, jobject thiz)
- {
- jclass SerialPortClass = env->GetObjectClass(thiz);
- jclass FileDescriptorClass = env->FindClass("java/io/FileDescriptor");
- jfieldID mFdID = env->GetFieldID(SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
- jfieldID descriptorID = env->GetFieldID(FileDescriptorClass, "descriptor", "I");
- jobject mFd = env->GetObjectField(thiz, mFdID);
- jint descriptor = env->GetIntField(mFd, descriptorID);
- LOGD("close(fd = %d)", descriptor);
- close(descriptor);
- return 1;
- }
- static JNINativeMethod gMethods[] = {
- { "open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;",(void*) native_open },
- { "close", "()I",(void*) native_close },
- };
- /*
- * 为某一个类注册本地方法
- */
- static int registerNativeMethods(JNIEnv* env, const char* className,
- JNINativeMethod* gMethods, int numMethods) {
- jclass clazz;
- clazz = env->FindClass(className);
- if (clazz == NULL) {
- return JNI_FALSE;
- }
- if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
- return JNI_FALSE;
- }
- return JNI_TRUE;
- }
- /*
- * 为所有类注册本地方法
- */
- static int registerNatives(JNIEnv* env) {
- const char* kClassName = "com/jerome/serialport/SerialPort"; //指定要注册的类
- return registerNativeMethods(env, kClassName, gMethods,
- sizeof(gMethods) / sizeof(gMethods[0]));
- }
- /*
- * System.loadLibrary("lib")时调用
- * 如果成功返回JNI版本, 失败返回-1
- */
- JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
- JNIEnv* env = NULL;
- jint result = -1;
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- return -1;
- }
- assert(env != NULL);
- if (!registerNatives(env)) { //注册
- return -1;
- }
- //成功
- result = JNI_VERSION_1_4;
- return result;
- }
在编译时注意修改const char* kClassName = "com/jerome/serialport/SerialPort";为你Java层与jni对应得包名;
2、Android.mk
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- TARGET_PLATFORM := android-3
- LOCAL_MODULE := serial_port
- LOCAL_SRC_FILES := SerialPort.cpp
- LOCAL_LDLIBS := -llog
- include $(BUILD_SHARED_LIBRARY)
如果要修改生成so文件的名称,请修改LOCAL_MODULE := serial_port
3、SerialPort.java
- package com.jerome.serialport;
- import java.io.File;
- import java.io.FileDescriptor;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- public class SerialPort {
- private static final String TAG = "SerialPort";
- /*
- * Do not remove or rename the field mFd: it is used by native method close();
- */
- private FileDescriptor mFd;
- private FileInputStream mFileInputStream;
- private FileOutputStream mFileOutputStream;
- public SerialPort(File device, int baudrate) throws SecurityException, IOException {
- mFd = open(device.getAbsolutePath(), baudrate);
- if (mFd == null) {
- throw new IOException();
- }
- mFileInputStream = new FileInputStream(mFd);
- mFileOutputStream = new FileOutputStream(mFd);
- }
- public InputStream getInputStream() {
- return mFileInputStream;
- }
- public OutputStream getOutputStream() {
- return mFileOutputStream;
- }
- private native FileDescriptor open(String path, int baudrate);
- public native int close();
- static {
- System.loadLibrary("serial_port");
- }
- }
4、SerialPortUtil.java
- package com.jerome.serialport;
- import java.io.BufferedWriter;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- /**
- * 串口操作类
- *
- * @author Jerome
- *
- */
- public class SerialPortUtil {
- private String TAG = SerialPortUtil.class.getSimpleName();
- private SerialPort mSerialPort;
- private OutputStream mOutputStream;
- private InputStream mInputStream;
- private ReadThread mReadThread;
- private String path = "/dev/ttyMT1";
- private int baudrate = 115200;
- private static SerialPortUtil portUtil;
- private OnDataReceiveListener onDataReceiveListener = null;
- private boolean isStop = false;
- public interface OnDataReceiveListener {
- public void onDataReceive(byte[] buffer, int size);
- }
- public void setOnDataReceiveListener(
- OnDataReceiveListener dataReceiveListener) {
- onDataReceiveListener = dataReceiveListener;
- }
- public static SerialPortUtil getInstance() {
- if (null == portUtil) {
- portUtil = new SerialPortUtil();
- portUtil.onCreate();
- }
- return portUtil;
- }
- /**
- * 初始化串口信息
- */
- public void onCreate() {
- try {
- mSerialPort = new SerialPort(new File(path), baudrate);
- mOutputStream = mSerialPort.getOutputStream();
- mInputStream = mSerialPort.getInputStream();
- mReadThread = new ReadThread();
- isStop = false;
- mReadThread.start();
- } catch (Exception e) {
- e.printStackTrace();
- }
- initBle();
- }
- /**
- * 发送指令到串口
- *
- * @param cmd
- * @return
- */
- public boolean sendCmds(String cmd) {
- boolean result = true;
- byte[] mBuffer = (cmd+"\r\n").getBytes();
- //注意:我得项目中需要在每次发送后面加\r\n,大家根据项目项目做修改,也可以去掉,直接发送mBuffer
- try {
- if (mOutputStream != null) {
- mOutputStream.write(mBuffer);
- } else {
- result = false;
- }
- } catch (IOException e) {
- e.printStackTrace();
- result = false;
- }
- return result;
- }
- public boolean sendBuffer(byte[] mBuffer) {
- boolean result = true;
- String tail = "\r\n";
- byte[] tailBuffer = tail.getBytes();
- byte[] mBufferTemp = new byte[mBuffer.length+tailBuffer.length];
- System.arraycopy(mBuffer, 0, mBufferTemp, 0, mBuffer.length);
- System.arraycopy(tailBuffer, 0, mBufferTemp, mBuffer.length, tailBuffer.length);
- //注意:我得项目中需要在每次发送后面加\r\n,大家根据项目项目做修改,也可以去掉,直接发送mBuffer
- try {
- if (mOutputStream != null) {
- mOutputStream.write(mBufferTemp);
- } else {
- result = false;
- }
- } catch (IOException e) {
- e.printStackTrace();
- result = false;
- }
- return result;
- }
- private class ReadThread extends Thread {
- @Override
- public void run() {
- super.run();
- while (!isStop && !isInterrupted()) {
- int size;
- try {
- if (mInputStream == null)
- return;
- byte[] buffer = new byte[512];
- size = mInputStream.read(buffer);
- if (size > 0) {
- if(MyLog.isDyeLevel()){
- MyLog.log(TAG, MyLog.DYE_LOG_LEVEL, "length is:"+size+",data is:"+new String(buffer, 0, size));
- }
- if (null != onDataReceiveListener) {
- onDataReceiveListener.onDataReceive(buffer, size);
- }
- }
- Thread.sleep(10);
- } catch (Exception e) {
- e.printStackTrace();
- return;
- }
- }
- }
- }
- /**
- * 关闭串口
- */
- public void closeSerialPort() {
- sendShellCommond1();
- isStop = true;
- if (mReadThread != null) {
- mReadThread.interrupt();
- }
- if (mSerialPort != null) {
- mSerialPort.close();
- }
- }
- }
5、使用方法:
a、配置ndk开发环境,具体百度一下;
b、工程根目录下新建jni文件夹,将Android.mk和SerialPort.cpp放进去;
c、ndk中进入jni目录,编译生成so文件,默认so生成在libs/armeabi下;
d、新建com.jerom.serialport目录,将SerialPort和SerialPortUtil放进去;
f、在你要使用的地方初始化SerialPortUtil,实现回调接口OnDataReceiveListener即可接受数据;
总结:
1、串口发送实质就是向串口设备(类似于文件操作)写入字节流,串口读取也是一样;
2、主要jni与Java native得对应;