一些android 手机是有串口可以提供打开,读取一些底层的数据。如果手机本身没有串口的话,那么打开估计会报错。串口读取数据肯定是使用jni调用c代码来完成的。有一个开源的串口库android-serialport-api。其主页在这里http://code.google.com/p/android-serialport-api/ ,这里可以下到APK及对源码。谷歌的代码库,无奈国内无法下载https://github.com/cepr/android-serialport-api ,GITHUB的地址,这个可以下载 但是下载源码之后发现源码不能直接使用,而且源码结构较为复杂。
在这里我是使用服务来读取串口数据,都出来数据直接写入到txt文件里面。
串口读写的库的地址:https://code.google.com/p/android-serialport-api/
github地址:https://github.com/cepr/android-serialport-api
本篇博客的下载地址:http://download.csdn.net/detail/qq_16064871/9731908
1,注意事项
选择串口时候,需要加上dev/。如果我选择ttyMT2,路径就是dev/ttyMT2。波特率就是整型38400,或者其他。
打开串口以及读取数据的串口类调用加载so库一定放在这个包下面android_serialport_api。不能放在其他包的下面。
SerialPortDevice.class
/** * */ package android_serialport_api; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import android.util.Log; public class SerialPortDevice{ private FileDescriptor mFd; private InputStream mInputStream; private OutputStream mOutputStream; private String path; private int baudrate; int flags=0; public SerialPortDevice(String path, int baudrate, int flags) { mFd = new FileDescriptor(); this.path=path; this.baudrate=baudrate; this.flags=flags; } /* (non-Javadoc) * @see com.gps.device.IDevice#connect() */ public boolean connect() { mFd = open(path, baudrate, 0); if (mFd == null) { return false; } else { return true; } } /* * (non-Javadoc) * * @see com.gps.device.IDevice#getInputStream() */ public InputStream getInputStream() { // TODO Auto-generated method stub return new FileInputStream(mFd); } /* * (non-Javadoc) * * @see com.gps.device.IDevice#getOutputStream() */ public OutputStream getOutputStream() { // TODO Auto-generated method stub return new FileOutputStream(mFd); } // JNI private native static FileDescriptor open(String path, int baudrate, int flags); public native void close(); static { System.loadLibrary("serial_port"); } }
2,服务类,以及封装的串口管理类
SerialPortService.class 服务
package com.mmsx.serial; import com.mmsx.serial.SerialPortIOManage.onSerialPortIOListener; import android.app.Service; import android.content.Intent; import android.os.Environment; import android.os.IBinder; public class SerialPortService extends Service { @Override public void onCreate() { SerialPortIOManage.getInstance().setonSerialPortIOListener(new onSerialPortIOListener() { @Override public void OnIOCallBack(byte[] data, int length) { } @Override public void OnConnectStatusCallBack(boolean statue) { if (statue) { String fileString = Environment.getExternalStorageDirectory().getPath() + "/serial.txt"; FileWrite.GetInstance().Create(fileString); } } }); } @Override public void onDestroy() { SerialPortIOManage.getInstance().disConnect(); super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { SerialPortIOManage.getInstance().Connect("dev/ttyMT2", 38400); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { return null; } }
service服务也是android 的四大组件,别忘了注册
<service android:name="com.mmsx.serial.SerialPortService" android:icon="@drawable/ic_launcher"> </service>
SerialPortIOManage.class串口打开获取数据的管理类
package com.mmsx.serial; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import android_serialport_api.SerialPortDevice; public class SerialPortIOManage{ SerialPortDevice temPortDevice = null; private InputStream mInputStream = null; private OutputStream mOutputStream = null; private String mstrComPath = ""; private int mnBaudrate = 57600; ReadThread mReadThread = null; onSerialPortIOListener mListener = null; private static class SingletonHolder { private static final SerialPortIOManage INSTANCE = new SerialPortIOManage(); } private SerialPortIOManage (){} public static final SerialPortIOManage getInstance() { return SingletonHolder.INSTANCE; } public void setonSerialPortIOListener(onSerialPortIOListener listener){ mListener = listener; } public interface onSerialPortIOListener { abstract void OnConnectStatusCallBack(boolean statue); abstract void OnIOCallBack(byte[] data, int length); } public void Connect(String path, int baudrate) { mstrComPath = path; mnBaudrate = baudrate; temPortDevice = new SerialPortDevice(path, baudrate, 0); if (temPortDevice.connect()) { mInputStream = temPortDevice.getInputStream(); mOutputStream = temPortDevice.getOutputStream(); mReadThread = new ReadThread(); mReadThread.start(); if (null != mListener) mListener.OnConnectStatusCallBack(true); } else { if (null != mListener) mListener.OnConnectStatusCallBack(false); } } public void disConnect() { if(mReadThread != null){ mReadThread.interrupt(); mReadThread = null; } try { if (mInputStream != null) { mInputStream.close(); mInputStream = null; } if (mOutputStream != null) { mOutputStream.close(); mOutputStream = null; } } catch (IOException e) { e.printStackTrace(); } if (temPortDevice != null) { temPortDevice.close(); } } class ReadThread extends Thread{ public void run(){ int nMaxBufLength = 1024; byte[] buffer = new byte[nMaxBufLength]; while(!isInterrupted()){ try{ int byteRead = -1; Thread.sleep(200); if(mInputStream != null){ byteRead = mInputStream.read(buffer); if(byteRead > 0){ if(mListener != null){ mListener.OnIOCallBack(buffer, byteRead); FileWrite.GetInstance().Write(buffer, byteRead); } } } else { break; } }catch (IOException e) { if (mListener != null) { mListener.OnConnectStatusCallBack(false); } break; } catch (InterruptedException e) { e.printStackTrace(); continue; } }//while(!isInterrupted()) } } }
3,实现效果
4,创建文件,持续写入数据管理类
package com.mmsx.serial; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class FileWrite { private long nDataLength = 0; private static class SerialPortFileWriteHolder { private static final FileWrite INSTANCE = new FileWrite(); } private FileWrite (){} public static final FileWrite GetInstance() { return SerialPortFileWriteHolder.INSTANCE; } FileOutputStream moutStream = null; private String mstrFileName =""; public void Create(String strFileName) { Close(); mstrFileName = strFileName; File newFile = new File(strFileName); try { nDataLength = 0; moutStream = new FileOutputStream(newFile); } catch (FileNotFoundException e) { } } public void Close() { if (moutStream != null) { try { nDataLength = 0; moutStream.close(); } catch (IOException e) { } moutStream = null; } } public boolean IsOpen() { return (moutStream != null); } public void Write(byte[] bytes, int nLength) { if (!IsOpen()) { if (!mstrFileName.isEmpty()) { File newFile = new File(mstrFileName); if (newFile.exists()) { try { nDataLength = 0; //文件存在,往文件后面增加内容 moutStream = new FileOutputStream(newFile,true); } catch (FileNotFoundException e) { } }else { Create(mstrFileName); } }else { return; } }else { try { moutStream.write(bytes, 0, nLength); nDataLength = nDataLength + nLength; //文件大于10M就停止写入 if (nDataLength > 10485760) { Close(); } } catch (IOException e) { Close(); } } } }
往存储创建文件,写入数据,别忘了权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
5,最后总结
当前进入一个activity页面,打开串口拿到数据持续写入数据到文件里面,别忘了生命周期的影响。如果点击了home,back,或者返回finish等。如果读取数据线程关闭了,怎么重新开启等问题。手机应用的大多数情况下我们只能在手机上看到一个程序的一个界面,用户除了通过程序界面上的功能按钮来在不同的窗体间切换,还可以通过Back键和 Home键来返回上一个窗口,而用户使用Back或者Home的时机是非常不确定的,任何时候用户都可以使用Home或Back来强行切换当前的界面。
HOME键的执行顺序:onPause->onStop->onRestart->onStart->onResume
BACK键的顺序: onPause->onStop->onDestroy->onCreate->onStart->onResume
onPause不要做太耗时的工作
解决办法:
可以在onResume里面重新调用查看一下线程的状态。
在这篇博客我就没有写这个方法onResume。之前在项目中遇到过,记录一下。就是打开串口采集时候,然后点击home,有概率会停止存数据。