news 2026/2/13 17:38:30

Android下OTG主机模式访问外设的完整示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android下OTG主机模式访问外设的完整示例

手把手教你用Android实现OTG外设通信:从U盘读写到扫码枪接入

你有没有想过,你的安卓手机不仅能充电、上网,还能像电脑一样插U盘、接键盘、连扫码枪?这并不是什么黑科技,而是早已内置于Android系统中的USB On-The-Go(OTG)功能。在仓储盘点、医疗设备、工业PDA甚至智能收银台中,这项技术正悄悄改变着移动终端的能力边界。

今天,我们就来彻底拆解Android下的OTG主机模式开发——不讲空话,不堆概念,只给你能跑起来的代码和踩过坑后的实战经验。


为什么是OTG?移动设备的“类PC化”之路

过去,我们用手机拍照片、刷视频;现在,越来越多场景要求它承担起“现场工作站”的角色:巡检人员要导出传感器数据,药店需要扫描药品条码,工厂工人得把检测结果存进U盘带回办公室……这些任务如果每次都靠Wi-Fi上传或蓝牙传输,不仅慢,还容易受环境干扰。

而OTG的出现,让这一切变得简单直接:一根小小的转接线,就能让你的平板变身迷你主机,即插即用U盘、键盘、鼠标、串口设备等标准USB外设。

关键优势一句话总结
高速 + 低延迟 + 兼容性强 + 无需配对,特别适合对稳定性要求高的专业场景。

从Android 3.1(API Level 12)开始,系统原生支持OTG Host Mode,通过UsbManager暴露接口,开发者可以完全掌控外设连接与数据交互全过程。


核心机制揭秘:当一个U盘插入时,系统到底做了什么?

当你把U盘插入带OTG功能的安卓设备时,背后其实发生了一连串精密协作:

  1. 硬件检测:Type-C或Micro USB的ID引脚被拉低,触发内核驱动(如dwc2)识别为“有设备接入”;
  2. 设备枚举:系统向U盘发送请求,获取它的厂商ID(VID)、产品ID(PID)、设备类(Class)、端点信息等;
  3. 广播通知:Android发出ACTION_USB_DEVICE_ATTACHED广播;
  4. 权限协商:App收到广播后,调用requestPermission()弹出授权对话框;
  5. 建立连接:用户点击“允许”,App获得UsbDeviceConnection,可进行读写操作。

整个流程由Linux USB子系统支撑,上层应用只需关注业务逻辑即可。


UsbManager:一切的起点

所有OTG开发的第一步,都是获取这个系统服务:

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);

有了它,你可以做三件最重要的事:

  • 查看当前已连接的设备列表
  • 监听插拔事件
  • 请求访问权限

如何监听设备插入?

必须注册一个广播接收器,并动态注册Intent Filter(静态注册在部分机型上无效):

