You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何将Android应用连接至打印机?USB端口打印机应用开发咨询

Android 应用连接外部打印机的实现方法

嘿,我之前开发打印类应用时也遇到过这个问题——Android官方文档确实在「实际设备连接」这块讲得比较零散,没给太直接的步骤。我来给你梳理下USB和常见蓝牙打印机的具体实现方法,都是经过实际验证的:

USB打印机连接实现

1. 先配置权限和过滤规则

首先要在AndroidManifest.xml里声明USB相关权限,同时注册广播接收器监听设备连接:

<!-- 声明USB Host功能(非强制,根据设备情况) -->
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
<!-- USB权限申请 -->
<uses-permission android:name="android.permission.USB_PERMISSION" />

<application ...>
    <!-- 监听USB设备插入的广播接收器 -->
    <receiver android:name=".UsbPrinterReceiver">
        <intent-filter>
            <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
        </intent-filter>
        <!-- 关联设备过滤文件 -->
        <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
            android:resource="@xml/usb_device_filter" />
    </receiver>
</application>

然后在res/xml目录下创建usb_device_filter.xml,用来指定你要连接的打印机(可以通过VID/PID精准匹配,或者留空匹配所有USB设备):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 替换成你的打印机的VID和PID,可通过adb命令或设备手册获取 -->
    <usb-device vendor-id="1234" product-id="5678" />
    <!-- 如果暂时不知道VID/PID,也可以用下面的通用匹配 -->
    <!-- <usb-device /> -->
</resources>

2. 检测设备并请求权限

在你的Activity里,通过UsbManager枚举已连接的USB设备,识别出打印机后向用户申请权限:

private static final String ACTION_USB_PERMISSION = "com.your.package.USB_PERMISSION";

private void scanUsbPrinters() {
    UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
    HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
    
    for (UsbDevice device : deviceList.values()) {
        // 这里可以通过设备名称、VID/PID判断是否是目标打印机
        if (isTargetPrinter(device)) {
            // 构造权限请求的PendingIntent
            PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, 
                new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
            usbManager.requestPermission(device, permissionIntent);
            break;
        }
    }
}

// 自定义判断逻辑,比如根据设备名称或VID/PID
private boolean isTargetPrinter(UsbDevice device) {
    return device.getVendorId() == 1234 && device.getProductId() == 5678;
}

然后创建广播接收器UsbPrinterReceiver处理权限结果:

public class UsbPrinterReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (ACTION_USB_PERMISSION.equals(action)) {
            synchronized (this) {
                UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if(device != null){
                        // 权限获取成功,开始连接打印机
                        ((YourActivity)context).connectToUsbPrinter(device);
                    }
                } else {
                    Log.d("UsbPrinter", "用户拒绝了USB权限");
                }
            }
        }
    }
}

3. 连接设备并发送打印数据

拿到权限后,就可以通过UsbDeviceConnection建立连接,发送打印指令(注意:不同打印机的指令集不同,比如热敏打印机常用ESC/POS指令):

private void connectToUsbPrinter(UsbDevice device) {
    // 通常打印机的第一个接口是打印相关的
    UsbInterface usbInterface = device.getInterface(0);
    // 获取输出端点(Bulk类型)
    UsbEndpoint endpoint = usbInterface.getEndpoint(0);
    
    UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
    UsbDeviceConnection connection = usbManager.openDevice(device);
    
    if (connection != null && connection.claimInterface(usbInterface, true)) {
        // 构造打印数据,这里以ESC/POS指令为例:打印文字+换行+切纸
        byte[] printData = ("Hello, USB Printer!\n").getBytes();
        // 添加切纸指令(ESC/POS:0x1B 0x69)
        byte[] cutCommand = {0x1B, 0x69};
        byte[] fullData = ByteBuffer.allocate(printData.length + cutCommand.length)
            .put(printData)
            .put(cutCommand)
            .array();
        
        // 发送数据
        int bytesWritten = connection.bulkTransfer(endpoint, fullData, fullData.length, 5000);
        if (bytesWritten > 0) {
            Toast.makeText(this, "打印成功", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "打印失败", Toast.LENGTH_SHORT).show();
        }
        
        // 用完记得释放资源
        connection.releaseInterface(usbInterface);
        connection.close();
    } else {
        Toast.makeText(this, "无法连接打印机", Toast.LENGTH_SHORT).show();
    }
}

