【Android开发】Android Host详解(翻译自官方文档)

原文:http://android.eoe.cn/topic/summary

 

文档内容

  • API概述
  • Android中manifest文件需求
  • 工作的设备
  • * 发现设备
  • * 获得和设备进行“交流”的权限
  • * 和设备进行“交流”
  • * 中止和设备的“交流”

相关例子

  • Adb测试用例
  • 相关链接 当您搭载Android系统的设备处于USB主机模式时,它就像一个USB主机,为总线提供能源,并且列举出所有已经连接上的设备。在Android 3.1或者更高的版本中支持USB主机模式。

API概述

在您开始之前,有个很重要的一点就是您必须对将要用到的类有个了解。下面的表格就向您描述了在android.hardware.usb这个包下USB主机APIs的一些特点。

  • 表1.* USB主机APIs <!-- 表格开始 --> {|style="border-spacing: 0px;margin: 4px 4px; width: 90%; border-left:1px solid #ccc;border-top:1px solid #ccc; "

<!-- 这段是表头 -->
|-style="background:#DEE8F1; "
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | Class/Interface
! style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px" | Description

<!-- 这段是表格 -->
|- style=" vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
UsbManager
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |
允许您枚举已连接的USB设备并且与其进行“交流”。

<!-- 这段是表格 -->
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
UsbDevice
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
代表了一个已连接的USB的设备并且包含具有该设备验证信息,接口和接入点的方法。

<!-- 这段是表格 -->
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
UsbInterface
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
代表了一个USB设备的一个接口,该接口定义了一系列关于设备的函数。一个设备在进行“交流”的时候可以有一个或者多个接口。

<!-- 这段是表格 -->
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
UsbEndpoint
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
代表一个接口的接入点,该接入点就是这个接口的通信信道。一个接口可以有一个或者多个这样的接入点,而且一般都是有输入和输出双向通信的接入点。

<!-- 这段是表格 -->
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
UsbDeviceConnection
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
代表该设备的一个连接,用来在接入点上传输数据。这个类允许您能用同步或者异步的方式发送和返回数据。

<!-- 这段是表格 -->
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
UsbRequest
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
在通过UsbDeviceConnection和设备进行“交流”的一个异步请求。

<!-- 这段是表格 -->
|- style="vertical-align:top;"
| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
UsbConstants
| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |
关于在linux内核中linux/usb/ch9.h的相关定义的USB常量。
|}
<!-- 表格结束 -->
在大多数的情况之下,在和一个USB设备进行“交流”时,上面这些类都需要用到(UsbRequest这个类只有在您做异步通信的时候才会用到)。一般来说,您可以通过查询要操作的UsbDevice来获得一个UsbManager。当您有这个设备时,您需要找到正确的UsbInterface以及和这个接口所对应的UsbEndpoint来进行和设备的“交流”。一旦您获得了正确的接入点,打开UsbDeviceConnection来和该USB设备进行“交流”。

Android中manifest文件的需求

下面的列表就是描述您应该在用USB主机APIs之前应该在您的应用中的manifest文件中添加些什么:
* 因为不是所有的搭载Android系统的设备都能保证支持USB主机的APIs,不能包含那个声明您的应用使用android.hardware.usb.host的这一元素。
* 设置您的应用的最低的SDK版本在12级或者更高。这个USB主机APIs不在更前面的版本之中。
* 如果您希望您的应用能够被连接的USB设备所提示,只要在您的主activity中在元素对中添加一个元素指向一个额外的XML资源文件,该文件是用来声明验证您希望探测到的设备的验证信息。
   在这个XML资源文件中,为您希望过滤的USB设备声明的属性。一般来说,如果您想为一个特定的设备过滤就使用该产品的供应商和产品ID,如果您希望为一组USB设备,例如大量存储设备或者是数码相机来进行过滤那么就应该用类,子类和协议。您可以不指定这些属性,也可以指定所有的属性。不为每个设备指定属性,只有在您的应用需要它时才这么做(这句话翻译的一点问题^_^):
