android 关于使用androidStudio开发串口问题

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

串口开发需要Root权限

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

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

   2.读串口;

   3.写串口;

   4.关闭串口。

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

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


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


如果你不想将你的包名命名为"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。

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


猜你喜欢

转载自blog.csdn.net/qq_34650238/article/details/79814568