蓝牙打印机连接实现

如果你的打印机是蓝牙连接的,流程类似但用蓝牙API:

1. 配置蓝牙权限

AndroidManifest.xml里声明蓝牙权限(注意适配Android 12+的新权限):

<!-- 基础蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Android 12+ 需要的连接权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" android:maxSdkVersion="33" />
<!-- 蓝牙搜索权限(Android 12+ 需单独申请) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" android:maxSdkVersion="33" />

2. 搜索并配对蓝牙打印机

先开启蓝牙,然后搜索设备,找到目标打印机后配对(如果未配对):

private static final int REQUEST_ENABLE_BT = 1;
private BluetoothAdapter bluetoothAdapter;

private void initBluetooth() {
    bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (bluetoothAdapter == null) {
        Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();
        return;
    }
    // 开启蓝牙
    if (!bluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    } else {
        startScanPrinters();
    }
}

private void startScanPrinters() {
    // 注册广播接收器监听搜索结果
    BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // 通过设备名称识别打印机
                if (device.getName() != null && device.getName().contains("Printer")) {
                    if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                        // 未配对,发起配对请求
                        try {
                            Method method = device.getClass().getMethod("createBond");
                            method.invoke(device);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else {
                        // 已配对,直接连接
                        connectToBluetoothPrinter(device);
                    }
                }
            }
        }
    };
    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
    registerReceiver(bluetoothReceiver, filter);
    
    // 开始搜索蓝牙设备
    bluetoothAdapter.startDiscovery();
}

3. 建立连接并打印

蓝牙打印机通常使用RFCOMM串口服务,用通用的串口UUID(00001101-0000-1000-8000-00805F9B34FB)连接:

private void connectToBluetoothPrinter(BluetoothDevice device) {
    // 注意:必须在子线程执行,避免ANR
    new Thread(() -> {
        try {
            // 获取RFCOMM套接字
            BluetoothSocket socket = device.createRfcommSocketToServiceRecord(
                UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
            socket.connect();
            
            OutputStream outputStream = socket.getOutputStream();
            // 发送打印数据(同样用ESC/POS指令)
            byte[] printData = ("Hello, Bluetooth Printer!\n").getBytes();
            byte[] cutCommand = {0x1B, 0x69};
            outputStream.write(printData);
            outputStream.write(cutCommand);
            outputStream.flush();
            
            // 关闭资源
            outputStream.close();
            socket.close();
            
            // 回到主线程提示结果
            runOnUiThread(() -> Toast.makeText(this, "打印成功", Toast.LENGTH_SHORT).show());
        } catch (IOException e) {
            e.printStackTrace();
            runOnUiThread(() -> Toast.makeText(this, "连接打印机失败", Toast.LENGTH_SHORT).show());
        }
    }).start();
}

关键注意事项

  • 指令集适配:不同品牌的打印机指令集可能不同,一定要参考打印机的官方手册,比如ESC/POS是热敏打印机的通用指令集,包含换行、加粗、切纸等命令。
  • 线程处理:所有设备连接、数据发送的操作都必须放在子线程中,绝对不能在主线程执行,否则会触发ANR。
  • 版本适配:不同Android版本的权限要求差异很大,比如Android 10+的USB权限弹窗逻辑,Android 12+的蓝牙分区权限,一定要做好适配。

内容的提问来源于stack exchange,提问作者Vadim Has

火山引擎 最新活动