android 低功耗蓝牙

谷歌官方文档:
https://developer.android.google.cn/guide/topics/connectivity/bluetooth-le.html
谷歌官方demo:
https://github.com/googlesamples/android-BluetoothLeGatt/
参考:
http://blog.csdn.net/chenfengdejuanlian/article/details/45787123
http://blog.csdn.net/z957250254/article/details/52411556

第一次接触蓝牙方面的知识,仅此记录,大家多多交流啊

先按照下图走一遍流程
这里写图片描述

检查、开启权限

开启蓝牙权限

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <!-- 声明此应用仅适用于具有低功耗蓝牙设备,若想适用于不支持BLE的设备,required设置为false -->
    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true" />

检查设备是否支持低功耗蓝牙

if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, "此设备不支持BLE(低功耗蓝牙)", Toast.LENGTH_LONG).show();
            finish();
        }

需要注意的是还要开启位置权限
这里写图片描述
在manifest添加

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

并在activity中动态申请权限(本例使用RxPermission请求权限)

    /**
     * 请求定位权限
     */
    private void checkPermission() {
        new RxPermissions((Activity) mContext).request(Manifest.permission.ACCESS_COARSE_LOCATION)
                .subscribe(new Observer<Boolean>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(Boolean granted) {
                        if (granted) {
                            initBlueTooth();
                        } else {
                            // 未获取权限
                            Toast.makeText(mContext, "未获取定位权限", Toast.LENGTH_LONG).show();
                        }
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

搜索蓝牙设备

搜索蓝牙设备是需要一个BluetoothAdapter来扫描设备的,这里又要进行设备是否支持蓝牙功能的判断和是否开启蓝牙(若未开启,提示用户或强制开启蓝牙)

private void initBlueTooth() {
        //BluetoothAdapter代表设备自己的蓝牙适配器(蓝牙无线电)
        BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

        //设备是否支持蓝牙
        if (mBluetoothAdapter == null) {
            Toast.makeText(mContext, "该设备不支持蓝牙功能", Toast.LENGTH_LONG).show();
            finish();
        }

        //蓝牙是否启用,若未启用,请求用户启用
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            return;
        }

        //若开启蓝牙,直接扫描低功耗蓝牙设备;若未开启,提示用户开启,用户开启后在onActivityResult开启扫描
        scanLeDevice(true);
    }

扫描或关闭扫描

注意:扫描不能一直进行,本例SCAN_PERIOD=10000毫秒后停止扫描

     /**
     * 扫描低功耗蓝牙设备
     * 您只能扫描蓝牙LE设备或扫描经典蓝牙设备,如蓝牙中所述  您无法同时扫描Bluetooth LE和传统设备。
     *
     * @param enable
     */
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            //Because scanning is battery-intensive, you should observe the following guidelines:
            //As soon as you find the desired device, stop scanning.找到所需设备停止扫描
            //Never scan on a loop, and set a time limit on your scan.切勿扫描循环,并在扫描上加上时间限制
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                    mTextView.setText("扫描结束");
                    invalidateOptionsMenu();
                }
            }, SCAN_PERIOD);

            mScanning = true;
            //如果只想扫描特定类型的外设,则可以改为调用startLeScan(UUID [],BluetoothAdapter.LeScanCallback)
            //提供指定您的应用程序支持的GATT服务的UUID对象数组
            mBluetoothAdapter.startLeScan(mLeScanCallback);
            mTextView.setText("扫描中...");
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
            mTextView.setText("扫描结束");
        }
        invalidateOptionsMenu();
    }

mBluetoothAdapter.startLeScan()的参数mLeScanCallback是扫描结果的回调

mLeScanCallback =
                new BluetoothAdapter.LeScanCallback() {
                    @Override
                    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                if (device != null) {
                                    //过滤同一设备
                                    if (!mDevices.contains(device)) {
                                        //将设备添加到列表中
                                        mDeviceListAdapter.addData(device);
                                    }
                                }
                            }
                        });
                    }
                };

其中mDeviceListAdapter为列表适配器,mDevices为列表适配器中的数据,若不进行同一设备的过滤,会返回很多相同设备

连接ble设备

本demo是基于谷歌的示例代码,就直接将谷歌连接设备的相关类拷过来了,这里直接对其分析

在activity中绑定一个BluetoothLeService服务,此服务是用于对指定蓝牙设备进行连接或数据通信的服务

        Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
        bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);

其中mServiceConnection管理BluetoothLeService服务的生命周期

    /**
     * 管理蓝牙服务的生命周期
     */
    private final ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
            if (!mBluetoothLeService.initialize()) {
                Log.e(TAG, "Unable to initialize Bluetooth");
                finish();
            }
            //成功启动初始化后自动连接到设备
            mBluetoothLeService.connect(mDeviceAddress);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBluetoothLeService = null;
        }
    };

在连接设备之前,要判断设备是否支持低功耗蓝牙

public boolean initialize() {
        // For API level 18 and above, get a reference to BluetoothAdapter through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                Log.e(TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }

        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
            return false;
        }

        return true;
    }

