Android低功耗蓝牙学习记录

前言

舍友是做小鸣单车app的,周末有空,找他了解一下蓝牙开锁的实现过程,整理成了一个简单的demo,方便以后做蓝牙硬件方面的项目。

蓝牙api简介:

BluetoothAdapter
BluetoothAdapter 拥有基本的蓝牙操作,例如开启蓝牙扫描,使用已知的 MAC 地址 (BluetoothAdapter#getRemoteDevice)实例化一个 BluetoothDevice 用于连接蓝牙设备的操作等等。

BluetoothDevice
代表一个远程蓝牙设备。这个类可以让你连接所代表的蓝牙设备或者获取一些有关它的信息,例如它的名字,地址和绑定状态等等。

BluetoothGatt
这个类提供了 Bluetooth GATT 的基本功能。例如重新连接蓝牙设备,发现蓝牙设备的 Service 等等。

BluetoothGattService
这一个类通过 BluetoothGatt#getService 获得,如果当前服务不可见那么将返回一个 null。这一个类对应上面说过的 Service。我们可以通过这个类的 getCharacteristic(UUID uuid) 进一步获取 Characteristic 实现蓝牙数据的双向传输。

BluetoothGattCharacteristic
这个类对应上面提到的 Characteristic。通过这个类定义需要往外围设备写入的数据和读取外围设备发送过来的数据。

开发步骤:

一、声明权限
    <!--使用蓝牙所需要的权限-->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <!--使用扫描和设置蓝牙的权限(申明这一个权限必须申明上面一个权限)-->
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <!--在Android5.0之前,是默认申请GPS硬件功能的。而在Android 5.0 之后,需要在manifest 中申明GPS硬件模块功能的使用。-->
    <uses-feature android:name="android.hardware.location.gps" />
    <!--在 Android 6.0 及以上,还需要打开位置权限。如果应用没有位置权限,蓝牙扫描功能不能使用(其它蓝牙操作例如连接蓝牙设备和写入数据不受影响)。-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
二、蓝牙初始化
       // 判断是否支持蓝牙
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, "您的设备不支持蓝牙4.0", Toast.LENGTH_SHORT).show();
            finish();
        }

        final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

        mBluetoothAdapter = bluetoothManager.getAdapter();

        // 判断是否开启蓝牙,没有就开启
        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivity(enableBtIntent);
        }
三、扫描和连接蓝牙
               // 开始扫描蓝牙,获取蓝牙对象
                mBluetoothAdapter.startLeScan(new BluetoothAdapter.LeScanCallback() {
                    @Override
                    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                        Log.e("=====", "扫秒到的设备------" + device.getAddress());
                        //下面这个mac地址是固定硬件的,做项目的时候需要按需修改
                        if (device.getAddress().equals("50:8C:B1:5A:42:F7")) {
                            // 停止蓝牙扫描,扫描耗资源,不能一直扫
                            mBluetoothAdapter.stopLeScan(this);
                            // 开始连接蓝牙设备
                            mBluetoothGatt = device.connectGatt(MainActivity.this, false, mGattCallback);
                            Log.e("===========", "开始连接.....");
                        }
                    }
                });

回调方法中,第一个参数是代表蓝牙设备的类,可以通过这个类建立蓝牙连接获取关于这一个设备的一系列详细的参数,例如名字,MAC 地址等等;第二个参数是蓝牙的信号强弱指标,通过蓝牙的信号指标,我们可以大概计算出蓝牙设备离手机的距离。计算公式为:d = 10^((abs(RSSI) - A) / (10 * n));第三个参数是蓝牙广播出来的广告数据。

四、监听蓝牙回调,操作读写数据
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        // 蓝牙连接状态回调
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            // 连接成功,调用discoverServices()发现服务
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.w("TAG", "connected");
                gatt.discoverServices();

            }
            // 连接断开,调用close()释放资源
            else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.w("TAG", "disconnetce");
                mBluetoothGatt.close();
                mBluetoothGatt = null;
            }
        }

        // 发现服务回调
        // 到这一步,我们已经成功和蓝牙设备建立了可通信的连接,接下来就可以执行相应的蓝牙通信操作了,
        // 例如写入数据,读取蓝牙设备的数据等等
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.w("TAG", "onServicesDiscovered");

            if (status == BluetoothGatt.GATT_SUCCESS) {
                readBatrery();  //读取电量操作
                sendSetting(); //下发配置值

                // BLE app通常需要获取设备中characteristic 变化的通知
                // 其实就是一般我们都是通过手机向设备读写数据,但是如果想让设备数据改变时,主动往手机发送数据,就需要

                // 1.开启 Android 端接收通知的开关
                gatt.setCharacteristicNotification(readCharacteristic, true);
                // 2.往 Characteristic 的 Descriptor 属性写入开启通知的数据开关
                BluetoothGattDescriptor descriptor = readCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                gatt.writeDescriptor(descriptor);

                // 小鸣蓝牙锁就是这种情况,往设备写入数据之后,设备不会立马返回结果,
                // 在执行完操作之后,通过onCharacteristicChanged主动上报的方式通知app

                // 注意:上面这部不是必须的,视硬件而言
            }
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {

        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            //读取到值,根据UUID来判断读到的是什么值
            if (characteristic.getUuid().toString().equals("00002a19-0000-1000-8000-00805f9b34fb")) {
                // 获取到电量
                int battery = characteristic.getValue()[0];
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //write成功(发送值成功),可以根据 characteristic.getValue()来判断是哪个值发送成功了
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            // 收到设备notify值 (设备上报值),进行自己的操作
            byte[] values = characteristic.getValue();

        }
    };

    void readBatrery() {
        //此处的0000180f...是举例,实际开发需要询问硬件那边
        BluetoothGattService batteryService = mBluetoothGatt.getService(UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb"));
        if (batteryService != null) {
            //此处的00002a19...是举例,实际开发需要询问硬件那边
            BluetoothGattCharacteristic batteryCharacteristic = batteryService.getCharacteristic(UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb"));
            if (batteryCharacteristic != null) {
                //读取电量, 这是读取batteryCharacteristic值的方法,读取其他的值也是如此,只是它们的ServiceUUID 和CharacUUID不一样
                mBluetoothGatt.readCharacteristic(batteryCharacteristic);
            }
        }
    }

    void sendSetting() {
        //此处的00001805...是举例,实际开发需要询问硬件那边
        BluetoothGattService sendService = mBluetoothGatt.getService(UUID.fromString("00001805-0000-1000-8000-00805f9b34fb"));
        if (sendService != null) {
            //此处的00002a08...是举例,实际开发需要询问硬件那边
            BluetoothGattCharacteristic sendCharacteristic = sendService.getCharacteristic(UUID.fromString("00002a08-0000-1000-8000-00805f9b34fb"));
            if (sendCharacteristic != null) {
                //随便举个数据
                sendCharacteristic.setValue(new byte[]{0x01, 0x20, 0x03});
                //写命令到设备,
                mBluetoothGatt.writeCharacteristic(sendCharacteristic);
            }
        }
    }

这样就大概能完成手机对蓝牙硬件的通信,可以淘宝买一些蓝牙硬件,然后来调试看看。
部分资料参考:Android BLE 蓝牙开发入门

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容