Android USB Host开发笔记
来源:互联网 发布:频繁跳槽如何优化简历 编辑:程序博客网 时间:2024/05/16 06:26
最近在做有关Android TV端使用USB外设进行用户交互方面的开发。总结一下开发过程中的困惑、解决方案以及开发的整个过程:
一、做USB Host开发前的准备工作:
1. “工欲善其事必先利其器”,先简单了解一下USB Host:
1> USB Device:从硬件角度看就是一个带有USB Client控制器的设备;从软件角度看,就是一个挂在USB总线上的一个普通意义上的设备,只不过它们的驱动是基于Host驱动之上的。
2> USB Host:USB主设备,可以从另外一个USB Device中取得数据,包括USB Host控制器和USB Host协议。与USB Client设备和USB Slave协议相对应。
3> USB Client:从硬件角度看就是指USB Device,从软件角度看,就是指USB Client协议。
4> USB OTG:On The Go,正在进行中的意思,也就是可以直接传输,就是可以从一个机器直接传到另一个机器中。
5> USB HUB:USB扩展/集线器,一种可以将一个USB接口扩展为多个(通常为4个),并可以使这些接口同时使用的装置。
我总结一下:Android TV上提供USB插口,Android TV是USB Host(包含协议,是主设备),插在插口上的USB Device为USB Client。如果一个插口上插了个USB分线器,这个是USB HUB。USB Client与USB Host之间进行数据交换叫做OTG。
2. Check your device surpport USB Host or not.
怎么检查呢?
1> 使用ES或RE文件浏览器,进入 /system/etc/permissions 目录下,查看是否有 android.hardware.usb.host.xml 文件。
2> 若有那么OK。没有的话自己创建一个同名的文件,在文件中写入:
<permissions> <feature name="android.hardware.usb.host /> </permissions>
3> 最好检查一下: /system/etc/permissions 目录下
手机:handheld_core_hardware.xml
平板/TV:tablelet_core_hardware.xml
文件中的< permissions >< /permissions >节点下是否第2>点中的那句话,没有的话可以考虑加一下。(Google官方说不加且有第1>点中的文件是没问题的。)
二、USB Host 的Android工程Demo编写
- 新建一个Android 工程,在清单配置文件中设置应用的USB Host权限(注意:此处是应用的权限,之前第一点是设备的硬件支持)
<uses-feature android:name="android.hardware.usb.host" android:required="true" /><uses-permission android:name="android.hardware.usb.host" /><uses-permission android:name="android.hardware.usb.accessory" />
另外,需要在< application>的某个< activity>节点中添加意图过滤(Intent-Fliter)、中继数据(meta-data),具体如下:
<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) 意图过滤表示的是 硬件USB设备依附的动作意图;
2) 中继数据表示的是 关联res/xml文件夹下的device_filter.xml文件,因此需要在对应的目录下创建这个文件,并且在此文件中编写代码(表示的是你的应用要获取的指定的USB设备的数据),我的是这样的:
<?xml version="1.0" encoding="utf-8"?><resources> <usb-device vendor-id="11021" product-id="257" /> <usb-device vendor-id="11020" product-id="513" /></resources>
这里的vendor_id也好、 product_id也好,一定要写你自己需要获取数据的设备的id。
2.Java代码部分。
1) 需要知道的几个类:UsbManager、UsbDevice、UsbInterface、UsbDeviceConnection、UsbEndPoint这几个类。
大概介绍一下(想详细了解可以参考AndroidDevelopers官方网站):
UsbManager:USB管理器,是获得USB设备,进行数据交换的最基本的类。
UsbDevice:表示USB设备的一个类。
UsbDeviceConnection:表示USB设备连接的类
UsbInterface:连接的USB设备的接口(类似Channel),表示数据是从哪个通道传递过来的。下面是一些通用的基本操作:
// 创建USB管理器对象UsbManager mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);// 获得被USB管理器管理的所有设备HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();// 查看管理器是否对某个USB设备有管理权限mUsbManager.hasPermission(mUsbDevice)// 获得USB数据传输的通道UsbInterface intf = mUsbDevice.getInterface(index);
2) 数据接收类(根据业务需求封装的类) —— HidUsb.java
重磅干货来袭!!!
public class HidUsb { private static final String TAG = HidUsb.class.getSimpleName(); private Context mContext; private UsbManager mUsbManager; private UsbDevice mUsbDevice; private UsbInterface mInterface; private UsbDeviceConnection mDeviceConnection; private UsbEndpoint mEpOut; private UsbEndpoint mEpIn; //主线程消息处理器 private Handler mHandler; //工作线程消息处理器 private HandlerThread mHandlerThread; private HidListener mListener; private Handler mHidHandler; public HidUsb(Context context) { Log.i(TAG, "The construction method of HidUsb had been executed!"); mContext = context; mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); start(); } public void release() { stop(); } /** * 此方法用来设置监听器 * * @param listener */ public void setListener(HidListener listener) { mListener = listener; } /** * 此方法用来开启HID */ public void openHID() { // 关闭HID closeHID(); // 判断是否是要求的设备 enumerateDevice(); // 判断是否有使用USB HOST的权限。 if (hasPermission()) { // 有权限则连接HID设备 // TODO 此处仍有提升空间connectHID() 和 openDevice() 方法都返回布尔值,返回false需要进行什么处理 connectHID(); } else { // 没有权限则获得权限 obtainPermission(); } } /** * 此方法用来连接HID设备 */ private boolean connectHID() { // 找到接口,将找到的指定接口的代号赋给成员变量mInterface findInterface(); // 分配端节点,给端节点赋值 assignEndpoint(); // 返回打开HID设备 return openDevice(); } public void closeHID() { if (mDeviceConnection != null) { mDeviceConnection.releaseInterface(mInterface); mDeviceConnection.close(); } mDeviceConnection = null; mUsbDevice = null; mInterface = null; } /** * 查找device,找到指定的HidUsb设备 */ private void enumerateDevice() { Log.v(TAG, "enumerateDevice."); if (mUsbManager != null) { Log.d(TAG, "enumerateDevice2"); HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList(); if (!deviceList.isEmpty()) { Iterator deviceIterator = deviceList.values().iterator(); while (deviceIterator.hasNext()) { UsbDevice device = (UsbDevice) deviceIterator.next(); Log.v(TAG, "device = " + device); // 获得USB设备列表,判断是否是要求的USB设备 if (isYourRequiredDevice(device)) { // 是要求的USB 设备的情况下,将此设备赋值给成员变量 mUsbDevice mUsbDevice = device; Log.d(TAG, "enumerateDevice: Get Device OK."); break; } } } else { Log.e(TAG, "enumerateDevice: Device list is null !!!"); } } else { Log.e(TAG, "enumerateDevice: USB manager is null !!!"); } // 否则报出错误是设备是空、或者设备管理器是空等 } /** * 是否有权限 * * @return true if caller has permission */ private boolean hasPermission() { if (mUsbDevice != null) { return mUsbManager.hasPermission(mUsbDevice); } return false; } /** * 获取权限 */ private void obtainPermission() { if (mUsbDevice != null) { PendingIntent mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(Constants.ACTION_USB_PERMISSION), 0); mUsbManager.requestPermission(mUsbDevice, mPermissionIntent); } } private void findInterface() { // 如果成员变量mUsbDevice不是空(参考enumerateDevice方法)时, // 获得Usb设备的接口(通道)的数目,在所有接口中找到 if (mUsbDevice != null) { int intfCount = mUsbDevice.getInterfaceCount(); for (int i = 0; i < intfCount; ++i) { UsbInterface intf = mUsbDevice.getInterface(i); int intfClass = intf.getInterfaceClass(); int intfSubClass = intf.getInterfaceSubclass(); int intfProtocol = intf.getInterfaceProtocol(); if (是你要的通道) { // TODO } } } } private void assignEndpoint() { // 如果指定的端口代号不是空,获得EndPoint的数目 // 找出指定的端节点,设置成员变量EndPointOut/EndPointIn if (mInterface != null) { int epCount = mInterface.getEndpointCount(); for (int i = 0; i < epCount; ++i) { UsbEndpoint ep = mInterface.getEndpoint(i); int epType = ep.getType(); int epDir = ep.getDirection(); if (epType == 指定的端节点) { // TODO } } } } /** * 打开device * * if(接口代号不为空){ * if(USB设备管理器有制定设备的管理权限){ * 初始化USB设备连接; * } * * // 表示有权限或者没权限初始化了连接 * if(USB设备连接仍然为空){ * 说明无法连接指定的USB设备; * return false; * } * * // 表示连接不是空 * if(申明接口){ * 重新发送消息,执行任务; * return true; * } * * }else{ * // 表示接口代号为空 * if(连接不为空){ * // 表示连接了设备,这个设备却没有接收数据的接口 * 关闭连接; * return false; * } * * return false; * } */ private boolean openDevice() { if (mInterface != null) { UsbDeviceConnection conn = null; if (mUsbManager.hasPermission(mUsbDevice)) { conn = mUsbManager.openDevice(mUsbDevice); } Log.d(TAG, "conn = " + conn); if (conn == null) { Log.e(TAG, "open device null!!!"); return false; } if (conn.claimInterface(mInterface, true)) { mDeviceConnection = conn; Log.d(TAG, "open device OK."); mHandler.removeCallbacks(mRunnable); mHandler.post(mRunnable); return true; } else { if (conn != null) { conn.close(); } Log.e(TAG, "open device Error!!!"); } } return false; } private void start() { Log.i(TAG, "start method running!"); // 1.创建了一个关联当前线程(主线程)的Handler mHandler = new Handler(); // 2.创建了一个自带Handler的工作线程,并启动线程 mHandlerThread = new HandlerThread("Hid_Thread"); mHandlerThread.start(); // 3.获得了工作线程的Handler对象 mHidHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { // 收到GET_DATA_MSG消息时执行 case GET_DATA_MSG: try { // 调用getData方法 final long gd = getData(); // 判断获得的数据不为0,监听器不为空,主线程的Handler不为空时执行 // 向主线程发送消息,调用监听器的onCodeReceive方法 if (gd != 0 && mListener != null && mHandler != null) { mHandler.post(new Runnable() { @Override public void run() { mListener.onCodeReceive(gd); } }); } } catch (NullPointerException e) { // 抓去空指针异常 e.printStackTrace(); // 休眠50毫秒 Thread.sleep(50); } catch (InterruptedException e2) { e2.printStackTrace(); } // 4.移除主线程任务队列中的可执行任务 if(mHandler!=null) { mHandler.removeCallbacks(mRunnable); // 5.向主消息队列中放置任务 mHandler.post(mRunnable); } break; default: break; } } }; } private Runnable mRunnable = new Runnable() { @Override public void run() { // 当主线程Handler不为空时执行清空消息队列中的消息,并发送GET_DATA_MSG消息。 if (mHidHandler != null) { mHidHandler.removeMessages(GET_DATA_MSG); mHidHandler.sendEmptyMessage(GET_DATA_MSG); } } }; @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private void stop() { mHandler.removeCallbacks(mRunnable); mHandler = null; if (mHandlerThread != null) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { mHandlerThread.quit(); } else { mHandlerThread.quitSafely(); } mHandlerThread = null; mHidHandler = null; } } private long getData() { return 你从底层获得的数据 } private boolean isYourRequiredDevice(UsbDevice device) { return 根据vendorid productid判断是否是你要的USB设备 } /** * 这是一个监听器接收数据的接口 */ public interface HidListener { void onCodeReceive(long code); }}
3.在页面中获得数据 MainActivity.java
public class MainActivity extends Activity implements HidUsb.HidListener { private static final String TAG = MainActivity.class.getSimpleName(); private HidUsb mHidUsb; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); onInitUsb(); } private void onInitUsb() { mHidUsb = new HidUsb(this); mHidUsb.setListener(this); mHidUsb.openHID(); } @Override public void onCodeReceive(long code) { // TODO }}
我觉得注释写的还算清楚就不解释了~~~
————————————————————————
- Android USB Host开发笔记
- AndRoid usb HOST开发
- 有关Android Usb Host开发
- Android USB Host
- Android USB Host
- Android USB Host
- Android USB Host
- Android USB Host
- Android USB Host
- Android USB Host
- OTG - Android USB Host
- android-USB Host
- Android USB HOST通信
- Android USB Host
- Android USB Host
- android usb otg转换成host调试笔记
- Android下的USB Host介绍和开发
- Android下的USB Host介绍和开发
- Error: com.mysql.jdbc.CommunicationsException: Communications link failure due to underlying except
- 伴娘礼服上的年华(十一)
- PluggableSchemaResolver:106 - Trying to resolve XML entity with public id [null] and system id
- C# 数据库查询取返回值
- android-root
- Android USB Host开发笔记
- Java基础数组应用
- Android App框架分享
- C#WPF窗口显示语音菜单(MVVM,DataBinding)
- Java 21选5 问题
- Redis的持久化-RDB
- 根据传入条件设置checkbox
- nginx搭建flv、mp4流媒体服务器
- HDU2024 C语言合法标识符