Android串列埠通訊:串列埠讀寫例項
阿新 • • 發佈:2019-01-11
轉自:http://gqdy365.iteye.com/blog/2188906
在Android串列埠通訊:基本知識梳理(http://gqdy365.iteye.com/admin/blogs/2188846)的基礎上,我結合我專案中使用串列埠的例項,進行總結;
Android使用jni直接進行串列埠裝置的讀寫網上已經有開源專案了,本文是基於網上的開源專案在實際專案中的使用做的調整和優化;
Google串列埠開源專案見:https://code.google.com/p/android-serialport-api/
下面是我專案中的相關程式碼及介紹:
1、SerialPort.cpp
Java程式碼
- /*
- * 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"
- staticconstchar *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) {
- case0:
- return B0;
- case50:
- return B50;
- case75:
- return B75;
- case110:
- return B110;
- case134:
- return B134;
- case150:
- return B150;
- case200:
- return B200;
- case300:
- return B300;
- case600:
- return B600;
- case1200:
- return B1200;
- case1800:
- return B1800;
- case2400:
- return B2400;
- case4800:
- return B4800;
- case9600:
- return B9600;
- case19200:
- return B19200;
- case38400:
- return B38400;
- case57600:
- return B57600;
- case115200:
- return B115200;
- case230400:
- return B230400;
- case460800:
- return B460800;
- case500000:
- return B500000;
- case576000:
- return B576000;
- case921600:
- return B921600;
- case1000000:
- return B1000000;
- case1152000:
- return B1152000;
- case1500000:
- return B1500000;
- case2000000:
- return B2000000;
- case2500000:
- return B2500000;
- case3000000:
- return B3000000;
- case3500000:
- return B3500000;
- case4000000:
- 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;
- constchar *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);
- return1;
- }
- static JNINativeMethod gMethods[] = {
- { "open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;",(void*) native_open },
- { "close", "()I",(void*) native_close },
- };
- /*
- * 為某一個類註冊本地方法
- */
- staticint registerNativeMethods(JNIEnv* env, constchar* 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;
- }
- /*
- * 為所有類註冊本地方法
- */
- staticint registerNatives(JNIEnv* env) {
- constchar* 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
Java程式碼
- 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
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;
- publicclass SerialPort {
- privatestaticfinal 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) {
- thrownew IOException();
- }
- mFileInputStream = new FileInputStream(mFd);
- mFileOutputStream = new FileOutputStream(mFd);
- }
- public InputStream getInputStream() {
- return mFileInputStream;
- }
- public OutputStream getOutputStream() {
- return mFileOutputStream;
- }
- privatenative FileDescriptor open(String path, int baudrate);
- publicnativeint close();
- static {
- System.loadLibrary("serial_port");
- }
- }
4、SerialPortUtil.java
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
- *
- */
- publicclass SerialPortUtil {
- private String TAG = SerialPortUtil.class.getSimpleName();
- private SerialPort mSerialPort;
- private OutputStream mOutputStream;
- private InputStream mInputStream;
- private ReadThread mReadThread;
- private String path = "/dev/ttyMT1";
- privateint baudrate = 115200;
- privatestatic SerialPortUtil portUtil;
- private OnDataReceiveListener onDataReceiveListener = null;
- privateboolean isStop = false;
- publicinterface OnDataReceiveListener {
- publicvoid onDataReceive(byte[] buffer, int size);
- }
- publicvoid setOnDataReceiveListener(
- OnDataReceiveListener dataReceiveListener) {
- onDataReceiveListener = dataReceiveListener;
- }
- publicstatic SerialPortUtil getInstance() {
- if (null == portUtil) {
- portUtil = new SerialPortUtil();
- portUtil.onCreate();
- }
- return portUtil;
- }
- /**
- * 初始化串列埠資訊
- */
- publicvoid 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
- */
- publicboolean 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;
- }
- publicboolean sendBuffer(byte[] mBuffer) {
- boolean result = true;
- String tail = "\r\n";
- byte[] tailBuffer = tail.getBytes();
- byte[] mBufferTemp = newbyte[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;
- }
- privateclass ReadThread extends Thread {
- @Override
- publicvoid run() {
- super.run();
- while (!isStop && !isInterrupted()) {
- int size;
- try {
- if (mInputStream == null)
- return;
- byte[] buffer = newbyte[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;
- }
- }
- }
- }
- /**
- * 關閉串列埠
- */
- publicvoid 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得對應;