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得对应;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。