Android串口通信:串口读写实例

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 
/* 
 * 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得对应; 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值