USB扫码枪获取数据流的实现方式

 

 

硬件条件:

  1. OTG接口转换器(或者自带usb接口的设备(大头))
  2. USB扫码枪(支持USB虚拟串口)
  3. 安卓设备

实现方式:

  1. 串口方式
  2. USB方式

使用场景:

在扫码枪连接机器的时候,当扫码枪扫描到内容时,获取扫描到的内容。

解决的问题:

之前扫码枪使用的是USB模式(即扫描到的内容会直接输入到EditText之中,并且末尾会增加一个回车键),但是后来发现,当二维码包含中文的时候,中文不会输出进来,于是乎,使用以上两种方式实现。

实现思路:

连接扫码枪,因为数据肯定是以字节流的方式发送的,那么我们只需获取到输入的字节流,自己处理成需要的内容就可以了。

实现方式------串口方式:

    需要经过以下几个步骤:

  • 集成谷歌原生serial_port包,参考该文章集成就可以了: Android串口集成
  • 寻找设备串口地址(这个咱们仅仅是为了获取串口地址,因为串口地址是固定的,所以这里可以不加在程序中,咱们目的是为了知道USB扫码枪的串口地址)
  • 连接设备
  • 获取数据流

步骤一:集成谷歌原生serial_port包,这个自己看上面教程了。

步骤二:寻找设备串口地址,在完成步骤一之后,会有一个SerialPortFinder的类,这个类的作用是寻找咱们安卓设备上连接的串口设备(包括扫码枪),具体获取扫码枪串口地址的思路:获取未插入扫码枪时所有设备的串口地址------》获取插入扫码枪时所有设备的串口地址

-----》看看多出那个,多出的那个就是扫码枪的串口地址了,废话不多说,直接贴代码了:

 case R.id.btn_print://获取所有串口设备地址,mFinder就是SerialPortFinder
                StringBuffer stringBuffer = new StringBuffer();
                for (String str:mFinder.getAllDevicesPath()) {
                    stringBuffer.append(str + "\n");
                }
                mTvMessage.setText(stringBuffer.toString());
                break;

 

步骤三:连接设备:

废不多讲,首先咱们连接到串口设备,如果你连接不上,那么就是步骤一有问题,请到步骤一那篇博客问问作者:

 private String mSerialPath = "/dev/ttyUSB0" ;//物理串口地址,这个就是咱们步骤二找到的地址了,这是我的设备的地址,你的自己找去
    private int baudrate = 9600;//波特率,这个是可以扫码枪自己设置的,看说明书
    private SerialPort mSerialPort;
    private InputStream mInputStream;
 
 
    @Override
    protected int getContentView() {
        return R.layout.activity_test;
    }
 
    @Override
    protected void initView() {
        super.initView();
        initSerialPort();
    }
 
    private void initSerialPort(){//连接串口设备,建议加try,硬件设备你永远不知道会为啥崩溃
        try {
            mSerialPort = new SerialPort(new File(mSerialPath),baudrate);
            mInputStream = mSerialPort.getInputStream();
            ReadThread thread = new ReadThread();//这是读取数据流的线程,代码在下方贴出
            thread.start();//启动数据流读取线程
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

步骤四:获取数据流,这时候步骤三,我们开启了一个线程读取扫码枪的数据流,这时候我写了一个死循环,轮询扫码枪发送的内容,线程代码如下

private InputStream mInputStream;
 
 
/**
     * 读串口线程
     */
    private class ReadThread extends Thread {//这是我的内部类,我代码习惯不好,别瞎鸡儿抄
        @Override
        public void run() {
            super.run();
            while ((!Thread.currentThread().isInterrupted())) {
                final int size;
                try {
                    if (mInputStream == null)
                        return;
                    final byte[] buffer = new byte[512];
                    size = mInputStream.read(buffer);//这就是扫描到的内容了
                    if (size > 0) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    ToastUtils.makeText(TestActivity.this,new String(buffer,"GB2312"));//此处我设置的编码是GB2312,这个可以看说明书,可以设置的
                                } catch (UnsupportedEncodingException e) {
                                    e.printStackTrace();
                                }
                            }
                        });
                    }
                    Thread.sleep(300);//你自己可以设置睡眠时间,这个睡眠时间可以会影响识别速度,那么就可以叫用户加钱,你懂我意思吧。
                } catch (Exception e) {
                    //如果抛出异常则再次设置中断请求
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }
    }

嗯,到了这一步,大功告成了。

注意:串口方式实现的话兼容性不好,因为遇到部分安卓设备直接阉割了这部分功能,所以我才会研究USB方式的。

实现方式------USB方式:

该种方式好处在于不需要集成serial_port包,但是里面一些类理解比较灵性,USB方式需要经过以下几个步骤:

  • 寻找需要连接的devices设备
  • 连接设备
  • 获取数据流

步骤一:寻找需要连接的devices设备,这里的话有两种场景

  1.USB扫码枪一直插在安卓设备上,我们需要在程序启动的时候自动找到并且连接

  2.USB扫码枪在热拔插的情况下,我们需要在插入的时候连接

第一种的话我是直接写了一个方法,在程序启动的时候检查时候插入需要支持的设备类型,如果有,那么直接连接,判断方法是通过设备的pid和vid对比进行判断,pid和vid获取方式自己百度,或者把扫码枪插在电脑上,在设备管理可以看到,我通过findDevices方法找到需要连接的扫码枪设备。

