Android Usb简单入门和使用

之前在公司做了个项目,是对接第三方厂家智能黑板实现一个同步的效果,当时这款黑板版本太老了,找厂家要协议都没有,心里mmp~,后来通过抓包去一点一点的搞才把这个功能搞好,里边主要用到usb这块技术,我这边简单的归纳一下。

一、基础知识

在使用Android Usb之前,我们需要了解一些基本概念和定义:

  • USB(Universal Serial Bus):通用串行总线,是一种用于连接计算机和外部设备的标准接口。
  • USB 主机(Host):连接到计算机的设备,能够控制其他连接到它的设备。
  • USB 设备(Device):连接到主机的外部设备,例如闪存驱动器、鼠标、键盘等。
  • USB 接口(Interface):定义了设备和主机之间的通信协议和数据格式。
  • USB 端点(Endpoint):一个端点代表设备的一个传输方向(输入或输出),每个端点都有一个唯一的地址。

以下是USB数据传输模式的几种类型:

  • Control(控制传输):提供无损传输,用于传输配置、状态和命令等通信。所有的USB设备都必须支持这种传输模式。
  • Interrupt(中断传输):保证有限延迟下设备的快速响应,主要用于键盘等输入设备。
  • Bulk(批量传输):用于大量数据的无损传输,但不保证带宽和延迟,比如用U盘拷贝文件。
  • Isochronous(同步传输):保证尽可能快的传输速度,但可能会有数据丢失,比如实时的音视频传输。
  • SuperSpeed Asynchronous Transfer(超级速度异步传输):USB 3.0标准引入的一种传输模式,可以在没有任何流控条件下进行大量数据的高速传输,适用于需要高带宽、高吞吐量和低延迟的应用场景。
  • Multi-Point Transfers(多点传输):用于多个设备通过一个USB主机进行数据通信时。
  • Synchronous Transfers(同步传输):可以精确地控制数据的传输时序,适用于对传输精度有严格要求的应用场景。

二、使用步骤

配置AndroidManifest.xml文件
<uses-feature android:name="android.hardware.usb.host" />
<uses-permission android:name="android.hardware.usb.accessory" />

当我们连接一个USB设备到Android设备上时,该USB设备可以被当做两种角色中的一种:USB主机或USB配件。USB主机是指具有控制权的设备,它可以向其他设备发出命令并控制数据传输。USB配件则是指响应USB主机的请求并进行相应操作的设备。

<uses-feature android:name="android.hardware.usb.host" /> 表示应用程序需要访问作为USB主机的设备。这意味着应用程序需要通过USB接口来控制连接到其上的其他USB设备,就像电脑上连接U盘一样。比如,一个打印机驱动的应用程序需要使用USB主机模式才能连接到打印机,并发送打印指令。

<uses-permission android:name="android.hardware.usb.accessory" /> 则表示应用程序需要访问USB配件设备。这意味着应用程序需要与其它设备通信以实现某种功能。例如,一个测量仪器的应用程序需要连接到一个USB配件设备才能获取和分析测试结果。

简单来说,USB主机是控制器,可以控制其它USB设备的行为,而USB配件则是响应者,执行USB主机的命令。当应用程序需要连接到一个特定的USB设备(不管是主机还是配件),它需要声明对应的权限和特性,以便获得访问该设备的授权。

<activity  >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <!--host模式下通知的过滤-->
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
    </intent-filter>
    <!--host模式下指定过滤设备的配置文件-->
    <meta-data
        android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
        android:resource="@xml/device_filter" />
    <!--accessory模式下通知的过滤-->
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
    </intent-filter>
    <!--accessory模式下指定过滤设备的配置文件-->
    <meta-data
        android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
        android:resource="@xml/device_filter" />
</activity>

----------------device_filter.xml文件----------------
<?xml version="1.0" encoding="utf-8"?>
<resources>
	<!--host模式-->
    <usb-device vendor-id="xxx" product-id="xxx" />
</resources>

如果想要在插入usb设备时,能够收到android系统的提示通知,则需要在AndroidManifest中配置相关信息,同时创建res/xml/device_filter.xml文件,填写usb设备的信息,这里边有多种信息可填,可以根据实际需求去填写,我这里就不再多说。

广播监听USB插拔

要在Android设备中配置插入USB设备的通知,需要在AndroidManifest.xml文件中添加以下权限和intent过滤器:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.MEDIA_MOUNTED" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- MyBroadcastReceiver是自己定义的广播接收器类名。 -->
<receiver android:name=".MyBroadcastReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_MOUNTED" />
        <action android:name="android.intent.action.MEDIA_EJECT" />
        <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
        <data android:scheme="file" />
    </intent-filter>
</receiver>

上面的方式也可以用以下方式:

public void initUsbBroadcast(Context context){
        PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
        //注册USB设备权限管理广播
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        mContext.registerReceiver(mMyBroadcastReceiver, filter);
    }

创建一个继承BroadcastReceiver的类,并覆盖onReceive方法,在该方法中处理USB设备插入、拔出和卸载等事件并发出通知:

public class MyBroadcastReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
            // USB设备已插入
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "channel_id")
                    .setSmallIcon(R.drawable.ic_usb)
                    .setContentTitle("USB已插入")
                    .setContentText("USB设备已成功连接")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT);
            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
            notificationManager.notify(1, builder.build());
        }
        else if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
            // USB设备已拔出
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "channel_id")
                    .setSmallIcon(R.drawable.ic_usb)
                    .setContentTitle("USB已拔出")
                    .setContentText("USB设备已成功断开连接")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT);
            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
            notificationManager.notify(2, builder.build());
        }
        else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
            // USB设备已卸载
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "channel_id")
                    .setSmallIcon(R.drawable.ic_usb)
                    .setContentTitle("USB已卸载")
                    .setContentText("USB设备已被卸载")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT);
            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
            notificationManager.notify(3, builder.build());
        }
    }
}

