您的位置:首页 > 移动开发 > Android开发

Android HID设备的连接

2016-06-11 13:48 309 查看
Hid是Human Interface Device的缩写,由其名称可以了解HID设备是直接与人交互的设备,例如键盘、鼠标与游戏手柄等。

我们知道在手机设置--蓝牙功能界面可以手动搜索蓝牙HID设备并进行连接,这篇博客就是介绍如何在android代码中实现HID设备的连接。最后会给出完整的代码工程。一个前提条件是android4.0以上才支持HID设备。

android手机与蓝牙HID设备连接的步骤:

1.开启蓝牙功能

2.手机搜索蓝牙HID设备

3.得到BluetoothDevice,配对HID设备

4.连接HID设备

了解过蓝牙开发的同学相信前面3个步骤都不是问题,本文会重点介绍第四个步骤,前面的步骤简单列出来。

1.开启蓝牙功能

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(this,"不支持蓝牙功能",0).show();
//不支持蓝牙
return;
}
//如果没有打开蓝牙
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.enable();
}


2.手机搜索蓝牙HID设备

//扫描蓝牙设备
if (!mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.startDiscovery();
}


3.配对HID设备

搜索到蓝牙设备后系统会发送这个广播

BluetoothDevice.ACTION_FOUND

通过监听这个广播就可以得到BluetoothDevice

//通过广播接收到了BluetoothDevice

final BluetoothDevice localBluetoothDevice = (BluetoothDevice) intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);


然后通过反射配对BluetoothDevice

/**
* 配对
* @param bluetoothDevice
*/
public void pair(BluetoothDevice bluetoothDevice) {
device = bluetoothDevice;
Method createBondMethod;
try {
createBondMethod = BluetoothDevice.class.getMethod("createBond");
createBondMethod.invoke(device);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}


相信以上步骤大家都十分了解了,下面最重要的部分来了。

4.连接HID设备

找遍所有公开的api中是没用方法可以直接连接HID设备的,既然手机设置-蓝牙界面可以连接HID设备,说明系统是可以做到的,那是不是把这个方法隐藏了,我们带着这个疑问去源码看看。

1)首先我们找到android/bluetooth/BluetoothProfile.class这个类

为什么是先找到这个类?

Bluetooth的一个很重要特性,就是所有的Bluetooth产品都无须实现全部的Bluetooth规范。为了更容易的保持Bluetooth设备之间的兼容,Bluetooth规范中定义了Profile。Profile定义了设备如何实现一种连接或者应用,你可以把Profile理解为连接层或者应用层协议。

所以我们要先确认HID设备属于哪种Profile。

/**
* Input Device Profile
* @hide
*/
public static final int INPUT_DEVICE = 4;


找到定义的INPUT_DEVICE,这就是我们HID设备的Profile,字面意思就是输入设备。

2)找到BluetoothProfile的子类BluetoothInputDevice

查看到该类里面的connect方法,入参BluetoothDevice,我们通过步骤三已经得到BluetoothDevice,我们大胆猜想是不是调用这个方法就可以实现连接了呢。

/**
* Initiate connection to a profile of the remote bluetooth device.
*
* <p> The system supports connection to multiple input devices.
*
* <p> This API returns false in scenarios like the profile on the
* device is already connected or Bluetooth is not turned on.
* When this API returns true, it is guaranteed that
* connection state intent for the profile will be broadcasted with
* the state. Users can get the connection state of the profile
* from this intent.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
* permission.
*
* @param device Remote Bluetooth Device
* @return false on immediate error,
*               true otherwise
* @hide
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
if (mService != null && isEnabled() && isValidDevice(device)) {
try {
return mService.connect(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}


我们再来看下该类的说明

/**
* This class provides the public APIs to control the Bluetooth Input
* Device Profile.
*
*<p>BluetoothInputDevice is a proxy object for controlling the Bluetooth
* Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
* the BluetoothInputDevice proxy object.
*
*<p>Each method is protected with its appropriate permission.
*@hide
*/


首先注意到该类是个隐藏类并不能直接调用,调用方法已经进行说明了。通过BluetoothAdapter#getProfileProxy方法得到隐藏类BluetoothInputDevice

3)BluetoothAdapter#getProfileProxy方法

* @param context Context of the application
* @param listener The service Listener for connection callbacks.
* @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH},
*                {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
*                {@link BluetoothProfile#GATT} or {@link BluetoothProfile#GATT_SERVER}.
* @return true on success, false on error
*/
public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
int profile)


第一个参数就不用说了,我们先看第三个参数 int profile,前面介绍了INPUT_DEVICE,这就是我们HID设备的Profile,我们看到也是隐藏字段,所以我们必须先通过反射获得这个参数。方法如下:

public static int getInputDeviceHiddenConstant() {
Class<BluetoothProfile> clazz = BluetoothProfile.class;
for (Field f : clazz.getFields()) {
int mod = f.getModifiers();
if (Modifier.isStatic(mod) && Modifier.isPublic(mod)
&& Modifier.isFinal(mod)) {
try {
if (f.getName().equals("INPUT_DEVICE")) {
return f.getInt(null);
}
} catch (Exception e) {
}
}
}
return -1;
}


那我们再看第二个参数BluetoothProfile.ServiceListener,这个参数可以回调出BluetoothInputDevice,然后再反射connect方法。

/**
*查看BluetoothInputDevice源码,connect(BluetoothDevice device)该方法可以连接HID设备,但是查看BluetoothInputDevice这个类
* 是隐藏类,无法直接使用,必须先通过BluetoothProfile.ServiceListener回调得到BluetoothInputDevice,然后再反射connect方法连接
*
*/
private BluetoothProfile.ServiceListener connect = new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
//BluetoothProfile proxy这个已经是BluetoothInputDevice类型了
try {
if (profile == getInputDeviceHiddenConstant()) {
if (device != null) {
//得到BluetoothInputDevice然后反射connect连接设备
Method method = proxy.getClass().getMethod("connect",
new Class[] { BluetoothDevice.class });
method.invoke(proxy, device);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(int profile) {

}
};


最后HID设备连接成功,事实证明我们的猜想是正确的。

项目下载地址:

https://github.com/liushenwenyuan/HIDConnect

http://download.csdn.net/detail/szydwy/9546246
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  蓝牙 HID