BluetoothLeService绑定成功后,将要连接的设备地址传入,在service中连接设备

    /**
     * 连接到Bluetooth LE设备上托管的GATT服务器
     *
     * @param address 设备地址
     * @return 返回是否连接是否成功启动(注意:是启动,并不是连接结果)
     * 连接结果是在BluetoothGattCallback onConnectionStateChange异步返回的
     */
    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        // 以前连接的设备,尝试重连
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null) {
            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
            if (mBluetoothGatt.connect()) {
                mConnectionState = STATE_CONNECTING;
                return true;
            } else {
                return false;
            }
        }
        //通过传入的设备地址获取设备
        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        // 我们要直接连接到设备,所以我们正在设置autoConnect参数为false (此处autoConnect为true可能不是立即连接)
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        Log.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

其中device.connectGatt()的第三个参数mGattCallback为蓝牙设备与GATT服务端连接和数据通信的监听,此方法的返回值mBluetoothGatt 稍后介绍

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        //回调指示何时GATT客户端连接到远程GATT服务器/从远程GATT服务器断开连接
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String intentAction;
            //连接成功
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                Log.i(TAG, "Connected to GATT server.");
                // 连接成功后尝试发现服务
                Log.i(TAG, "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
                Log.i(TAG, "Disconnected from GATT server.");
                broadcastUpdate(intentAction);
            }
        }

        //发现新服务时调用回调
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        //回调报告特征性读取操作的结果
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }

        //由于远程特征通知而触发的回调
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }
    };

其中,连接成功、断开或发现服务或数据通信的回调均发送了广播,在activity中接收广播进行管理,这里先不关注每一个方法,尽量从一个流程分析

看到onConnectionStateChange回调中,判断状态为连接成功,调用了如下代码发现服务

mBluetoothGatt.discoverServices()

注意:

上文中mBluetoothGatt相当于手机与设备通信的管道
通过BluetoothGatt

  • 发现服务(discoverServices)
  • 获取服务(getServices)
  • 开启指定指定特征的通知(setCharacteristicNotification)
  • 对指定特征写入数据(writeCharacteristic)
  • 把相应地属性返回到BluetoothGattCallback

demo中发现服务后发送广播到activity中,在activity中接收广播调用service(此service为BluetoothLeService,是自己写的对指定蓝牙设备进行连接或数据通信的服务)的获取服务(此处的服务为**蓝牙设备所拥有的服务**BluetoothGattService)方法getServices,然后在activity中对服务进行遍历,并存储每个服务下的特征值

此处分析下流程:调用mBluetoothGatt.discoverServices方法后,发现服务后会回调给BluetoothGattCallback的onServicesDiscovered,在调用mBluetoothGatt.getServices()返回一个包含BluetoothGattService服务的集合,再对集合进行遍历,获取每一个BluetoothGattService服务的特征

注意:(这里有点绕)

  • BluetoothLeService 自己写的对指定蓝牙设备进行连接或数据通信的服务
  • BluetoothGattService 蓝牙设备所拥有的服务
  • BluetoothGattCharacteristic 某一BluetoothGattService的某一特征
  • BluetoothGattDescriptor 某一BluetoothGattCharacteristic下的属性,用来描述characteristic变量的属性

下面这张图为BluetoothGattService 和 BluetoothGattCharacteristic 和 BluetoothGattDescriptor的关系

这里写图片描述

遍历服务(这里尤其绕)使用了ExpandableListView展示服务和服务下的特征

private void displayGattServices(List<BluetoothGattService> gattServices) {
        if (gattServices == null) return;
        String uuid = null;
        String unknownServiceString = getResources().getString(R.string.unknown_service);
        String unknownCharaString = getResources().getString(R.string.unknown_characteristic);
        ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>();
        ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
                = new ArrayList<ArrayList<HashMap<String, String>>>();
        mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

        //遍历所有的服务
        for (BluetoothGattService gattService : gattServices) {
            //每一个服务的name 和 UUID保存在map中
            HashMap<String, String> currentServiceData = new HashMap<String, String>();
            uuid = gattService.getUuid().toString();
            currentServiceData.put(
                    LIST_NAME, SampleGattAttributes.lookup(uuid, unknownServiceString));
            currentServiceData.put(LIST_UUID, uuid);
            //将 每一个 包含服务信息的map 都添加到集合中
            gattServiceData.add(currentServiceData);

            ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
                    new ArrayList<HashMap<String, String>>();
            List<BluetoothGattCharacteristic> gattCharacteristics =
                    gattService.getCharacteristics();
            ArrayList<BluetoothGattCharacteristic> charas =
                    new ArrayList<BluetoothGattCharacteristic>();

            //遍历服务中的特征
            for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                //将此服务中的特征添加到集合中
                charas.add(gattCharacteristic);

                //将此特征的UUID和name保存在map中
                HashMap<String, String> currentCharaData = new HashMap<String, String>();
                uuid = gattCharacteristic.getUuid().toString();
                currentCharaData.put(
                        LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString));
                currentCharaData.put(LIST_UUID, uuid);

                //将 每一个 包含特征信息的map 都添加到集合中
                gattCharacteristicGroupData.add(currentCharaData);
            }
            //将  每个服务中包含的所有特征的集合  添加到总集合中
            mGattCharacteristics.add(charas);
            //将  每个服务中包含的所有特征信息的集合  添加到总集合中
            gattCharacteristicData.add(gattCharacteristicGroupData);
        }

        SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(
                this,
                gattServiceData,
                android.R.layout.simple_expandable_list_item_2,
                new String[]{LIST_NAME, LIST_UUID},
                new int[]{android.R.id.text1, android.R.id.text2},
                gattCharacteristicData,
                android.R.layout.simple_expandable_list_item_2,
                new String[]{LIST_NAME, LIST_UUID},
                new int[]{android.R.id.text1, android.R.id.text2}
        );
        mGattServicesList.setAdapter(gattServiceAdapter);
    }

