博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android蓝牙——HID开发
阅读量:6426 次
发布时间:2019-06-23

本文共 8473 字,大约阅读时间需要 28 分钟。

代码地址如下:

http://www.demodashi.com/demo/13891.html

原文地址:

https://blog.csdn.net/VNanyesheshou/article/details/61914974

一 环境

开发环境:

 jdk1.6 Eclipse
 or jdk1.8 AS3.0.1
运行环境:
 华为V10(Android8.0)
实现功能:
 Android 蓝牙Hid——连接蓝牙鼠标、键盘等输入设备。

二 代码结构

AS

Eclipse

三、代码

1 Hid简介

HID设备(Hunman Interface Device Profile),即人机交互设备,常见的有鼠标,键盘,游戏手柄,等等。一般有线方式都是通过USB连线连接到机器设备,作为用户输入设备。在蓝牙技术中,HID设备的接入就是无线的了。

网上查资料说hid从android4.0开始支持(可能是usb hid),不过蓝牙hid应该从android4.2开始支持的,如下图所示:

20170313113645190
android4.1.2中的Bluetooth应用中没有hid的相关代码,而android4.2源码中Bluetooth应用中才有hid的代码。
20170313113633799

2 实现

连接hid设备步骤:

  1. 开启蓝牙
  2. 获得inputDevice profile
  3. 扫描
  4. 配对
  5. 连接