或者通过以下方式:

public class MyBroadcastReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
    	String action = intent.getAction();
        UsbDevice tmpUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
        if (isBlackboard(tmpUsbDevice)){ //这里写了个方法过滤是否是我需要的usb
            if (ACTION_USB_PERMISSION.equals(action)) {
            } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)){
            } else if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)){
            }
        }
    }
}

2.1 Kotlin版本

1. 获取UsbManager对象

在Android应用程序中,首先需要使用UsbManager类来管理USB设备。

val manager = getSystemService(Context.USB_SERVICE) as UsbManager
2. 查找设备

接下来,需要查找与Android设备连接的USB设备。通过下面的代码可以实现:

val deviceList = manager.deviceList
val deviceIterator = deviceList.values.iterator()
while (deviceIterator.hasNext()) {
    val device = deviceIterator.next()
    // 对连接的USB设备进行处理...
}
3. 打开设备

打开设备需要获取设备权限。通过下面的代码可以实现:

val connection = manager.openDevice(device)
if (connection != null) { 
    // 对连接的USB设备进行读写操作...
    connection.close()
}
4. 进行读写操作

打开设备后,可以使用UsbDeviceConnection对象进行读写操作。通过下面的代码可以实现:

val usbInterface = device.getInterface(0) //从USB设备中获取第一个接口
val usbEpOut = usbInterface!!.getEndpoint(0) // 获取输出端点
if (usbEpOut.direction == UsbConstants.USB_DIR_OUT) {
    // 输出端点用于发送数据
} else {
    // 获取输出端点失败
}

val usbEpIn = usbInterface!!.getEndpoint(1) // 获取输入端点
if (usbEpIn.direction == UsbConstants.USB_DIR_IN) {
    // 输入端点用于接收数据
} else {
    // 获取输入端点失败
}
connection.claimInterface(usbInterface, true) //连接系统将要使用该接口,该接口不能被其他应用程序使用。
val buffer = ByteArray(usbEpIn.maxPacketSize) //创建一个缓冲区数组,大小为端点最大包大小。
val count = connection.bulkTransfer(usbEpIn, buffer, buffer.size, TIMEOUT) //使用连接从端点读取数据,数据将被存储在缓冲区中

在使用USB进行数据传输时,通常需要使用UsbEndpoint来指定数据的传输方向。对于一个USB设备的一个接口,可能会包含多个USB端点,其中输入端点(In Endpoint)用于接收数据,输出端点(Out Endpoint)用于发送数据。
bulkTransfer这个方法的第一个参数是一个UsbEndpoint对象,用来表示数据传输的方向和类型。如果这个UsbEndpoint对象是输入端点(In Endpoint),那么这个方法就是用来从USB设备获取数据的;如果这个UsbEndpoint对象是输出端点(Out Endpoint),那么这个方法就是用来向USB设备发送数据的。注意这个方法是同步的,如果想异步可以使用UsbRequest

val usbRequest = UsbRequest()
usbRequest.initialize(connection, usbEpIn) // 指定请求数据的方向和传输数据的方式
val byteBuffer = ByteBuffer.allocate(usbEpIn.maxPacketSize)
usbRequest.queue(byteBuffer, inMax)
5. 关闭设备

完成读写操作后,需要关闭设备。通过下面的代码可以实现:

connection.releaseInterface(usbInterface)
connection.close()

2.2 Java版本

1. 获取UsbManager对象
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
2. 查找设备
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while (deviceIterator.hasNext()) {
    UsbDevice device = deviceIterator.next();
    // 对连接的USB设备进行处理...
}
3. 打开设备
UsbDeviceConnection connection = manager.openDevice(device);
if (connection != null) { 
    // 对连接的USB设备进行读写操作...
    connection.close();
}
4. 进行读写操作
UsbInterface usbInterface = device.getInterface(0);
UsbEndpoint usbEpOut = usbInterface .getEndpoint(0); // 获取输出端点
if (usbEpOut.getDirection() == UsbConstants.USB_DIR_OUT) {
    // 输出端点用于发送数据
} else {
    // 获取输出端点失败
}

UsbEndpoint usbEpIn = usbInterface .getEndpoint(1); // 获取输入端点
if (usbEpIn.getDirection() == UsbConstants.USB_DIR_IN) {
    // 输入端点用于接收数据
} else {
    // 获取输入端点失败
}
connection.claimInterface(usbInterface, true);
byte[] buffer = new byte[usbEpIn.getMaxPacketSize()];
int count = connection.bulkTransfer(usbEpIn, buffer, buffer.length, TIMEOUT);

异步传输:

UsbRequest usbRequest = new UsbRequest();
usbRequest.initialize(connection , usbEpIn); //指定请求数据的方向和传输数据的方式
ByteBuffer byteBuffer = ByteBuffer.allocate(usbEpIn.maxPacketSize);
usbRequest.queue(byteBuffer, inMax);
5. 关闭设备
connection.releaseInterface(usbInterface);
connection.close();

三、结语

  • 感谢各位同学的耐心阅读,希望这篇文章对您有所帮助。
  • 如果你还有任何疑问或建议,欢迎在评论区留言,我会尽快回复。
  • 期待能够与更多的Android开发者分享更多的技术、经验和创意。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>