公司最近正好有个关于Android串口通信的模块,所以我学习并总结了一下,Android串口通信要使用到JNI以及NDK的内容

串口开发需要Root权限

关于串口的操作不外乎几步:

   1.打开串口(及配置串口);

   2.读串口;

   3.写串口;

   4.关闭串口。

第一:JNI技术,它使得java中可以调用c语言写成的库。源码:点击下载源码。

下载完成后将jni以及jniLibs文件夹直接拉到java同级的目录下,如下:

android 关于使用androidStudio开发串口问题_第1张图片

然后在你的项目的java文件下创建一个包,包名为android_serialport_api。这里的包名对应的是jni中SerialPort.c文件中的方法名。

android 关于使用androidStudio开发串口问题_第2张图片

如果你不想将你的包名命名为"android_serialport_api"的话,则需要将SerialPort.c文件中对应的方法名也改掉。

创建好包后,接下来就是在该包下新建一个SerialPort类,在这个类中我们主要创建两个本地方法,分别为open,以及close,

运行时,Android会自动的调用jni中对应的方法,从而实现对指定串口的打开及关闭。

  1. package android.serialport;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileDescriptor;  
  5. import java.io.FileInputStream;  
  6. import java.io.FileOutputStream;  
  7. import java.io.IOException;  
  8. import java.io.InputStream;  
  9. import java.io.OutputStream;  
  10.   
  11. import android.util.Log;  
  12.   
  13. public class SerialPort {  
  14.   
  15.     private static final String TAG = "SerialPort";  
  16.   
  17.     /* 
  18.      * Do not remove or rename the field mFd: it is used by native method close(); 
  19.      */  
  20.     private FileDescriptor mFd;  
  21.     private FileInputStream mFileInputStream;  
  22.     private FileOutputStream mFileOutputStream;  
  23.   
  24.     public SerialPort(File device, int baudrate) throws SecurityException, IOException {  
  25.   
  26.         /* Check access permission */  
  27.         if (!device.canRead() || !device.canWrite()) {  
  28.             try {  
  29.                 /* Missing read/write permission, trying to chmod the file */  
  30.                 Process su;  
  31.                 su = Runtime.getRuntime().exec("/system/bin/su");  
  32.                 String cmd = "chmod 777 " + device.getAbsolutePath() + "\n"  
  33.                         + "exit\n";  
  34.                 /*String cmd = "chmod 777 /dev/s3c_serial0" + "\n" 
  35.                 + "exit\n";*/  
  36.                 su.getOutputStream().write(cmd.getBytes());  
  37.                 if ((su.waitFor() != 0) || !device.canRead()  
  38.                         || !device.canWrite()) {  
  39.                     throw new SecurityException();  
  40.                 }  
  41.             } catch (Exception e) {  
  42.                 e.printStackTrace();  
  43.                 throw new SecurityException();  
  44.             }  
  45.         }  
  46.   
  47.         mFd = open(device.getAbsolutePath(), baudrate);  
  48.         if (mFd == null) {  
  49.             Log.e(TAG, "native open returns null");  
  50.             throw new IOException();  
  51.         }  
  52.         mFileInputStream = new FileInputStream(mFd);  
  53.         mFileOutputStream = new FileOutputStream(mFd);  
  54.     }  
  55.   
  56.     // Getters and setters  
  57.     public InputStream getInputStream() {  
  58.         return mFileInputStream;  
  59.     }  
  60.   
  61.     public OutputStream getOutputStream() {  
  62.         return mFileOutputStream;  
  63.     }  
  64.   
  65.     // JNI  
  66.     private native static FileDescriptor open(String path, int baudrate);  
  67.     public native void close();  
  68.     static {  
  69.         System.loadLibrary("serial_port");  
  70.     }  
  71. }  

可以看到System.loadLibrary("serial_port");一句,这一句就是用来加载动态链接库。我们的串口操作就是要给予这个类来实现。