上面的代码如果不太明白可以慢慢看的,先看下面的
我们只需要知道,点击ExpandableListView group为此设备下的所有service,点击group会展开此service下的所有特征

/**
     * ExpandableListView 子条目点击监听 点击条目,会把正在通信的特征关闭通知,再把点击条目的特征打开通知
     */
    private final ExpandableListView.OnChildClickListener servicesListClickListener =
            new ExpandableListView.OnChildClickListener() {
                @Override
                public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
                                            int childPosition, long id) {
                    if (mGattCharacteristics != null) {
                        final BluetoothGattCharacteristic characteristic =
                                mGattCharacteristics.get(groupPosition).get(childPosition);
                        /**
                         * 正常流程
                         * 打开  指定接收特征  (指定UUID)通知
                         * 在  指定写入特征  写入数据
                         */
                        //从指定UUID的特征打开接收(测试用)
                        if (characteristic.getUuid().toString().equals("0000ff02-0000-1000-8000-00805f9b34fb")) {
                            mBluetoothLeService.setCharacteristicNotification(
                                    characteristic, true);

                        }
//                        //从指定UUID的特征写入(测试用)
                        if (characteristic.getUuid().toString().equals("0000ff01-0000-1000-8000-00805f9b34fb")) {
                            mBluetoothLeService.write(characteristic);
                        }
                        return true;
                    }
                    return false;
                }
            };

本例中
UUID为0000ff02-0000-1000-8000-00805f9b34fb的特征为读取特征
UUID为0000ff01-0000-1000-8000-00805f9b34fb的特征为写入特征

点击0000ff02-0000-1000-8000-00805f9b34fb条目打开读取通知

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

点击0000ff01-0000-1000-8000-00805f9b34fb条目写入数据

//写入数据(测试用)
    public void write(BluetoothGattCharacteristic characteristic) {
        //握手
        characteristic.setValue(hexStringToByteArray("A2"));
        mBluetoothGatt.writeCharacteristic(characteristic);
        //握手和命令不能超过4秒,但又不能马上
        SystemClock.sleep(500);
        //命令
        characteristic.setValue("SINSAM");
        mBluetoothGatt.writeCharacteristic(characteristic);
    }

数据写入后,就会在mGattCallback的onCharacteristicRead回调里通过characteristic.getValue()拿到数据了
ok基本总结完了

源码点我

最后一张美图镇楼。。(按照自己对低功耗蓝牙的理解画的图)
这里写图片描述

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 低功耗蓝牙是在传统蓝牙的基础上开发的一种蓝牙版本,它与传统蓝牙模块不同,最大的特点是降低了成本和功耗。低功耗蓝牙可以快速搜索并快速连接设备,并以超低功耗传输数据。它是专门为物联网设备设计的,可以延长电池寿命。目前,低功耗蓝牙技术已广泛应用于耳机、手环、电子秤、鼠标、键盘、灯、音箱等设备。\[2\] 要开发Android低功耗蓝牙应用,有一些细节需要注意。首先,你需要了解Android蓝牙开发的基础知识。如果之前没有接触过Android蓝牙开发,建议先学习相关知识。其次,你需要确定蓝牙设备的固件是公司自己的还是第三方提供的。如果是公司自己的固件,你可能需要与固件开发人员合作。此外,你还需要蓝牙固件和蓝牙应用的文档和Demo来帮助你进行开发。确保你对业务功能需求有明确的了解,这样才能更好地设计和实现应用。如果是接手老项目,你需要评估是否需要重新编写应用。最后,你需要确定应用是否需要上架,以及需要上架的平台数量。\[1\] 在具体的开发过程中,你可以使用Android的BluetoothLeScanner类来进行蓝牙设备的搜索。通过ScanCallback和ScanResult类,你可以处理搜索到的设备信息。对于与设备的连接和数据传输,你可以使用BluetoothGatt和BluetoothGattCallback类来实现。这些类提供了与低功耗蓝牙设备进行通信的方法和回调函数。\[3\] #### 引用[.reference_title] - *1* *2* *3* [Android 低功耗蓝牙开发简述](https://blog.csdn.net/qq_38436214/article/details/126866948)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v4^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值