:* 供应商ID
:* 产品ID
:* 类
:* 子类
:* 协议(设备或者借口)
   将您的资源文件保存到元素中指明的那个名字。在下面的例子中是这个XML资源文件的格式。

Manifest文件和资源文件的例子

下面的例子告诉您一个manifest文件以及与它相关资源文件的例子:




...


...


1
2
3
4
        <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
            android:resource="@xml/device_filter" />
    </activity>
</application>

在这种情况下,下面的资源文件应该被保存在res/xml/device_filter.xml来确保找到那些特定符合您要求属性的USB设备:

<?xml version"utf-8"?>

用配件工作

当用户将USB配件连接到搭载Android系统的设备上面时,Android系统会判断您的应用是否适用于已连接的该配件。如果适用,您就可以根据您的喜好为该设备建立连接。要这么做,您的应用必须做下面这些动作:

您需要通过一个可以过滤USB设备附加事件的意图过滤器或者枚举已连接的USB设备来发现连接的配件来找到合适的接口。

尚未获得许可的用户在适用USB设备操作时需要验证权限。

通过在接入的端点进行读写数据的操作达到和USB设备交互的目的。

发现设备

您的应用可以通过两种方式来发现USB设备,一种是用一个意图过滤器在用户连接一个设备时对其进行通知,另一种则是通过枚举您已经连接的所有USB设备。如果您希望您的应用能够自动的探测到你想要的设备,请使用一个意图过滤器来做。但是,如果您希望得到一个已连接设备的列表或者您不希望过滤意图,枚举所有的设备会是一个更好的选择。

使用一个意图过滤器

为了让您的应用可以发现一个特定的USB设备,您可以为android.hardware.usb.action.USB_DEVICE_ATTACHED这个意图指定一个意图来进行过滤。伴随着这个意图过滤器,您需要指定一个资源文件来特别说明这个USB设备的属性,例如供应商和产品ID。当用户连接到一个符合您配件过滤条件的配件时,这个系统会谈出一个对话框询问他们是否希望开始您的应用。如果用户同意,那么您的应用在失去连接之前会自动获取和设备连接的权限。

下面的例子告诉您该如何声明这个意图过滤器:


...


1
2
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
    android:resource="@xml/device_filter" />

下面的例子告诉您怎么样声明指定您希望连接的USB设备的相关资源文件:

<?xml version"utf-8"?>

在您的activity文件中,您可以从像这样的意图(有附加类的)中获取UsbDevice来代表这个相关的配件:

UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

枚举所有配件

您可以使您的应用在运行时列举出所有能够被识别的USB设备。通过getDeviceList()方法来获得一个包含所有已连接USB配件的数组:

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

HashMap deviceList = manager.getDeviceList();
UsbDevice device = deviceList.get("deviceName");

如果您喜欢,您也可以一个接一个的从每一个设备的哈希图和过程中获取一个迭代器:

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap deviceList = manager.getDeviceList();
Iterator deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
UsbDevice device = deviceIterator.next()
//your code
}

获得使用一个配件的权限

在您使用一个USB设备前,您的应用必须从用户那里获得权限。

* 注意:* 如果您的应用在连接USB设备时通过一个意图过滤器来发现它们,如果用户允许您的应用来处理这个意图,它将自动接收这个权限。如果用户不允许,那么您就必须在连接设备之前详细在您的应用中写明需要请求的权限。

在某些情况下很有必要明确权限的许可要求,例如当您的应用枚举出所有已经连接的USB设备并且您希望和其中的一个进行“交流”。您必须在和该设备“交流”前检查是否有连接该设备的权限。如果不是这样,您的应用将在用户拒绝您连接该设备的权限之后收到个运行错误。

