美文网首页
USB打印机开发记录

USB打印机开发记录

作者: _非_阳_ | 来源:发表于2019-07-18 15:24 被阅读0次

    最近公司的餐厅点餐app又提了新需求,增加对usb打印机的支持。

    USB开发,涉及到主从模式

    • 主机模式
      Android设备充当主主设备,并为总线供电。
      例如数字相机,键盘,鼠标和游戏控制器。USB设备与Android应用进行数据交互。

    • 附件模式(从属)
      外部硬件充当USB主设备,并为总线供电。例如手机和电脑连接

    主从模式图

    做usb通信,首先要先弄清楚哪边是HOST那边是SLAVE。
    比如你的android手机做host,要获得slave,用UsbDevice表示slave
    要是你的android手机做slave,要获得host,用UsbAccessory表示host

    我们的项目是主模式开发,也就是说USB打印机连接到我们的点餐设备上,点餐设备充当USB主设备,并为总线供电。

    首先你可以在manifest.xml清单文件中,声明usb权限是否为必须。
    如果不声明,默认为false。

    如果required=true,则在安装app的同时,如果android设备不支持usb功能,则app是无法安装的。

    同时,做声明的好处是,如果你的app受众人群在国外,那么googleplay会帮你过滤掉没有usb功能的设备,也就是说没有usb功能的设备时搜索不到或者无法下载你的app的!

     <uses-feature
            android:name="android.hardware.usb.host"
            android:required="true" />
    

    声明插入或拔出usb设备时,打开指定的activity.

     <activity
                android:name="com.mjt.print.PrinterConnectDialog"
                android:configChanges="keyboardHidden|orientation|screenSize"
                android:label="@string/app_name"
                android:theme="@android:style/Theme.Light" >
    
                <intent-filter>
                    <!--host模式开发时,在设备插入/拔出时启动该activity的action-->
                    <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
                    <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
    
                    <!--accessory模式开发时,在设备插入/拔出时启动该activity的action-->
    
                    <!--<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />-->
                    <!--<action android:name="android.hardware.usb.action.USB_ACCESSORY_DETTACHED" />-->
                </intent-filter>
    
                <!--过滤usb设备,在device_file.xml文件中声明的设备,-->
                <meta-data
                    android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                    android:resource="@xml/device_filter" />
               
    
      </activity>
    

    声明要过滤的usb设备的相关信息

    vender-id为生产厂家号 product-id为产品号

    其中vendor-id和product-id为插入USB设备的生产厂家号和产品号,在 插入(attached)上面列出的设备之一时,就会弹出选择打开应用程序的对话框。

    <resources>
        <!-- Accept all device VID/PID combinations -->
        <usb-device vendor-id="22339" product-id="1155" />
    </resources>
    

    usb开发的相关api在android.hardware.usb包下


    image.png
    说明
    UsbManager 获得 USB 管理器,与连接的 USB 设备通信。
    UsbDevice host模式下,USB 设备的抽象,每个UsbDevice 都代表一个 USB 设备。
    UsbAccessory (从属模式下,也就是你的android设备相当于一个附件挂在usb设备上)此时的UsbAccessory代表的就是android设备所挂在的那个设备
    UsbInterface 表示一个UsbDevice的一个接口。UsbDevice可以具有一个或多个在接口用来通信。
    UsbEndpoint 表示一个UsbInterface一个端点,它是接口的通信通道。一个接口可以具有一个或多个端点,与设备进行双向通信通常有一个端点用于输入和一个端点用于输出。
    UsbDeviceConnection 表示与设备的连接,用来收发数据,传输控制信息。
    UsbRequest 通过UsbDeviceConnection与设备通信的异步请求,只用来异步通信
    UsbConstants USB常量定义,与Linux内核的linux / usb / ch9.h中的定义相对应

    UsbPort

    package com.mjt.factory.print.engine;
    
    import android.app.PendingIntent;
    import android.content.Context;
    import android.content.Intent;
    import android.hardware.usb.UsbConstants;
    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.util.Log;
    
    import com.gprinter.io.PortManager;
    import com.gprinter.utils.Utils;
    import com.mjt.common.base.CommonLib;
    
    import java.io.IOException;
    import java.util.Vector;
    
    import static com.mjt.common.constants.Constants.ACTION_USB_PERMISSION;
    
    /**
     * Copyright:mjt_pad_android
     * Author: liyang <br>
     * Date:2019-05-28 14:07<br>
     * Desc: <br>
     */
    public class UsbPort extends PortManager {
        private static final String TAG = UsbPort.class.getSimpleName();
    
    
        private UsbDevice usbDevice;
    
        private UsbManager usbManager;
    
        private UsbDeviceConnection connection;
    
        private UsbInterface interf;
    
        private UsbEndpoint epIn, epOut;
    
        public static final int USB_REQUEST_CODE = 0;
    
        public UsbPort(UsbDevice device) {
            usbDevice = device;
            usbManager = (UsbManager) CommonLib.getContext().getSystemService(Context.USB_SERVICE);
        }
    
        public boolean openPort() {
            if (this.usbDevice != null) {
                if (!this.usbManager.hasPermission(this.usbDevice)) {
                    PendingIntent intent = PendingIntent.getBroadcast(CommonLib.getContext(), USB_REQUEST_CODE, new Intent(ACTION_USB_PERMISSION), 0);
                    usbManager.requestPermission(usbDevice, intent);
                    return false;
                } else {
                    openUsbPort();
                    return  epOut != null;
                }
            }
            return false;
        }
    
        private void openUsbPort() {
            int          count        = usbDevice.getInterfaceCount();
            UsbInterface usbInterface = null;
    
            if (count > 0) {
                usbInterface = usbDevice.getInterface(0);
            }
    
            if (usbInterface != null) {
                interf = usbInterface;
                connection = null;
                connection = usbManager.openDevice(usbDevice);
            }
            if (connection != null && connection.claimInterface(interf, true)) {
                for (int i = 0; i < interf.getEndpointCount(); ++i) {
                    UsbEndpoint ep = interf.getEndpoint(i);
                    if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                        if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {
                            this.epOut = ep;
                        } else {
                            this.epIn = ep;
                        }
                    }
                }
            }
    
        }
    
        public void writeDataImmediately(Vector<Byte> data) throws IOException {
            this.writeDataImmediately(data, 0, data.size());
        }
    
    
        public void writeDataImmediately(Vector<Byte> data, int offset, int len) throws IOException {
            int          result   = 0;
            Vector<Byte> sendData = new Vector<>();
    
            for (int i = 0; i < data.size(); ++i) {
                if (sendData.size() >= 1024) {
                    Log.e(TAG, "i = " + i + "\tsendData size -> " + sendData.size() + "\tdata size -> " + data.size());
                    result += this.connection.bulkTransfer(this.epOut, Utils.convertVectorByteTobytes(sendData), sendData.size(), 1000);
                    sendData.clear();
                    Log.e(TAG, "sendData.clear() size -> " + sendData.size());
                }
    
                sendData.add(data.get(i));
            }
    
            if (sendData.size() > 0) {
                Log.e(TAG, "sendData size -> " + sendData.size());
                result += this.connection.bulkTransfer(this.epOut, Utils.convertVectorByteTobytes(sendData), sendData.size(), 1000);
            }
    
            if (result == data.size()) {
                Log.d(TAG, "send success");
            }else {
                throw new IOException("send failed");
            }
        }
    
        public int readData(byte[] bytes) throws IOException {
            return this.connection != null&&epIn!=null ? this.connection.bulkTransfer(this.epIn, bytes, bytes.length, 200) : 0;
        }
    
        public boolean closePort() {
            if (this.interf != null && this.connection != null) {
                this.connection.releaseInterface(this.interf);
                this.connection.close();
                this.connection = null;
                return true;
            } else {
                return false;
            }
        }
    
        public UsbDevice getUsbDevice() {
            return this.usbDevice;
        }
    
        public void setUsbDevice(UsbDevice usbDevice) {
            this.usbDevice = usbDevice;
        }
    }
    
    

    在MainActivity中注册Usb广播

       private final BroadcastReceiver usbDeviceReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                Log.d("action", action);
                UsbDevice mUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (ACTION_USB_PERMISSION.equals(action)) {
                    synchronized (this) {
                        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) && mUsbDevice != null) {
                            Log.d("receiver", action + ",device:" + mUsbDevice.getDeviceName() + "被授予了权限!");
                        } else {
                            UIUtils.showToast(context, "USB设备请求被拒绝");
                        }
                    }
                } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                    if (mUsbDevice != null) {
                        UIUtils.showToast(context, "有设备拔出");
                        EventBus.getDefault().post(new UsbAttachedChangedEvent(mUsbDevice, false));
                    }
                } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
                    if (mUsbDevice != null) {
                        UIUtils.showToast(context, "有设备插入");
                        EventBus.getDefault().post(new UsbAttachedChangedEvent(mUsbDevice, true));
                    }
                }
            }
        };
    

    DeviceConnManager类的openPort方法

      /**
         * 打开端口
         *
         * @return
         */
        public void openPort(boolean needNotify) {
            this.needNotify = needNotify;
            isOpenPort = false;
            sendStateBroadcast(CONN_STATE_CONNECTING);
            switch (connMethod) {
                case BLUETOOTH:
                    System.out.println("id -> " + id);
                    mPort = new BluetoothPort(macAddress);
                    isOpenPort = mPort.openPort();
    
                    break;
                case USB:
                    mPort = new UsbPort(mUsbDevice);
                    isOpenPort = mPort.openPort();
    
                    break;
                case WIFI:
                    mPort = new EthernetPort(ip, port);
                    isOpenPort = mPort.openPort();
                    break;
                case SERIAL_PORT:
                    mPort = new SerialPort(serialPortPath, baudrate, 0);
                    isOpenPort = mPort.openPort();
                    break;
                default:
                    break;
            }
            //端口打开成功后,检查连接打印机所使用的打印机指令ESC、TSC
            Log.e(TAG, "openPort :" + isOpenPort + ",thread is " + Thread.currentThread().getName());
            if (!isOpenPort) {
                if (getConnMethod() == CONN_METHOD.USB && mUsbDevice != null) {
                    mUsbDevice = null;
                }
                mPort.closePort();
                sendStateBroadcast(CONN_STATE_FAILED);
            } else {
                sendStateBroadcast(CONN_STATE_CONNECTED);
            }
        }
    

    PrinterManager类中的addUsbPrinter方法和connectUsb方法

      public void addUsbPrinter(String id, String name) {
            DeviceConnFactoryManager usbManager = getManager(id);
            UsbManager               manager    = (UsbManager) CommonLib.getContext().getSystemService(Context.USB_SERVICE);
            UsbDevice                usbDevice  = null;
            for (UsbDevice usb : manager.getDeviceList().values()) {
                String pidVid = String.format("%s%s", usb.getProductId(), usb.getVendorId());
                if (usb.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_PRINTER && pidVid.equals(id)) {
                    usbDevice = usb;
                }
            }
            if (usbManager == null) {
                if (usbDevice != null) {
                    new DeviceConnFactoryManager.Build().setId(id)
                                                        .setConnMethod(DeviceConnFactoryManager.CONN_METHOD.USB)
                                                        .setName(name)
                                                        .setUsbDevice(usbDevice)
                                                        .build();
                }
    
            } else {
                //由于usb设备每次查吧usbdevice的deviceId都会变,所以需要重新设备UsbDevice
                if (usbDevice != null && usbManager.usbDevice() == null) {
                    usbManager.setUsbDevice(usbDevice);
                }
            }
        }
    
    
       public void connectUsb(String pidVid, String name, boolean needNotify) {
            DeviceConnFactoryManager deviceManager = getManager(pidVid);
            UsbManager               manager       = (UsbManager) CommonLib.getContext().getSystemService(Context.USB_SERVICE);
            UsbDevice                usbDevice     = null;
            for (UsbDevice usb : manager.getDeviceList().values()) {
                String pv = String.format("%s%s", usb.getProductId(), usb.getVendorId());
                if (usb.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_PRINTER && pidVid.equals(pv)) {
                    usbDevice = usb;
                }
            }
            if (deviceManager == null) {
                if (usbDevice != null) {
                    new DeviceConnFactoryManager.Build().setId(pidVid)
                                                        .setConnMethod(DeviceConnFactoryManager.CONN_METHOD.USB)
                                                        .setUsbDevice(usbDevice)
                                                        .setName(name)
                                                        .build();
                } else {
                    Log.e(TAG, "connectUsb: usbDevice is null");
                }
    
            } else {
                if (usbDevice != null) {
                    deviceManager.setUsbDevice(usbDevice);
                } else {
                    Log.e(TAG, "connectUsb: usbDevice is null");
                }
    
            }
            if (getManager(pidVid)!=null){
                getManager(pidVid).openPort(needNotify);
            }else {
                UIUtils.showToast("请确保已插入该USB打印机!");
                Log.e(TAG, String.format("connectUsb: getManager(%s)",pidVid));
            }
    
        }
    

    相关文章

      网友评论

          本文标题:USB打印机开发记录

          本文链接:https://www.haomeiwen.com/subject/yrsklctx.html