zoukankan      html  css  js  c++  java
  • Android蓝牙——HID开发

    代码地址如下:
    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开始支持的,如下图所示:

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

    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 typewriter
    add 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。
    true

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

    Android蓝牙——HID开发

    代码地址如下:
    http://www.demodashi.com/demo/13891.html

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

  • 相关阅读:
    [Redis知识体系] 一文全面总结Redis知识体系
    MySQL数据导入到ClickHouse
    docker本地搭建clickhouse
    【linux】修改宝塔默认的PHP CLI版本
    windows 10 安装go环境
    docker安装centos8
    Bootstrap 简洁、直观、强悍的前端开发框架,让web开发更迅速、简单。
    C#调用WebService
    登录时,记住用户的帐号密码
    asp.net,cookie,写cookie,取cookie
  • 原文地址:https://www.cnblogs.com/demodashi/p/9582536.html
Copyright © 2011-2022 走看看