为了确切地获得权限,首先需要创建个广播接收器。这个接收器在您调用requestPermission()这个方法时从您得到的广播中监听这个意图。通过调用requestPermission()这个方法为用户跳出一个是否连接该设备的对话框。下面的例子告诉您如何创建一个广播接收器:

private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();
    if (ACTION_USB_PERMISSION.equals(action)) {
        synchronized (this) {
            UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

            if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                if(device != null){
                  //call method to set up device communication
               }
            } 
            else {
                Log.d(TAG, "permission denied for device " + device);
            }
        }
    }
}

};

为了注册您的广播接收器,将其放在您activity中的onCreate()方法中去:

UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
...
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);

当您需要展示征求用户同意连接这个设备的权限的对话框时,调用requestPermission()这个方法:

UsbDevice device;
...
mUsbManager.requestPermission(device, mPermissionIntent);

当用户回应这个对话框时,你的广播接收器就会收到一个包含用一个boolean值来表示结果的EXTRA_PERMISSION_GRANTED字段的意图。在您连接设备之前检查这个字段的值是否为true。

和设备之间的“交流”

我们可以同步或者异步的和USB设备进行“交流”。在任意一种情况之下,您都应该创建一个新的线程来进行数据传输,这样就不会阻塞您的主线程了。要想正确的设置好和一个设备之间的连接,您需要获得该设备正确的UsbInterface和UsbEndpoint来和您进行“交流”以及通过UsbDeviceConnection在这个接入点上发送请求。一般来说,您的代码应该这样:
* 检查一个UsbDevice对象的属性,例如产品ID,供应商ID,或者是关于设备的类,以此来确认您是否希望和该设备进行“交流”。
* 当您确信您希望和该设备进行“交流”时,找到关于该设备正确的UsbInterface以及和该接口所对应的UsbEndpoint。接口可以有一个或者多个接入点,而且一般都会有一个双向通信的输入和输出接入点。
* 当您找到正确的接入点时,在该接入点时打开一个UsbDeviceConnection。
* 您可以通过bulkTransfer()和controlTransfer()这两个方法在接入点上传输您所需要传递的数据。您最好在另起一个新的线程来进行这个步骤以避免阻塞主线程。想要详细地了解关于Android中使用线程的信息,详见线程和进程。
下面的代码段是做同步数据传输的一个简单方式。您的代码应该有更多的逻辑来准确地找到和设备“交流”的接口和接入点,而且应该能够在不同于主线程的线程中能够传输任何的数据传输。

private Byte[] bytes
private static int TIMEOUT = 0;
private boolean forceClaim = true;

...

UsbInterface intf = device.getInterface(0);
UsbEndpoint endpoint = intf.getEndpoint(0);
UsbDeviceConnection connection = mUsbManager.openDevice(device);
connection.claimInterface(intf, forceClaim);
connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread

为了能够异步传输数据,使用UsbRequest类来初始队列化一个异步请求,然后等待requestWait()方法的结果。

想要了解更多地信息,请您参考Adb Test sample,这个参考将会告诉您如何进行异步批量传输,还有MissleLauncher sample将会告诉您如何异步监听一个中断端点。

中止和设备的“交流”

当您在完成和设备的“交流”之后,又或者该设备被移除了,通过调用releaseInterface()和close()的方法来关闭UseInterface和UsbDeviceConnection。为了监听分离这样的事件,您需要创建一个如下的广播接收器:

BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();

1
2
3
4
5
6
7
  if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
        UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
        if (device != null) {
            // call your method that cleans up and closes communication with the device
        }
    }
}

};

在您的应用中创建这个广播接收器,不是在manifest文件中,允许您的应用只能在它运行的时候处理这样的设备分离事件。这样的话,设备分离这个事件就只向正在运行的应用广播,而不是向所有的应用进行广播。

posted on 2013-06-19 15:45  vus520  阅读(1056)  评论(0编辑  收藏  举报

导航