贴代码:

  private UsbManager mManager;
   
 
 //查找已连接的设备
    UsbDevice findDevices() {
        if (null == mManager) {
            return null;
        }
        HashMap<String, UsbDevice> mMap = mManager.getDeviceList();
        for (UsbDevice device : mMap.values()) {
            if (BaseScanner.isS5920(device) || BaseScanner.isS5600(device)) {//判断设备是否支持的类型
                return device;
            }
        }
        return null;
    }

这是baseScanner,这个类名我觉得不够准确,它是用来记录需要支持的设备的信息的,包含设备的判断方法:

public class BaseScanner{
    private static final int scanner5600_pid = 4456;
    private static final int scanner5600_vid = 866;
    private static final int scanner5920_pid = 9527;
    private static final int scanner5920_vid = 549;
 
    public static boolean isS5600(UsbDevice usbDevice){//是不是s5600扫码枪
        return usbDevice.getProductId() == scanner5600_pid && usbDevice.getVendorId() == scanner5600_vid;
    }
 
    public static boolean isS5920(UsbDevice usbDevice){//是不是s5920
        return usbDevice.getProductId() == scanner5920_pid && usbDevice.getVendorId() == scanner5920_vid;
    }
}

第二种USB扫码枪热拔插连接方式,这时候我们采用的是广播监听的方式,因为设备拨叉都会有广播发送,这条广播包含的信息包含一个USBDevices对象(也就是扫码枪),我们需要动态注册拔,插两条广播,静态注册不生效,代码如下:

//这是广播类,因为我的扫描是写在一个service中,所以把连接的设备交给service处理就可以
public class USBBroadcastReceiver extends BroadcastReceiver{
 
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        String action = intent.getAction();
        if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
            UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            if (device != null) {
                // call your method that cleans up and closes communication with the device
                Toast.makeText(context, "拔出", Toast.LENGTH_LONG).show();
            }
        }else if(UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)){//设备插入
            UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);//获取插入的设备
            Intent intent1 = new Intent(context,ScannerService.class);
            Bundle bundle = new Bundle();
            bundle.putParcelable("device",device);
            intent1.putExtra("data",bundle);//将设备包入intent中,交给service处理
            context.startService(intent1);
            Toast.makeText(context, "插入", Toast.LENGTH_LONG).show();
        }
    }
 
}

 

我是在程序启动的时候就注册了这个广播如下:

public class MyApplication extends Application {
 
    private USBBroadcastReceiver mUsbReceiver;
 
    @Override
    public void onCreate() {
        super.onCreate();
        registBroadCast();
    }
 
    @Override
    public void onTerminate() {
        unregisterReceiver(mUsbReceiver);
        super.onTerminate();
    }
 
    private void registBroadCast(){//注册USB插拔广播
        mUsbReceiver = new USBBroadcastReceiver();
        IntentFilter usbDeviceStateFilter = new IntentFilter();
 
        usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);//插入设备状态
        usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);//拔出设备状态
        registerReceiver(mUsbReceiver, usbDeviceStateFilter);
    }
 
}

 

步骤二,连接设备,这边我把所有都写注释里面:

private UsbDevice mDevide;
    private UsbDeviceConnection mConnect;
    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
    private static UsbConnectManager mInstance;
    private ScannerListener mListener;
    private UsbManager mManager;
    private UsbEndpoint mUsbEndpointIn;
    private UsbInterface mUsbInterface;
    private Thread mReadingthread;
 
 
 public void connetDevice(UsbDevice device, Context context) {
        disConnect();//断开之前的连接
        mDevide = device;//当前需要连接的设备
        try {
            if (!mManager.hasPermission(mDevide)) {//检查是否有扫码枪的访问权限,如果没有,那么请求一下临时权限
                PendingIntent intent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
                mManager.requestPermission(mDevide, intent);
            }
            mConnect = mManager.openDevice(mDevide);//打开设备
            mUsbInterface = device.getInterface(0);//获取数据的接口.这个可以理解成设备有多少个线头子
            mUsbEndpointIn = mUsbInterface.getEndpoint(0);//获取设备的输出流向,输入或者输出因为usb扫码枪只有一个输出项,所以直接选择0
            if (mConnect.claimInterface(mUsbInterface, true)) {//获取设备数据写入流,如果获取成功,那么数据已经通常,我这边写的是一个获取数据,输入数据的话把getEndpoint(0)换成输入的接口即可
                Log.i(TAG, "connetDevice: find device,start get data!!!");
                startReading();//开启读取数据流的线程
            } else {
                mConnect.close();
                Log.i(TAG, "connetDevice: not find device !!!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

步骤三,获取输入的流,并解析成需要的内容,代码如下:

OK啦,USB模式的也完成了。

有疑问或者有建议的膀友可以加群:497438697  我是群里的 杭州-大魔王,有艾特必应。

祝大家新年快乐,万事亨通!!!

    //开线程读取数据
    private void startReading() {
        if (null != mReadingthread) {
            mReadingthread.interrupt();
        }
        mReadingthread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!mReadingthread.isInterrupted()) {
                    synchronized (this) {
                        byte[] bytes = new byte[128];
                        int ret = mConnect.bulkTransfer(mUsbEndpointIn, bytes, bytes.length, 100);//获取数据流
                        if (ret > 0) {
                            try {
                                byte[] bs = new byte[ret];
                                System.arraycopy(bytes, 0, bs, 0, ret);//获取数据长度
                                String str = new String(bs,"GB2312");//转换成汉字
                                if (null != mListener){
                                    mListener.onScanner(str);//你自己需要的处理方式
                                }
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        });
        mReadingthread.start();
    }
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值