,Java 这两类我都接触过,但要先区分运行环境,因为“蓝牙打印 / 扫码枪”在 Java 里实现方式差别很大:


1. 先说结论

蓝牙打印

Java 可以对接,但要看你是:

  • Android Java
    最常见,支持最好。
    通过 BluetoothAdapter / BluetoothDevice / BluetoothSocket 连接蓝牙打印机,然后按打印机指令集发送数据:

    • ESC/POS:小票打印机最常见

    • CPCL / TSPL / ZPL:标签打印机常见

  • Java SE(Windows/Linux/macOS 桌面端)
    也能做,但比 Android 麻烦。通常有几种方式:

    1. 通过系统已配对的蓝牙串口(SPP)通信

    2. 用第三方库访问串口/蓝牙

    3. 如果打印机安装成系统打印机,也可走 javax.print

扫码枪

扫码枪更简单,因为很多扫码枪本质上是键盘输入设备,或者串口设备:

  • USB 扫码枪(键盘口/HID)

    • Java 基本不用专门对接

    • 光标在哪,扫码结果就像键盘输入到哪

    • 一般监听文本框回车即可

  • 串口扫码枪

    • Java 用串口库读取数据即可

    • 常见库:

      • jSerialComm

      • RXTX

      • nrjavaserial

  • 蓝牙扫码枪

    • 如果蓝牙扫码枪工作在 HID 模式:和键盘一样处理

    • 如果工作在 SPP 串口模式:按串口/蓝牙流读取


2. 我实际会怎么判断方案

如果你问“Java 能不能做”,我一般先确认这几个问题:

对蓝牙打印机

  1. Android App 还是 桌面 Java 程序

  2. 打印机品牌型号是什么?

  3. 指令集是什么?

    • ESC/POS

    • TSPL

    • CPCL

    • ZPL

  4. 连接方式是什么?

    • 蓝牙 classic

    • USB

    • WiFi / 网口

  5. 打印内容是什么?

    • 纯文字小票

    • 二维码/条码

    • 标签

对扫码枪

  1. 扫码枪是 USB蓝牙、还是 串口

  2. 当前电脑/设备识别成什么?

    • 键盘

    • COM 串口

  3. 扫码后输出格式是什么?

    • 是否自动回车

    • 是否有前后缀

  4. 运行环境是:

    • Java Swing

    • JavaFX

    • Spring Boot + 本地客户端

    • Android


3. 蓝牙打印:Java 常见做法


3.1 Android Java 对接蓝牙打印机

这是最常见的场景。

核心流程:

  1. 获取蓝牙适配器

  2. 搜索/选择已配对设备

  3. 建立 BluetoothSocket

  4. 获取 OutputStream

  5. 发送 ESC/POS 或标签指令

  6. 关闭连接

示例思路

javaCopy

        
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(macAddress);
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID.fromString(SPP_UUID));
socket.connect();

OutputStream os = socket.getOutputStream();

// 发送 ESC/POS 指令
os.write(new byte[]{0x1B, 0x40}); // 初始化打印机
os.write("Hello Printer\n".getBytes("GBK"));
os.write(new byte[]{0x0A, 0x0A, 0x1D, 0x56, 0x42, 0x00}); // 走纸+切纸

os.flush();
os.close();
socket.close();
        
      

注意点

  • 安卓 12 以后蓝牙权限变了,要申请:

    • BLUETOOTH_CONNECT

    • BLUETOOTH_SCAN

  • 很多小票打印机中文编码要用:

    • GBK

    • 有些也支持 UTF-8,但不一定稳定

  • 不同打印机切纸、二维码、图片命令可能不完全一致


3.2 Java SE 桌面端蓝牙打印

桌面端要看打印机暴露的是什么能力。

方式 A:如果蓝牙打印机映射成串口

Java 可以直接按串口写数据。

比如用 jSerialComm

javaCopy

        
import com.fazecast.jSerialComm.SerialPort;
import java.nio.charset.Charset;

