Просмотр исходного кода

1. 优化串口管理器
2. 完善数据接收逻辑

王育民 5 лет назад
Родитель
Сommit
5478eb5e81

+ 6 - 8
app/src/main/java/cn/minbb/serial/activities/MainActivity.java

@@ -1,6 +1,7 @@
 package cn.minbb.serial.activities;
 
 import android.os.Bundle;
+import android.os.Environment;
 import android.support.v7.app.AppCompatActivity;
 import android.view.View;
 import android.widget.Button;
@@ -63,7 +64,7 @@ public class MainActivity extends AppCompatActivity {
             @Override
             public void onDataReceived(byte[] bytes, String data) {
                 runOnUiThread(() -> {
-                    byte[] bytes1 = ByteUtil.addBytes(data.getBytes(), new byte[64]);
+                    Toast.makeText(getApplicationContext(), data, Toast.LENGTH_SHORT).show();
 //                    setText(data, data.length());
                     setText(Arrays.toString(bytes), bytes.length);
 //                    try {
@@ -121,17 +122,14 @@ public class MainActivity extends AppCompatActivity {
                 // byte[] reportID = {0x0};
                 // byte[] data = ByteUtil.addBytes(reportID, editText.getText().toString().getBytes());
                 String s = editText.getText().toString();
-                serialPortManager.sendString(s, (progress, index, total, bytes) -> log(Arrays.toString(bytes)));
-                log("发送字符串:" + s);
+                serialPortManager.sendString(s, (progress, index, total, bytes) -> setText(Arrays.toString(bytes), bytes.length));
+                setText("发送字符:" + s, s.length());
                 Toast.makeText(getApplicationContext(), "数据已发送", Toast.LENGTH_SHORT).show();
                 break;
             case R.id.send_file:
+                File file = new File(Environment.getExternalStorageDirectory(), "Documents/image.jpg");
+                Toast.makeText(getApplicationContext(), file.toString(), Toast.LENGTH_SHORT).show();
                 break;
         }
     }
-
-    private void log(String log) {
-        StringBuilder stringBuilder = new StringBuilder(textView.getText());
-        textView.setText(stringBuilder.append("\n").append(log));
-    }
 }

+ 9 - 7
app/src/main/res/layout/activity_main.xml

@@ -45,28 +45,30 @@
 
     <EditText
         android:id="@+id/edit_text"
-        android:layout_width="match_parent"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginTop="16dp"
+        app:layout_constraintEnd_toStartOf="@id/send_string"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@id/close_serial_port" />
 
     <Button
         android:id="@+id/send_string"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
         android:onClick="onClick"
-        android:text="发送字符"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/edit_text" />
+        android:text="发送字符"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/close_serial_port" />
 
     <Button
         android:id="@+id/send_file"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:onClick="onClick"
         android:text="发送文件"
-        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toBottomOf="@id/send_string" />
 
     <ScrollView

+ 2 - 0
serialport/src/main/AndroidManifest.xml

@@ -1,6 +1,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="cn.minbb.serialport">
 
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
     <application
         android:allowBackup="true"
         android:label="@string/app_name"

+ 87 - 31
serialport/src/main/java/cn/minbb/serialport/SerialPortManager.java

@@ -12,6 +12,7 @@ import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.Serializable;
+import java.util.Arrays;
 import java.util.Timer;
 import java.util.TimerTask;
 
@@ -25,6 +26,8 @@ import cn.minbb.serialport.listener.OnDataProgressListener;
 import cn.minbb.serialport.listener.OnOpenSerialPortListener;
 import cn.minbb.serialport.listener.OnSerialPortDataListener;
 import cn.minbb.serialport.thread.SerialPortReadThread;
+import cn.minbb.serialport.utils.ByteUtil;
+import cn.minbb.serialport.utils.LogUtil;
 
 /**
  * 串口管理器 - 单例模式  防止 反射 克隆 序列化 破坏
@@ -43,6 +46,13 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
     private Handler mSendingHandler;
     private SerialPortReadThread mSerialPortReadThread;
 
+    private SerialPortStatus serialPortStatus = SerialPortStatus.CLOSE;
+
+    // 报文长度
+    private static int LENGTH = 64;
+    // 超时时间
+    private static int TIMEOUT = 5000;
+
     // 默认是第一次创建
     private static volatile boolean isCreate = false;
     // 单例对象
@@ -69,6 +79,22 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
         return serialPortManager;
     }
 
+    /**
+     * 获取当前串口状态
+     */
+    public SerialPortStatus getSerialPortStatus() {
+        return serialPortStatus;
+    }
+
+    /**
+     * 设置接收数据超时时间
+     *
+     * @param timeout 毫秒
+     */
+    public static void setTimeout(int timeout) {
+        SerialPortManager.TIMEOUT = timeout;
+    }
+
     /**
      * 打开串口
      * <p>
@@ -107,6 +133,7 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
             startSendThread();
             // 开启接收消息的线程
             startReadThread();
+            serialPortStatus = SerialPortStatus.OPEN;
             return true;
         } catch (Exception e) {
             e.printStackTrace();
@@ -150,6 +177,7 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
 
         mOnOpenSerialPortListener = null;
         mOnSerialPortDataListener = null;
+        serialPortStatus = SerialPortStatus.CLOSE;
     }
 
     /**
@@ -190,18 +218,18 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
                     Data data = (Data) msg.obj;
                     switch (data.getDataType()) {
                         case BYTES:
-                            SendDataBytesImpl sendDataBytes = new SendDataBytesImpl();
-                            SendDataContext<byte[]> contextBytes = new SendDataContext<>(mFileInputStream, mFileOutputStream, onDataProgressListener, sendDataBytes);
+                            SendDataBytesImpl sendDataBytesImpl = new SendDataBytesImpl();
+                            SendDataContext<byte[]> contextBytes = new SendDataContext<>(mFileInputStream, mFileOutputStream, onDataProgressListener, sendDataBytesImpl);
                             contextBytes.doSend((byte[]) data.getData());
                             break;
                         case STRING:
-                            SendDataStringImpl sendDataString = new SendDataStringImpl();
-                            SendDataContext<String> contextString = new SendDataContext<>(mFileInputStream, mFileOutputStream, onDataProgressListener, sendDataString);
+                            SendDataStringImpl sendDataStringImpl = new SendDataStringImpl();
+                            SendDataContext<String> contextString = new SendDataContext<>(mFileInputStream, mFileOutputStream, onDataProgressListener, sendDataStringImpl);
                             contextString.doSend((String) data.getData());
                             break;
                         case FILE:
-                            SendDataFileImpl sendDataFile = new SendDataFileImpl();
-                            SendDataContext<File> contextFile = new SendDataContext<>(mFileInputStream, mFileOutputStream, onDataProgressListener, sendDataFile);
+                            SendDataFileImpl sendDataFileImpl = new SendDataFileImpl();
+                            SendDataContext<File> contextFile = new SendDataContext<>(mFileInputStream, mFileOutputStream, onDataProgressListener, sendDataFileImpl);
                             contextFile.doSend((File) data.getData());
                             break;
                     }
@@ -216,9 +244,9 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
 //                        }
 //                    }
                 } catch (IOException e) {
-                    e.printStackTrace();
+                    throw new RuntimeException(e);
                 } catch (Exception e) {
-                    e.printStackTrace();
+                    throw new RuntimeException(e);
                 }
             }
         };
@@ -239,47 +267,48 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
     /**
      * 开启接收消息的线程
      */
-    private static String data = "";
-    private static boolean over = false;
-    private final long TIMEOUT = 500;  // 超时时间
+    private static byte[] dataBytes = new byte[0];
 
     private void startReadThread() {
         mSerialPortReadThread = new SerialPortReadThread(mFileInputStream) {
             @Override
             public void onDataReceived(final byte[] bytes) {
-                // if (null != mOnSerialPortDataListener) {
-                //     mOnSerialPortDataListener.onDataReceived(bytes);
-                // }
-                // 收到数据,计时开始
-                if (data.trim().isEmpty()) {
+                // 收到 EOF(-1) 或者超时则认为数据包接收完毕
+                // 串口空闲状态下收到数据,启动超时计时器,并修改串口状态为数据输入状态
+                if (SerialPortStatus.OPEN == serialPortStatus) {
+                    serialPortStatus = SerialPortStatus.DATA_IN;
                     new Timer().schedule(new TimerTask() {
                         @Override
                         public void run() {
-                            String s = data.trim();
-                            if (!s.isEmpty()) {
-                                over = false;
-                                mOnSerialPortDataListener.onDataReceived(bytes, s);
-                                data = "";
+                            // 超时执行
+                            if (SerialPortStatus.DATA_IN == serialPortStatus) {
+                                // 解包收到的数据
+                                LogUtil.setAppendFile("接收超时");
+                                dataNotify();
                             }
                         }
                     }, TIMEOUT);
                 }
+                // 遍历数据块,检测到报文结束符 EOF(-1) 则终止报文接收
+                int eofIndex = -1;
                 int blockSize = bytes.length;
                 byte[] byteBlock = new byte[blockSize];
                 for (int i = 0; i < blockSize; i++) {
-                    if (bytes[i] == '\n') {
-                        over = true;
+                    if (bytes[i] == -1) {
+                        eofIndex = i;
                         break;
+                    } else {
+                        byteBlock[i] = bytes[i];
                     }
-                    byteBlock[i] = bytes[i];
                 }
-                String block = new String(byteBlock);
-                data += block;
-                // 收到数据结束符
-                if (over && !data.isEmpty()) {
-                    over = false;
-                    mOnSerialPortDataListener.onDataReceived(bytes, data.trim());
-                    data = "";
+                if (eofIndex != -1) {
+                    // 收到包尾,拼接包尾数据后通知
+                    byte[] tailBytes = new byte[eofIndex];
+                    System.arraycopy(byteBlock, 0, tailBytes, 0, eofIndex);
+                    dataBytesReceived(tailBytes);
+                    dataNotify();
+                } else {
+                    dataBytesReceived(byteBlock);
                 }
             }
         };
@@ -341,11 +370,38 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
                 message.obj = data;
                 return mSendingHandler.sendMessage(message);
             }
+            LogUtil.setAppendFile("缺失发送处理器");
             throw new RuntimeException("缺失发送处理器");
         }
+        LogUtil.setAppendFile("数据流为空");
         throw new RuntimeException("数据流为空");
     }
 
+    /**
+     * 数据回调方法,当串口接收数据超时或者收到数据结束符时候调用,用于通知应用程序收到的数据
+     */
+    private void dataNotify() {
+        // 串口状态设置为空闲
+        serialPortStatus = SerialPortStatus.OPEN;
+        if (null != mOnSerialPortDataListener) {
+            mOnSerialPortDataListener.onDataReceived(dataBytes, new String(dataBytes));
+            LogUtil.setAppendFile(Arrays.toString(dataBytes));
+            dataBytes = new byte[0];
+        } else {
+            LogUtil.setAppendFile("串口数据监听器未定义");
+            throw new RuntimeException("串口数据监听器未定义");
+        }
+    }
+
+    /**
+     * 收到串口数据调用,同步锁
+     *
+     * @param bytes 收到的 Byte 数组
+     */
+    private synchronized void dataBytesReceived(byte[] bytes) {
+        dataBytes = ByteUtil.addBytes(dataBytes, bytes);
+    }
+
     @Override
     protected Object clone() throws CloneNotSupportedException {
         return serialPortManager;

+ 23 - 0
serialport/src/main/java/cn/minbb/serialport/SerialPortStatus.java

@@ -0,0 +1,23 @@
+package cn.minbb.serialport;
+
+/**
+ * 串口状态
+ */
+public enum SerialPortStatus {
+    /**
+     * 开启且空闲
+     */
+    OPEN,
+    /**
+     * 关闭
+     */
+    CLOSE,
+    /**
+     * 数据传入中
+     */
+    DATA_IN,
+    /**
+     * 数据传出中
+     */
+    DATA_OUT
+}

+ 3 - 0
serialport/src/main/java/cn/minbb/serialport/impl/SendDataBytesImpl.java

@@ -3,14 +3,17 @@ package cn.minbb.serialport.impl;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.Arrays;
 
 import cn.minbb.serialport.handler.SendDataHandler;
 import cn.minbb.serialport.listener.OnDataProgressListener;
+import cn.minbb.serialport.utils.LogUtil;
 
 public class SendDataBytesImpl implements SendDataHandler<byte[]> {
 
     @Override
     public void send(FileInputStream fileInputStream, FileOutputStream fileOutputStream, OnDataProgressListener onDataProgressListener, byte[] bytes) throws IOException {
         fileOutputStream.write(bytes);
+        LogUtil.setAppendFile("发送字符数组:" + Arrays.toString(bytes));
     }
 }

+ 4 - 1
serialport/src/main/java/cn/minbb/serialport/impl/SendDataFileImpl.java

@@ -5,6 +5,8 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 import cn.minbb.serialport.handler.SendDataHandler;
 import cn.minbb.serialport.listener.OnDataProgressListener;
@@ -16,12 +18,13 @@ public class SendDataFileImpl implements SendDataHandler<File> {
         FileInputStream fis = new FileInputStream(file);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         int len;
-        byte[] buffer = new byte[1024];
+        byte[] buffer = new byte[64];
         while ((len = fis.read(buffer)) != -1) {
             baos.write(buffer, 0, len);
         }
         byte[] byteArray = baos.toByteArray();
         fis.close();
         baos.close();
+        List<byte[]> list = new ArrayList<>();
     }
 }

+ 4 - 0
serialport/src/main/java/cn/minbb/serialport/impl/SendDataStringImpl.java

@@ -4,15 +4,19 @@ import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.charset.Charset;
+import java.util.Arrays;
 
 import cn.minbb.serialport.handler.SendDataHandler;
 import cn.minbb.serialport.listener.OnDataProgressListener;
+import cn.minbb.serialport.utils.LogUtil;
 
 public class SendDataStringImpl implements SendDataHandler<String> {
 
     @Override
     public void send(FileInputStream fileInputStream, FileOutputStream fileOutputStream, OnDataProgressListener onDataProgressListener, String s) throws IOException {
         byte[] dataBytes = s.getBytes(Charset.forName("UTF-8"));
+        LogUtil.setAppendFile("发送字符:[" + s + "] -> " + Arrays.toString(dataBytes));
+        // 分包,发送数据
         fileOutputStream.write(dataBytes);
         byte[] buffer = new byte[64];
         int length;

+ 7 - 0
serialport/src/main/java/cn/minbb/serialport/utils/ByteUtil.java

@@ -2,6 +2,13 @@ package cn.minbb.serialport.utils;
 
 public class ByteUtil {
 
+    /**
+     * 拼接两个 Byte 数组
+     *
+     * @param data1 Byte 数组 1
+     * @param data2 Byte 数组 2
+     * @return 拼接后的数组,长度为两个数组长度之和
+     */
     public static byte[] addBytes(byte[] data1, byte[] data2) {
         byte[] data3 = new byte[data1.length + data2.length];
         System.arraycopy(data1, 0, data3, 0, data1.length);

+ 42 - 0
serialport/src/main/java/cn/minbb/serialport/utils/LogUtil.java

@@ -0,0 +1,42 @@
+package cn.minbb.serialport.utils;
+
+import android.os.Environment;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class LogUtil {
+
+    // 路径为 /sdcard/[appName]/log.txt
+    private static final String appName = "Documents";
+    private static File file;
+
+    static {
+        file = new File(Environment.getExternalStorageDirectory(), appName);
+        if (!file.exists()) {
+            file.mkdirs();
+        }
+        file = new File(file, SimpleDateFormat.getDateInstance().format(new Date()) + ".txt");
+    }
+
+    /**
+     * 将文本追加写入到文件
+     */
+    public static void setAppendFile(String value) {
+        try {
+            FileWriter fw = new FileWriter(file, true);
+            BufferedWriter bw = new BufferedWriter(fw);
+            PrintWriter printWriter = new PrintWriter(bw);
+            printWriter.print(SimpleDateFormat.getDateTimeInstance().format(new Date()) + ":[");
+            printWriter.print(value);
+            printWriter.println("]");
+            printWriter.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}