2.1 开启蓝牙,通过广播接收者监听蓝牙相关状态。

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if (mBluetoothAdapter == null) {    Toast.makeText(this, "不支持蓝牙功能", 0).show();    // 不支持蓝牙    return;}// 如果没有打开蓝牙if (!mBluetoothAdapter.isEnabled()) {    mBluetoothAdapter.enable();}// 初始化广播接收者mBroadcastReceiver = new BlueBroadcastReceiver();IntentFilter intentFilter = new IntentFilter();intentFilter.addAction(BluetoothDevice.ACTION_FOUND);intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);intentFilter.addAction("android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED");this.registerReceiver(mBroadcastReceiver, intentFilter);

2.2 获得inputDevice profile

// 4.2以上才支持HID模式

if (Build.VERSION.SDK_INT >= 17) {
  mHidUtil = HidUtil.getInstance(this);
}

public static HidUtil getInstance(Context context){    if(null == instance){        instance = new HidUtil(context);    }    return instance;} private HidUtil(Context context) {    this.context = context;    mBtAdapter = BluetoothAdapter.getDefaultAdapter();    try {        mBtAdapter.getProfileProxy(context,                mListener, INPUT_DEVICE);    } catch (Exception e) {        e.printStackTrace();    }}

通过BluetoothAdapter对象调用getProfileProxy()函数获取代理蓝牙输入设备代理对象。

其中参数mListener必须实现BluetoothProfile.ServiceListener()。获取代理对象成功或失败都会回调该Listener。

private BluetoothProfile.ServiceListener mListener = new BluetoothProfile.ServiceListener() {    @Override    public void onServiceConnected(int profile, BluetoothProfile proxy) {        Log.i(TAG, "mConnectListener onServiceConnected");        //BluetoothProfile proxy这个已经是BluetoothInputDevice类型了        try {            if (profile == INPUT_DEVICE) {                mBluetoothProfile = proxy;            }        } catch (Exception e) {            e.printStackTrace();        }    }     @Override    public void onServiceDisconnected(int profile) {        Log.i(TAG, "mConnectListener onServiceConnected");    }};

当连接代理服务成功,回调onServiceConnected()函数,失败则回调onServiceDisconnected()函数。

其中onServiceConnected()中的参数proxy类型为BluetoothProfile,这里因为获取BluetoothHeadset、BluetoothA2dp对象也要使用该回调函数,所以参数类型设置为BluetoothInputDevice、BluetoothHeadset、BluetoothA2dp的父类。这里可以将其转换成BluetoothInputDevice类型(BluetoothInputDevice是BluetoothProfile的子类)。

获取到输入设备的代理对象,之后就可以进行连接操作了。

2.3 扫描(点击连接按钮开始扫描蓝牙设备)

mBluetoothAdapter.startDiscovery();

2.4 配对

广播接收者监听扫描到蓝牙设备,过滤出所需蓝牙设备进行配对,如果之前配对过则直接连接。

if(action.equals(BluetoothDevice.ACTION_FOUND)){    // 通过广播接收到了BluetoothDevice    final BluetoothDevice device = (BluetoothDevice) intent            .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);    if (device == null) return;    String btname = device.getName();    String address = device.getAddress();    Log.i(TAG, "bluetooth name:"+btname+",address:"+address);    if((address != null&& address.equals(HID_ADDR))||(btname != null && btname.equals(HID_NAME))){        mConnectDevice = device;        mBluetoothAdapter.cancelDiscovery();        if(!mHidUtil.isBonded(device)){            //先配对            mHidUtil.pair(device);        }else {            //已经配对则直接连接            mHidUtil.connect(device);        }    }}

HidUtil类中的配对方法:

/**     * 配对     * @param BluetoothDevice     */public void pair(BluetoothDevice device) {    Log.i(TAG, "pair device:"+device);    Method createBondMethod;    try {        createBondMethod = BluetoothDevice.class.getMethod("createBond");        createBondMethod.invoke(device);    } catch (Exception e) {        e.printStackTrace();    }}

2.5 连接(配对成功后)

if(action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)){    BluetoothDevice device = intent            .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);    String name = device.getName();    String address = device.getAddress();    Log.i(TAG,"name:"+name+",address:"+address+",bondstate:"+device.getBondState());    if((address != null&& address.equals(HID_ADDR))||(name != null && name.equals(HID_NAME))){            if(device.getBondState() == BluetoothDevice.BOND_BONDED)                mHidUtil.connect(device);    }}

判断是否是要连接的输入设备,如果符合条件则进行连接。

HidUtil中connect 方法

/** * 连接设备 * @param bluetoothDevice */public void connect(final BluetoothDevice device) {    Log.i(TAG, "connect device:"+device);    try {        //得到BluetoothInputDevice然后反射connect连接设备        Method method = mBluetoothProfile.getClass().getMethod("connect",                new Class[] { BluetoothDevice.class });        method.invoke(mBluetoothProfile, device);    } catch (Exception e) {        e.printStackTrace();    }}

BluetoothInputDevice中的connect方法是隐藏的,所以需要通过反射机制获取该方法进行调用。

2.6 监听连接状态

通过广播接收者监听连接状态。

if(action.equals("android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED")){    int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,0);    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);    Log.i(TAG,"state="+state+",device="+device);    if(state == BluetoothProfile.STATE_CONNECTED){//连接成功        Toast.makeText(MainActivity.this, R.string.connnect_success, Toast.LENGTH_SHORT).show();    } else if(state == BluetoothProfile.STATE_DISCONNECTED){//连接失败        Toast.makeText(MainActivity.this, R.string.disconnected, Toast.LENGTH_SHORT).show();    }}

2.7 断开连接

if(mConnectDevice != null)    mHidUtil.disConnect(mConnectDevice);

HidUtil中disconnect方法

/** * 断开连接 * @param BluetoothDevice */public void disConnect(BluetoothDevice device) {    Log.i(TAG, "disConnect device:"+device);    try {        if (device != null) {            Method method = mBluetoothProfile.getClass().getMethod("disconnect",                    new Class[] { BluetoothDevice.class });            method.invoke(mBluetoothProfile, device);        }    } catch (Exception e) {        e.printStackTrace();    }}

手机端断开连接后,重新连接会提示“只能有鼠标发起重新连接请求,请使用鼠标重新连接”。

3 接收数据

adb shell

getevent -l
当连接成功后,会看到如下内容:

could not get driver version for /dev/input/mouse1, Not a typewriteradd device 7: /dev/input/event6  name:     "Bluetooth Mouse"

这表示蓝牙鼠标成为一个输入设备。

左击鼠标:

/dev/input/event6: EV_MSC       MSC_SCAN             00090001            /dev/input/event6: EV_KEY       BTN_MOUSE            DOWN                /dev/input/event6: EV_SYN       SYN_REPORT           00000000            /dev/input/event6: EV_MSC       MSC_SCAN             00090001            /dev/input/event6: EV_KEY       BTN_MOUSE            UP                  /dev/input/event6: EV_SYN       SYN_REPORT           00000000

我们应用中打印

03-13 12:08:36.526 I/MainActivity(23670): dispatchTouchEvent ev:MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=269.7555, y[0]=543.9628, toolType[0]=TOOL_TYPE_MOUSE, buttonState=BUTTON_PRIMARY, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=16788085, downTime=16788085, deviceId=39, source=0x2002 }03-13 12:08:36.653 I/MainActivity(23670): dispatchTouchEvent ev:MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=269.7555, y[0]=543.9628, toolType[0]=TOOL_TYPE_MOUSE, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=16788216, downTime=16788085, deviceId=39, source=0x2002 }

表示在屏幕中某位置处点击了一下。

右击鼠标:

/dev/input/event6: EV_MSC       MSC_SCAN             00090002            /dev/input/event6: EV_KEY       BTN_RIGHT            DOWN                /dev/input/event6: EV_SYN       SYN_REPORT           00000000            /dev/input/event6: EV_MSC       MSC_SCAN             00090002            /dev/input/event6: EV_KEY       BTN_RIGHT            UP                  /dev/input/event6: EV_SYN       SYN_REPORT           00000000

表示点击了一下返回键,程序退出。

移动鼠标会发现屏幕上小光标在移动,滑动鼠标也会触发相应事件。

4 其他
现在大部分手机是支持hid的,并且也将该功能打开状态。

如果是做系统开发,就需要注意将Bluetooth中的hid开关打开。

将源码中的packages/apps/Bluetooth/res/values/config.xml的profile_supported_hid 修改为true。

欢迎关注我的微信公众号:

20170415165019939Android蓝牙——HID开发

代码地址如下:

http://www.demodashi.com/demo/13891.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

你可能感兴趣的文章
Lucene.Net 2.3.1开发介绍 —— 四、搜索(一)
查看>>
MyBatis Review——开发Dao的方法
查看>>
技术研发国产化进程加快 看传感器企业如何展示十八般武艺
查看>>
技术助力第三次革命
查看>>
《HTML与CSS入门经典(第8版)》——2.6 总结
查看>>
新手指南:在 Ubuntu 和 Fedora 上安装软件包
查看>>
在 CentOS7.0 上搭建 Chroot 的 Bind DNS 服务器
查看>>
大型网站的 HTTPS 实践(二):HTTPS 对性能的影响
查看>>
《Swift 权威指南》——第6章,第6.10节嵌套函数
查看>>
《自己动手做交互系统》——1.3 本章小结
查看>>
Mobile devices bundled with malware?
查看>>
《JavaScript面向对象精要》——1.5 访问属性
查看>>
《Python数据可视化编程实战》—— 第 1 章 准备工作环境
查看>>
Android应用性能优化最佳实践.1.1 Android Studio的优势
查看>>
《设计模式解析(第2版•修订版)》—第2章 2.2节什么是UML
查看>>
【直播】APP全量混淆和瘦身技术揭秘
查看>>
10个大坑,当你产品上架AppStore会遇到
查看>>
【shell 脚本】两种登录方式
查看>>
学习编程的方法
查看>>
升级linux自带的Python
查看>>