接下来,在你的项目的build.gradle(app)文件中加入这么几句话:
sourceSets {    main { jni.srcDirs = [] }}
将其放在buildTypes内部,如下:
buildTypes {    release {        minifyEnabled false        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'    }    sourceSets {        main { jni.srcDirs = [] }    }}

至此,项目的引入以及配置就完成了,接下来就只要将你的项目build一下,然后运行,能成功运行就表示配置完成了。

按照以上步骤将你的项目配置完成后,接下来就是开始编写你的项目了,我这里创建了一个工具类来对串口的打开、关闭、发送及接收进行处理。

代码如下:

public class SerialPortUtil {    public static SerialPort serialPort = null;    public static InputStream inputStream = null;    public static OutputStream outputStream = null;    public static Thread receiveThread = null;    public static boolean flag = false;    public static String serialData;    /**     * 打开串口的方法     */    public static void openSrialPort(){        Log.i("test","打开串口");        try {            serialPort = new SerialPort(new File("/dev/"+ IConstant.PORT),IConstant.BAUDRATE,0);            //获取打开的串口中的输入输出流,以便于串口数据的收发            inputStream = serialPort.getInputStream();            outputStream = serialPort.getOutputStream();            flag = true;            receiveSerialPort();        } catch (IOException e) {            e.printStackTrace();        }    }    /**     *关闭串口的方法     * 关闭串口中的输入输出流     * 然后将flag的值设为flag,终止接收数据线程     */    public static void closeSerialPort(){        Log.i("test","关闭串口");        try {            if(inputStream != null) {                inputStream.close();            }            if(outputStream != null){                outputStream.close();            }            flag = false;        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * 发送串口数据的方法     * @param data 要发送的数据     */    public static void sendSerialPort(String data){        Log.i("test","发送串口数据");        try {            byte[] sendData = data.getBytes();            outputStream.write(sendData);            outputStream.flush();            Log.i("test","串口数据发送成功");        } catch (IOException e) {            e.printStackTrace();            Log.i("test","串口数据发送失败");        }    }    /**     * 接收串口数据的方法     */    public static void receiveSerialPort(){        Log.i("test","接收串口数据");        if(receiveThread != null)            return;        /*定义一个handler对象要来接收子线程中接收到的数据            并调用Activity中的刷新界面的方法更新UI界面         */        final Handler handler = new Handler(){            @Override            public void handleMessage(Message msg) {                if(msg.what==1){                    MainActivity.refreshTextView(serialData);                }            }        };        /*创建子线程接收串口数据         */        receiveThread = new Thread(){            @Override            public void run() {                while (flag) {                    try {                        byte[] readData = new byte[1024];                        if (inputStream == null) {                            return;                        }                        int size = inputStream.read(readData);                        if (size>0 && flag) {                            serialData = new String(readData,0,size);                            Log.i("test", "接收到串口数据:" + serialData);                            /*将接收到的数据封装进Message中,然后发送给主线程                             */                            handler.sendEmptyMessage(1);                            Thread.sleep(1000);                        }                    } catch (IOException e) {                        e.printStackTrace();                    }catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        };        //启动接收线程        receiveThread.start();    }}
我这里接收到串口数据后,先通过handler消息传递机制传递给主线程,然后在主线程中调用Activity中的refreshTextView(String)方法将接收到字符串传到Activity使其更新界面显示。 接下来,就是创建一个Activity来调用该工具类中的方法以实现对于串口的处理。我这里就不贴出了,

一些可能遇到的问题请参考以下:

1、如果程序报SerialPort的26行,即:
Process su = Runtime.getRuntime().exec("/system/xbin/su");
IO异常,则可能你的模拟器没有root权限,具体开启方法,也请自行百度你使用的模拟器的root方法。 2、串口可以正常打开,但无法收发串口信息。 首先,确认你的两个串口是否正常连接。若已正常连接,且可以使用串口调试工具对两个串口互发数据,请往下看: 尝试更改你的串口号及波特率。如我的串口号是定义在一个接口中的常量,使用的是ttyS1。

若有不好的地方,还请各位指教,不胜感激


更多相关文章

  1. Android安卓系统提示应用程序未安装的解决方法
  2. android 从文件制定位置读取数据
  3. Android---8---Intent及使用Intent传递数据
  4. Android 文件缓存方法
  5. 还原Android彩信数据库
  6. 一种Android数据请求框架
  7. 使用delphi 开发 web(五)Android 与delphi 服务器交互访问数据库
  8. adb shell 调试 Android 串口
  9. Android 2.2数据共享功能开启与否将由运营商自主决定

随机推荐

  1. Android4.2 4.4keyguard锁屏流程梳理
  2. android复制文字功能
  3. WMS总体框架
  4. Android(安卓)点击输入框弹出日历 《H》
  5. qt for android opencv 笔记
  6. 图片保存到sd卡
  7. Android(安卓)TextView实现超链接
  8. android 分类联动效果 模仿每日优鲜
  9. android————EditText
  10. android Tab和ViewPager结合的例子