在Android串口通信:基本知识梳理(http://gqdy365.iteye.com/admin/blogs/2188846)的基础上,我结合我项目中使用串口的实例,进行总结;

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-3LOCAL_MODULE    := serial_portLOCAL_SRC_FILES := SerialPort.cppLOCAL_LDLIBS    := -lloginclude $(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,大家根据项目项目做修改,也可以去掉,直接发送mBuffertry {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,大家根据项目项目做修改,也可以去掉,直接发送mBuffertry {if (mOutputStream != null) {mOutputStream.write(mBufferTemp);} else {result = false;}} catch (IOException e) {e.printStackTrace();result = false;}return result;}private class ReadThread extends Thread {@Overridepublic 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得对应;

更多相关文章

  1. Android如何使用so文件和Android(安卓)studio中导入so
  2. android studio使用问题及说明
  3. 用Gradle 构建你的android程序
  4. [置顶] Android项目组织和代码重用
  5. Android(安卓)项目结构图
  6. Android(安卓)8.1 中Systemui中的常见修改(六)NavigationBar加载流
  7. eclipse导入的Android项目没有android.jar包并报错
  8. Cortex-A8和Android应用开发视频教程
  9. Android(安卓)开发一定要看的15个实战项目

随机推荐

  1. android push notification serfice andr
  2. Android用户注册界面
  3. android中基于蓝牙开发的demo
  4. Android(安卓)OpenGL ES学习笔记之常用AP
  5. Android_Log_1_基础知识
  6. 理解Android中的Handler/Message
  7. 有关Android中EditText的一些属性
  8. Android(安卓)模拟器现已支持 AMD 处理器
  9. android开机logo和动画修改
  10. Android onSaveInstanceState和onRestore