You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android USB OTG连接Zebra打印机:bulkTransfer返回值问题求助

USB OTG连接Zebra打印机的Android实现

最近我尝试在Android设备上通过USB OTG连接Zebra打印机,根据官方文档,UsbDeviceConnection#bulkTransfer的返回值规则是:成功时返回传输的数据长度(非负,包括0),失败时返回负值。下面是我完整的实现代码,包含设备枚举、权限申请、打印逻辑等核心部分:

package in.silentsudo.andoidprinter.usbprinter;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;

import in.silentsudo.andoidprinter.R;

public class MainActivity extends Activity {
    private UsbManager mUsbManager;
    private UsbDevice mUsbDevice;
    private PendingIntent mPermissionIntent;
    private UsbDeviceConnection usbConnection;
    UsbEndpoint usbWriterEndpoint;
    UsbInterface usbInterface;

    private static final String ACTION_USB_PERMISSION = "in.silentsudo.usbprinter.USB_PERMISSION";
    private StringBuffer logBuffer = new StringBuffer();
    private TextView textView;
    private Button button;

    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.txt_status);
        button = findViewById(R.id.btn_print);

        addLog("Creating usbConnection");
        mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        String usbManagerString = mUsbManager == null ? "USBManager is null" : "USB manager is valid";
        addLog(usbManagerString);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        registerReceiver(mUsbReceiver, filter);
        addLog("Registered received with filter :" + ACTION_USB_PERMISSION);

        if (mUsbManager != null) {
            if (mUsbManager.getDeviceList() != null && mUsbManager.getDeviceList().size() > 0) {
                addLog("Total usb devices: " + mUsbManager.getDeviceList().size());
                for (Map.Entry<String, UsbDevice> entry : mUsbManager.getDeviceList().entrySet()) {
                    addLog("    " + entry.getKey() + " -> " + entry.getValue().getSerialNumber());
                    mUsbDevice = entry.getValue();
                    // 申请设备权限
                    mUsbManager.requestPermission(mUsbDevice, mPermissionIntent);
                }
            } else {
                addLog("No device connected");
            }
        }

        addLog("Waiting for usbConnection");
        addLog("Setting up interfaces");
        setUpInterfaces();
    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mUsbReceiver);
        if (usbConnection != null) {
            usbConnection.releaseInterface(usbInterface);
            usbConnection.close();
        }
    }

    public void setUpInterfaces() {
        if (mUsbDevice != null) {
            int interfaceCount = mUsbDevice.getInterfaceCount();
            addLog(mUsbDevice.getSerialNumber() + " has total " + interfaceCount + " Interface count");
            for (int i = 0; i < interfaceCount; i++) {
                final UsbInterface usbInterface = mUsbDevice.getInterface(i);
                addLog("        usb at index " + i + " " + getInterfaceDataAsString(usbInterface));
                final int endpointCount = usbInterface.getEndpointCount();
                addLog("             " + usbInterface.getId() + " has total " + endpointCount + " endpoints");

                // 遍历所有端点,找到输出类型的端点(更通用的写法)
                for(int j=0; j<endpointCount; j++){
                    UsbEndpoint endpoint = usbInterface.getEndpoint(j);
                    printUsbEndPointAsString(endpoint);
                    // 判断是否为输出端点(用于写入数据到打印机)
                    if(endpoint.getDirection() == UsbEndpoint.DIRECTION_OUT){
                        usbWriterEndpoint = endpoint;
                        this.usbInterface = usbInterface;
                        addLog("Selected output endpoint: " + endpoint.getAddress());
                        break;
                    }
                }
            }
        }
    }

    final String THREAD_TAG = "BG THREAD:-> ";

    public boolean connectAndClaim() {
        if(mUsbDevice == null || usbInterface == null) return false;
        usbConnection = mUsbManager.openDevice(mUsbDevice);
        if (usbConnection != null) {
            Log.e("Connection:", " connected");
            addLog("Device connected");
            // 声明接口,获取访问权限
            boolean claimed = usbConnection.claimInterface(usbInterface, true);
            addLog("Interface claimed: " + claimed);
            return claimed;
        }
        addLog("Failed to open device connection");
        return false;
    }

    public void writeToPrinter(final String contentToBeWritten) {
        // 先确保连接并声明接口成功
        if(!connectAndClaim()){
            addLog("Failed to connect or claim interface, cannot print");
            return;
        }

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    addLog(THREAD_TAG + "Writing to usbConnection");
                    addLog(THREAD_TAG + " Content to be written `" + contentToBeWritten + "`");
                    addLog(THREAD_TAG + " Content size`" + contentToBeWritten.length() + "`");

                    byte[] bytes = contentToBeWritten.getBytes();
                    int transferResult = usbConnection.bulkTransfer(usbWriterEndpoint, bytes, bytes.length, 50000);
                    if(transferResult >=0){
                        addLog(THREAD_TAG + "Successfully transferred " + transferResult + " bytes");
                    } else {
                        addLog(THREAD_TAG + "Transfer failed, error code: " + transferResult);
                    }
                    addLog(THREAD_TAG + "Written to usbConnection");

                } catch (Exception e) {
                    StringWriter sw = new StringWriter();
                    e.printStackTrace(new PrintWriter(sw));
                    String exceptionAsString = sw.toString();
                    addLog(THREAD_TAG + exceptionAsString);
                } finally {
                    // 打印完成后释放资源
                    if(usbConnection != null){
                        usbConnection.releaseInterface(usbInterface);
                        usbConnection.close();
                    }
                }
            }
        });
        thread.start();
    }

    private void printUsbEndPointAsString(UsbEndpoint point) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(point.toString());
        stringBuilder.append(", type: ").append(getEndpointType(point.getType()));
        stringBuilder.append(", direction: ").append(point.getDirection() == UsbEndpoint.DIRECTION_OUT ? "OUT" : "IN");
        stringBuilder.append(", address: ").append(point.getAddress());
        addLog("            Endpoint info: " + stringBuilder.toString());
    }

    private String getEndpointType(int type){
        switch (type){
            case UsbEndpoint.TYPE_BULK: return "BULK";
            case UsbEndpoint.TYPE_CONTROL: return "CONTROL";
            case UsbEndpoint.TYPE_INTERRUPT: return "INTERRUPT";
            default: return "UNKNOWN";
        }
    }

    private String getInterfaceDataAsString(UsbInterface usbInterface) {
        return "ID: " + usbInterface.getId() + ", Class: " + usbInterface.getInterfaceClass();
    }

    private void addLog(String log) {
        logBuffer.append(log).append("\n");
        // 确保在主线程更新UI
        textView.post(() -> textView.setText(logBuffer.toString()));
        Log.d("USB_PRINTER", log);
    }

    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        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 (device != null) {
                        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                            addLog("Permission granted for device: " + device.getSerialNumber());
                            mUsbDevice = device;
                            setUpInterfaces();
                        } else {
                            addLog("Permission denied for device: " + device.getSerialNumber());
                        }
                    }
                }
            }
        }
    };
}

关键实现优化说明:

  • 权限申请逻辑补充:在枚举到设备后主动调用requestPermission申请访问权限,配合广播接收器处理权限结果,确保能合法访问打印机。
  • 端点选择优化:不再固定选择index=1的端点,而是遍历所有端点找到**输出类型(DIRECTION_OUT)**的端点,适配更多型号的Zebra打印机。
  • 资源管理完善:在打印完成后主动关闭连接、释放接口,避免资源泄漏;在onPause中也完善了资源释放逻辑。
  • 结果判断清晰化:对bulkTransfer的返回值做了明确的成功/失败判断,方便排查传输问题。
  • 日志输出增强:补充了端点类型、方向等关键信息的日志,便于调试时确认设备接口是否匹配。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:08:21