Android串口通信之串口读写实例
在Android串口通信:基本知识梳理的基础上,我结合我项目中使用串口的实例,进行总结;
Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化;
Google串口开源项目
下面是我项目中的相关代码及介绍:
1、SerialPort.cpp
/* *Copyright2009CedricPriscal * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotusethisfileexceptincompliancewiththeLicense. *YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense. */ #include#include #include #include #include #include #include #include #include #include #include #include"android/log.h" staticconstchar*TAG="serial_port"; #defineLOGI(fmt,args...)__android_log_print(ANDROID_LOG_INFO,TAG,fmt,##args) #defineLOGD(fmt,args...)__android_log_print(ANDROID_LOG_DEBUG,TAG,fmt,##args) #defineLOGE(fmt,args...)__android_log_print(ANDROID_LOG_ERROR,TAG,fmt,##args) staticspeed_tgetBaudrate(jintbaudrate){ switch(baudrate){ case0: returnB0; case50: returnB50; case75: returnB75; case110: returnB110; case134: returnB134; case150: returnB150; case200: returnB200; case300: returnB300; case600: returnB600; case1200: returnB1200; case1800: returnB1800; case2400: returnB2400; case4800: returnB4800; case9600: returnB9600; case19200: returnB19200; case38400: returnB38400; case57600: returnB57600; case115200: returnB115200; case230400: returnB230400; case460800: returnB460800; case500000: returnB500000; case576000: returnB576000; case921600: returnB921600; case1000000: returnB1000000; case1152000: returnB1152000; case1500000: returnB1500000; case2000000: returnB2000000; case2500000: returnB2500000; case3000000: returnB3000000; case3500000: returnB3500000; case4000000: returnB4000000; default: return-1; } } /* *Class:cedric_serial_SerialPort *Method:open *Signature:(Ljava/lang/String;)V */ JNIEXPORTjobjectJNICALLnative_open(JNIEnv*env,jobjectthiz,jstringpath,jintbaudrate){ intfd; speed_tspeed; jobjectmFileDescriptor; LOGD("initnativeCheckarguments"); /*Checkarguments*/ { speed=getBaudrate(baudrate); if(speed==-1){ /*TODO:throwanexception*/ LOGE("Invalidbaudrate"); returnNULL; } } LOGD("initnativeOpeningdevice!"); /*Openingdevice*/ { jbooleaniscopy; constchar*path_utf=env->GetStringUTFChars(path,&iscopy); LOGD("Openingserialport%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){ /*Throwanexception*/ LOGE("Cannotopenport%d",baudrate); /*TODO:throwanexception*/ returnNULL; } } LOGD("initnativeConfiguredevice!"); /*Configuredevice*/ { structtermioscfg; if(tcgetattr(fd,&cfg)){ LOGE("Configuredevicetcgetattr()failed1"); close(fd); returnNULL; } cfmakeraw(&cfg); cfsetispeed(&cfg,speed); cfsetospeed(&cfg,speed); if(tcsetattr(fd,TCSANOW,&cfg)){ LOGE("Configuredevicetcsetattr()failed2"); close(fd); /*TODO:throwanexception*/ returnNULL; } } /*Createacorrespondingfiledescriptor*/ { jclasscFileDescriptor=env->FindClass("java/io/FileDescriptor"); jmethodIDiFileDescriptor=env->GetMethodID(cFileDescriptor," ","()V"); jfieldIDdescriptorID=env->GetFieldID(cFileDescriptor,"descriptor","I"); mFileDescriptor=env->NewObject(cFileDescriptor,iFileDescriptor); env->SetIntField(mFileDescriptor,descriptorID,(jint)fd); } returnmFileDescriptor; } /* *Class:cedric_serial_SerialPort *Method:close *Signature:()V */ JNIEXPORTjintJNICALLnative_close(JNIEnv*env,jobjectthiz) { jclassSerialPortClass=env->GetObjectClass(thiz); jclassFileDescriptorClass=env->FindClass("java/io/FileDescriptor"); jfieldIDmFdID=env->GetFieldID(SerialPortClass,"mFd","Ljava/io/FileDescriptor;"); jfieldIDdescriptorID=env->GetFieldID(FileDescriptorClass,"descriptor","I"); jobjectmFd=env->GetObjectField(thiz,mFdID); jintdescriptor=env->GetIntField(mFd,descriptorID); LOGD("close(fd=%d)",descriptor); close(descriptor); return1; } staticJNINativeMethodgMethods[]={ {"open","(Ljava/lang/String;I)Ljava/io/FileDescriptor;",(void*)native_open}, {"close","()I",(void*)native_close}, }; /* *为某一个类注册本地方法 */ staticintregisterNativeMethods(JNIEnv*env,constchar*className, JNINativeMethod*gMethods,intnumMethods){ jclassclazz; clazz=env->FindClass(className); if(clazz==NULL){ returnJNI_FALSE; } if(env->RegisterNatives(clazz,gMethods,numMethods)<0){ returnJNI_FALSE; } returnJNI_TRUE; } /* *为所有类注册本地方法 */ staticintregisterNatives(JNIEnv*env){ constchar*kClassName="com/jerome/serialport/SerialPort";//指定要注册的类 returnregisterNativeMethods(env,kClassName,gMethods, sizeof(gMethods)/sizeof(gMethods[0])); } /* *System.loadLibrary("lib")时调用 *如果成功返回JNI版本,失败返回-1 */ JNIEXPORTjintJNICALLJNI_OnLoad(JavaVM*vm,void*reserved){ JNIEnv*env=NULL; jintresult=-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; returnresult; }
在编译时注意修改constchar*kClassName="com/jerome/serialport/SerialPort";为你Java层与jni对应得包名;
2、Android.mk
LOCAL_PATH:=$(callmy-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
packagecom.jerome.serialport;
importjava.io.File;
importjava.io.FileDescriptor;
importjava.io.FileInputStream;
importjava.io.FileOutputStream;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.OutputStream;
publicclassSerialPort{
privatestaticfinalStringTAG="SerialPort";
/*
*DonotremoveorrenamethefieldmFd:itisusedbynativemethodclose();
*/
privateFileDescriptormFd;
privateFileInputStreammFileInputStream;
privateFileOutputStreammFileOutputStream;
publicSerialPort(Filedevice,intbaudrate)throwsSecurityException,IOException{
mFd=open(device.getAbsolutePath(),baudrate);
if(mFd==null){
thrownewIOException();
}
mFileInputStream=newFileInputStream(mFd);
mFileOutputStream=newFileOutputStream(mFd);
}
publicInputStreamgetInputStream(){
returnmFileInputStream;
}
publicOutputStreamgetOutputStream(){
returnmFileOutputStream;
}
privatenativeFileDescriptoropen(Stringpath,intbaudrate);
publicnativeintclose();
static{
System.loadLibrary("serial_port");
}
}
4、SerialPortUtil.java
packagecom.jerome.serialport;
importjava.io.BufferedWriter;
importjava.io.File;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.OutputStream;
importjava.io.OutputStreamWriter;
importjava.io.PrintWriter;
/**
*串口操作类
*
*@authorJerome
*
*/
publicclassSerialPortUtil{
privateStringTAG=SerialPortUtil.class.getSimpleName();
privateSerialPortmSerialPort;
privateOutputStreammOutputStream;
privateInputStreammInputStream;
privateReadThreadmReadThread;
privateStringpath="/dev/ttyMT1";
privateintbaudrate=115200;
privatestaticSerialPortUtilportUtil;
privateOnDataReceiveListeneronDataReceiveListener=null;
privatebooleanisStop=false;
publicinterfaceOnDataReceiveListener{
publicvoidonDataReceive(byte[]buffer,intsize);
}
publicvoidsetOnDataReceiveListener(
OnDataReceiveListenerdataReceiveListener){
onDataReceiveListener=dataReceiveListener;
}
publicstaticSerialPortUtilgetInstance(){
if(null==portUtil){
portUtil=newSerialPortUtil();
portUtil.onCreate();
}
returnportUtil;
}
/**
*初始化串口信息
*/
publicvoidonCreate(){
try{
mSerialPort=newSerialPort(newFile(path),baudrate);
mOutputStream=mSerialPort.getOutputStream();
mInputStream=mSerialPort.getInputStream();
mReadThread=newReadThread();
isStop=false;
mReadThread.start();
}catch(Exceptione){
e.printStackTrace();
}
initBle();
}
/**
*发送指令到串口
*
*@paramcmd
*@return
*/
publicbooleansendCmds(Stringcmd){
booleanresult=true;
byte[]mBuffer=(cmd+"\r\n").getBytes();
//注意:我得项目中需要在每次发送后面加\r\n,大家根据项目项目做修改,也可以去掉,直接发送mBuffer
try{
if(mOutputStream!=null){
mOutputStream.write(mBuffer);
}else{
result=false;
}
}catch(IOExceptione){
e.printStackTrace();
result=false;
}
returnresult;
}
publicbooleansendBuffer(byte[]mBuffer){
booleanresult=true;
Stringtail="\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(IOExceptione){
e.printStackTrace();
result=false;
}
returnresult;
}
privateclassReadThreadextendsThread{
@Override
publicvoidrun(){
super.run();
while(!isStop&&!isInterrupted()){
intsize;
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,"lengthis:"+size+",datais:"+newString(buffer,0,size));
}
if(null!=onDataReceiveListener){
onDataReceiveListener.onDataReceive(buffer,size);
}
}
Thread.sleep(10);
}catch(Exceptione){
e.printStackTrace();
return;
}
}
}
}
/**
*关闭串口
*/
publicvoidcloseSerialPort(){
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与Javanative得对应;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。