public class PrinterDemo {
    public static void main(String[] args) {
        SerialPort port = SerialPort.getCommPort("COM5");
        port.setBaudRate(9600);

        if (port.openPort()) {
            byte[] init = new byte[]{0x1B, 0x40};
            port.writeBytes(init, init.length);

            byte[] text = "测试打印\n".getBytes(Charset.forName("GBK"));
            port.writeBytes(text, text.length);

            port.closePort();
        }
    }
}
        
      

方式 B:作为系统打印机

如果系统里已经安装好了打印机驱动,也可以通过 Java Print Service:

javaCopy

        
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
for (PrintService service : services) {
    System.out.println(service.getName());
}
        
      

然后把内容发送给指定打印服务。

但要注意:

  • javax.print 更适合普通文档打印

  • 对小票打印机的精细控制,不如直接发 ESC/POS 灵活


4. 扫码枪:Java 常见做法


4.1 扫码枪是键盘模式(最常见)

这种最简单。

扫码枪扫完码后,相当于键盘输入:

  • 6901234567890

  • 然后自动回车

你只要在输入框监听即可。

Swing 示例

javaCopy

        
JTextField textField = new JTextField();
textField.addActionListener(e -> {
    String code = textField.getText();
    System.out.println("扫描结果: " + code);
    textField.setText("");
});
        
      

JavaFX 示例

javaCopy

        
TextField textField = new TextField();
textField.setOnAction(e -> {
    String code = textField.getText();
    System.out.println("扫描结果: " + code);
    textField.clear();
});
        
      

适用特点

  • 开发成本最低

  • 不需要设备驱动开发

  • 基本所有业务系统都这么接


4.2 串口扫码枪

如果扫码枪输出到 COM 口,就要监听串口数据。

jSerialComm 示例

javaCopy

        
import com.fazecast.jSerialComm.SerialPort;

public class ScannerDemo {
    public static void main(String[] args) {
        SerialPort port = SerialPort.getCommPort("COM3");
        port.setBaudRate(9600);
        port.setNumDataBits(8);
        port.setNumStopBits(1);
        port.setParity(SerialPort.NO_PARITY);

        if (port.openPort()) {
            byte[] buffer = new byte[1024];
            while (true) {
                int len = port.readBytes(buffer, buffer.length);
                if (len > 0) {
                    String data = new String(buffer, 0, len);
                    System.out.println("扫描数据: " + data);
                }
            }
        }
    }
}
        
      

注意点

  • 要处理一条码数据的结束符:

    • \r

    • \n

    • \r\n

  • 有的扫码枪支持配置前缀/后缀

  • 需要做缓冲拼包,不能假设一次 read 就是一整条码


4.3 蓝牙扫码枪

蓝牙扫码枪一般两种模式:

HID 模式

  • 配对后像蓝牙键盘

  • Java 侧不用特殊 SDK

  • 就按输入法/文本框处理

SPP 模式

  • 配对后形成虚拟串口

  • Java 侧按串口读数据


5. 实战里最容易踩的坑

蓝牙打印常见坑

  1. 打印乱码

    • 中文编码不对

    • 打印机字库不支持

  2. 安卓权限不全

    • 尤其 Android 12+

  3. 蓝牙连接不稳定

    • 连接超时、配对失效、socket 被占用

  4. 不同品牌指令不兼容

    • 虽然都说支持 ESC/POS,但细节会有差异

  5. 图片打印慢

    • 要做位图压缩、二值化

扫码枪常见坑

  1. 以为要接 SDK,其实只是键盘输入

  2. 回车/后缀没处理好

  3. 焦点丢失导致扫不到输入框

  4. 串口参数不匹配

    • 波特率、校验位、停止位

  5. 蓝牙扫码枪模式搞错

    • HID 和 SPP 处理方式完全不同


6. 我建议的落地方案

如果你是做收银/仓储/PDA

打印

  • Android:直接蓝牙 socket + ESC/POS/TSPL

  • 桌面端:优先 USB / 网口打印,其次蓝牙串口

    • 因为桌面端蓝牙兼容性通常不如 Android

扫码

  • 优先键盘模式

    • 最稳、最省开发

  • 如果必须拿到底层数据,再走串口模式