private static final String ACTION_USB_PERMISSION = "com.example.otgdemo.USB_PERMISSION"; BroadcastReceiver usbReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null && intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { // 用户点了“允许” connectToDevice(device); } } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); handleDeviceAttached(device); // 检查是否为目标设备 } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); cleanupDeviceResources(device); // 释放资源 } } }; // 注册广播 IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_USB_PERMISSION); filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); registerReceiver(usbReceiver, filter);

⚠️注意Android 12+兼容性问题
如果你的目标SDK >= 31,记得给PendingIntent加上标志位:

PendingIntent.FLAG_MUTABLE

否则权限请求不会弹窗!


实战一:读取U盘文件内容(无需root)

很多人以为Android不能读U盘是因为权限限制,其实更大的问题是——系统并不自动挂载FAT/exFAT格式的U盘。即使你能看到设备,也无法像PC那样直接访问/mnt/media_rw/...路径。

那怎么办?答案是:自己解析文件系统。

这里推荐使用开源库libaums,它封装了完整的USB Mass Storage协议栈,支持SCSI命令、CBW/CSW帧处理、分区表解析和FAT32/exFAT读写。

添加依赖

implementation 'com.github.mjdev:libaums:0.9.0'

连接并读取U盘根目录

public void openUsbDrive(UsbDevice device) { UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); try { // 创建通信通道 UsbCommunication communication = new UsbCommunicationFactory(manager, device).create(); // 包装成块设备 BlockDevice blockDevice = new UsbBlockDevice(communication); blockDevice.init(); // 解析主引导记录(MBR),取第一个有效分区 Partition partition = new MBRPartition(blockDevice).getPartitions().get(0); // 初始化文件系统 FileSystem fs = FAT32FileSystem.read(partition); // 列出根目录文件 List<FileSystemObject> files = fs.getRootDirectory().getList(); for (FileSystemObject f : files) { Log.d("OTG", "文件名: " + f.getName() + ", 大小: " + f.getLength()); } // 读取某个文本文件 FsFile targetFile = fs.getRootDirectory().search("log.txt"); if (targetFile != null) { InputStream is = targetFile.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line; while ((line = reader.readLine()) != null) { Log.d("FILE_CONTENT", line); } reader.close(); } } catch (IOException e) { e.printStackTrace(); } }

💡提示
libaums目前只支持读写,不支持创建新文件系统或格式化。但对于绝大多数数据导入/导出需求已经足够。


实战二:接入HID设备(键盘、扫码枪)

HID(Human Interface Device)是最常见的USB设备类型之一。有趣的是,很多条码扫描枪本质上就是一台“会打字的键盘”,它们以HID身份接入后,直接模拟按键输入。

这意味着:只要你禁用软键盘,就可以让扫码内容自动填入输入框,体验丝滑流畅。

但如果你想自定义行为呢?比如区分普通键盘和专用扫码枪?或者提取原始数据包做二次处理?

那就需要手动打开设备,监听中断端点。

手动读取HID设备数据流

public void readHidDevice(UsbDevice device, UsbDeviceConnection connection) { UsbInterface hidInterface = device.getInterface(0); if (!connection.claimInterface(hidInterface, true)) { Log.e("OTG", "无法声明接口"); return; } UsbEndpoint interruptIn = null; for (int i = 0; i < hidInterface.getEndpointCount(); i++) { UsbEndpoint ep = hidInterface.getEndpoint(i); if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT && ep.getDirection() == UsbConstants.USB_DIR_IN) { interruptIn = ep; break; } } if (interruptIn == null) { Log.e("OTG", "未找到IN方向的中断端点"); return; } new Thread(() -> { byte[] buffer = new byte[interruptIn.getMaxPacketSize()]; while (true) { int ret = connection.bulkTransfer(interruptIn, buffer, buffer.length, 1000); if (ret > 0) { parseHidReport(Arrays.copyOf(buffer, ret)); } else if (ret == -1) { break; // 错误或断开 } } }).start(); }

解析键盘报告(简化版)

private void parseHidReport(byte[] report) { byte modifier = report[0]; // Shift/Ctrl等修饰键 byte[] keyCodes = Arrays.copyOfRange(report, 2, 8); // 健盘码数组 StringBuilder sb = new StringBuilder(); for (byte code : keyCodes) { if (code != 0) { String key = UsbHidKeycodeMapper.keycodeToString(code); sb.append(key).append(" "); } } if (sb.length() > 0) { Log.d("HID_INPUT", "输入: " + sb.toString().trim()); } }

📌应用场景举例
- 条码扫描枪连续扫多个码时,每个码末尾通常带回车符,可用于触发提交动作。
- 游戏手柄可通过HID上报摇杆坐标和按钮状态,实现本地控制。


常见问题与避坑指南

别急着上线,先看看这些你一定会遇到的问题:

问题原因分析解决方案
插上没反应OTG线质量问题使用带芯片的主动式OTG线,避免“只能充电”的劣质线材
权限请求不弹窗PendingIntent未设置FLAG_MUTABLEAndroid 12+必须添加.setFlag(PendingIntent.FLAG_MUTABLE)
U盘识别但读不出来分区表异常或exFAT不支持使用libaums手动解析;确保U盘格式为FAT32
扫码枪输入重复系统默认注入 + 自己又读了一遍若使用手动读取,关闭系统HID映射(需root或定制ROM)
设备供电不足OTG输出电流有限(一般≤500mA)高功耗设备外接电源,或使用带供电的集线器

最佳实践建议

  1. 设备过滤更精准
    res/xml/device_filter.xml中指定只监听特定VID/PID:

```xml




```

并在AndroidManifest.xml中关联:

xml <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/device_filter" />

  1. 避免内存泄漏
    每次断开都要及时调用connection.close()unregisterReceiver()

  2. 提升用户体验
    可预先缓存已授权设备,在重启后尝试自动重连(仍需用户确认)。

  3. 多品牌适配测试不可少
    华为、小米、三星等厂商对OTG支持程度不同,务必实机测试。


写在最后:OTG不只是“插U盘”那么简单

掌握OTG开发,意味着你能让Android设备真正成为一个通用数据枢纽。无论是:

  • 工业现场快速导出日志文件,
  • 零售门店构建低成本POS系统,
  • 教育场景实现作业批量提交,
  • 辅助设备接入物理键盘帮助视障人士,

……都有它的用武之地。

随着USB Type-C成为主流,PD快充协议普及,未来的OTG甚至可能支持反向供电、多设备级联、音视频同步传输等功能。今天的底层能力积累,正是为了迎接那一天的到来。

如果你在项目中实现了有趣的OTG功能,欢迎留言交流!我们一起推动移动嵌入式开发的边界。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/8 11:55:20

Open-AutoGLM移动端落地难?资深工程师亲授手机端高效部署秘诀

第一章&#xff1a;Open-AutoGLM移动端落地难&#xff1f;资深工程师亲授手机端高效部署秘诀在将 Open-AutoGLM 这类大型语言模型部署至移动端时&#xff0c;许多开发者面临推理延迟高、内存占用大和设备兼容性差等问题。然而&#xff0c;通过合理的模型压缩与运行时优化策略&a…

作者头像 李华
网站建设 2026/2/10 21:04:59

Apache Arrow与PostgreSQL集成:7种高效数据连接方案完整教程

Apache Arrow与PostgreSQL集成&#xff1a;7种高效数据连接方案完整教程 【免费下载链接】arrow Apache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing 项目地址: https://gitcode.com/gh_mirrors/arrow13/arrow Apach…

作者头像 李华
网站建设 2026/2/4 1:15:42

【大模型自动化新纪元】:Open-AutoGLM为何成为AI工程师的必备工具?

第一章&#xff1a;Open-AutoGLM的核心价值与行业影响Open-AutoGLM 作为新一代开源自动化通用语言模型框架&#xff0c;正在重塑企业级AI应用的开发范式。其核心价值不仅体现在模型性能的显著提升&#xff0c;更在于对开发效率、部署成本和行业适配性的全面优化。推动AI工程化落…

作者头像 李华
网站建设 2026/2/4 20:02:52

超详细版LCD显示屏驱动时序分析:适合新手学习

搞懂LCD显示时序&#xff1a;从原理到实战&#xff0c;新手也能轻松上手你有没有遇到过这样的情况&#xff1f;屏幕背光亮了&#xff0c;但画面却是花屏、错位&#xff0c;甚至完全黑屏——明明代码烧进去了&#xff0c;引脚也接对了&#xff0c;为什么就是出不来图像&#xff…

作者头像 李华
网站建设 2026/2/6 22:55:48

如何快速检测Windows 11兼容性:免费工具完整指南 [特殊字符]

如何快速检测Windows 11兼容性&#xff1a;免费工具完整指南 &#x1f680; 【免费下载链接】WhyNotWin11 Detection Script to help identify why your PC is not Windows 11 Release Ready. Now Supporting Update Checks! 项目地址: https://gitcode.com/gh_mirrors/wh/Wh…

作者头像 李华
网站建设 2026/2/10 13:36:10

服务器管理终极指南:从入门到精通的运维实战宝典

服务器管理终极指南&#xff1a;从入门到精通的运维实战宝典 【免费下载链接】hestiacp Hestia Control Panel | A lightweight and powerful control panel for the modern web. 项目地址: https://gitcode.com/gh_mirrors/he/hestiacp 作为一名资深服务器管理员&#…

作者头像 李华