android 项目串口通信(serialPort)开发遇到的炕
16lz
2022-06-10
android 项目开发中使用到串口通信功能,在过程中遇到了一些炕。
一、首先要安装Cmake以及 ndk工具。
二、在跟main 目录下创建cpp 文件夹,以及CMakeLists.text 、SerialPort.c 文件
三、在CMakeLists.text 文件中编写生成动态so库
# For more information about using CMake with Android Studio, read the# documentation: https://d.android.com/studio/projects/add-native-code.html# Sets the minimum version of CMake required to build the native library.cmake_minimum_required(VERSION 3.4.1)# Creates and names a library, sets it as either STATIC# or SHARED, and provides the relative paths to its source code.# You can define multiple libraries, and CMake builds them for you.# Gradle automatically packages shared libraries with your APK.add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). SerialPort.c )# Searches for a specified prebuilt library and stores the path as a# variable. Because CMake includes system libraries in the search path by# default, you only need to specify the name of the public NDK library# you want to add. CMake verifies that the library exists before# completing its build.find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log)# Specifies libraries CMake should link to your target library. You# can link multiple libraries, such as libraries you define in this# build script, prebuilt third-party libraries, or system libraries.target_link_libraries( # Specifies the target library. native-lib # Links the target library to the log library # included in the NDK. ${log-lib})
四、在SerialPort.c 文件中写调用jni 接口,一般都是使用android 标准串口。
/* * 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 o0r 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 #include #include #include #include #include #include #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;default: return -1;}}/* * Class: cedric_serial_SerialPort * Method: open * Signature: (Ljava/lang/String;)V */JNIEXPORT jobject JNICALL Java_com_serialPort_SerialPort_open (JNIEnv *env, jobject thiz, jstring path, jint baudrate){int fd;speed_t speed;jobject mFileDescriptor;/* Check arguments */{speed = getBaudrate(baudrate);if (speed == -1) {return NULL;}}/* Opening device */{jboolean iscopy;const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);LOGD("Opening serial port %s", path_utf);fd = open(path_utf, O_RDWR | O_SYNC);LOGD("open() fd = %d", fd);(*env)->ReleaseStringUTFChars(env, path, path_utf);if (fd == -1){/* Throw an exception */LOGE("Cannot open port");/* TODO: throw an exception */return NULL;}}/* Configure device */{struct termios cfg;LOGD("Configuring serial port");if (tcgetattr(fd, &cfg)){LOGE("tcgetattr() failed");close(fd);/* TODO: throw an exception */return NULL;}cfmakeraw(&cfg);cfsetispeed(&cfg, speed);cfsetospeed(&cfg, speed);if (tcsetattr(fd, TCSANOW, &cfg)){LOGE("tcsetattr() failed");close(fd);/* TODO: throw an exception */return NULL;}}/* Create a corresponding file descriptor */{jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V");jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);}return mFileDescriptor;}/* * Class: cedric_serial_SerialPort * Method: close * Signature: ()V */JNIEXPORT void JNICALL Java_com_serialPort_SerialPort_close (JNIEnv *env, jobject thiz){jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);LOGD("close(fd = %d)", descriptor);close(descriptor);}
五、创建Serialport文件:
* 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. */import android.util.Log;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 { /* Check access permission *///if (!device.canRead() || !device.canWrite()) {//try {///* Missing read/write permission, trying to chmod the file *///Process su;//su = Runtime.getRuntime().exec("/system/xbin/su");///*String cmd = "chmod 777 " + device.getAbsolutePath() + "\n"//+ "exit\n";*///String cmd = "chmod 777 /dev/ttyS4" + "\n"//+ "exit\n";//su.getOutputStream().write(cmd.getBytes());//if ((su.waitFor() != 0) || !device.canRead()//|| !device.canWrite()) {//throw new SecurityException();//}//} catch (Exception e) {//e.printStackTrace();//throw new SecurityException();//}//} // mFd = open(device.getAbsolutePath(), baudrate, 0); if (mFd == null) { Log.e(TAG, "native open returns null"); throw new IOException(); } mFileInputStream = new FileInputStream(mFd); mFileOutputStream = new FileOutputStream(mFd); } public InputStream getInputStream() { return mFileInputStream; } public OutputStream getOutputStream() { return mFileOutputStream; } private native static FileDescriptor open(String path, int baudrate); public native void close(); public native static FileDescriptor open(String path, int baudrate, int flags); }
要注意文件路径创建要跟jni调用路径一直否则调用不到,出现找不到方法的异常,特别是编译好so库之后,给第三方使用要注明
六、读取线程
class ReadThread extends Thread{ @Override public void run() { super.run(); byte[] bytes = new byte[1024]; while (!isInterrupted()){ int size = 0; try { if (mInputStream != null) { if (mInputStream.available() > 0 ) { SystemClock.sleep(2); try { size = mInputStream.read(bytes); if (size > 0) { Log.i("SerialPort_test","==== " + bytes ); } } catch (IOException e) { e.printStackTrace(); } } } } catch (Exception e) { e.printStackTrace(); } } } }
**在通信过程中如数据比较大,可以会遇到数据读取不完整,感觉有掉帧一样,执行mInputStream.available(),这时可以在读取之前先延时一段时间,再一次文件流读取数据,
避免数据的错乱,接着过滤协议数据。
**读线程所在的类实例最好是一个单列模式,以避免多线程操作创建不同的对象,否则会读取到数据也跟掉帧一样,多线程并发还是要注意的!!!
更多相关文章
- [读书笔记]intent.putExtra的使用与原理分析
- android工程字符串多语言翻译遗漏查找工具
- Adapter软件设计模式在Android的应用
- Android(安卓)studio出现Error:Unable to tunnel through proxy.
- android模拟器SD卡使用
- Android(安卓)NDK移植libiconv和libxml2
- mybatisplus的坑 insert标签insert into select无参数问题的解决
- python起点网月票榜字体反爬案例
- NPM 和webpack 的基础使用