android Ble通信

本文详细介绍BLE(Bluetooth Low Energy)蓝牙4.0技术,包括其特点、应用场景及Android平台上的开发流程。涵盖BLE的基本概念如GAP和GATT,以及如何在Android上实现BLE设备的搜索、连接与数据交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.BLE介绍

BLE是Bluetooth Low Energy的缩写,又叫蓝牙4.0,区别于蓝牙3.0和之前的技术.BLE前身是NOKIA开发的Wibree技术,主要用于实现移动智能终端与周边配件之间的持续连接,是功耗极低的短距离无线通信技术,并且有效传输距离被提升到了100米以上,同时只需要一颗纽扣电池就可以工作数年之久.BLE是在蓝牙技术的基础上发展起来的,同同于蓝牙,又区别于传统蓝牙.BLE设备分单模和双模两种,双模简称BR,商标为蓝牙Smart Ready,单模简称BLE或者LE,商标为Bluetooth Smart.Android是在4.3后才支持BLE,这说明不是所有蓝牙手机都支持BLE,而且支持BLE的蓝牙手机一般是双模的。双模兼容传统蓝牙,可以和传统蓝牙通信,也可以和BLE通信,常用在手机上,android4.3和IOS4.0之后版本都支持BR,也就是双模设备。单模只能和BR和单模的设备通信,不能和传统蓝牙通信,由于 功耗低,待机长,所以常用在手环的智能设备上。


二.基本概念

1.通用访问配置文件:(GAP)用来控制设备连接和广播,GAP使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。
2.通用属性配置文件:(GATT)通过BLE连接,读写属性类数据的简介通用规范,现在所有的BLE应用资料都是基于总协定的。
3.属性协议:(ATT)总协定是基于ATTProtocol的,ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据,每个属性都有一个唯一的UUID,属性将以特征和服务的形式传输。特征特征可以理解为一个数据类型,它包括一个值和0至多个对次值的描述(描述符)。

4.Descriptor:对Characteristic的描述,例如范围、计量单位等。
1. Service:
Characteristic的集合。例如一个service叫做“Heart Rate Monitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart ratemeasurement”的Characteristic。
2. UUID:
唯一标示符,每个Service,Characteristic,Descriptor,都是由一个UUID定义。


三,Android BLE API

  1. BluetoothManager:通过BluetoothManager来获取BluetoothAdapter.BluetoothManager bluetoothManager =(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
  2. BluetoothAdapter:代表了移动设备的本地的蓝牙适配器,通过该蓝牙适配器可以对蓝牙进行基本操作,一个Android系统只有一个BluetoothAdapter,通过BluetoothManager获取.BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
  3. BluetoothDevice:扫描后发现可连接的设备,获取已经连接的设备.BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
  4. BluetoothGatt:继承蓝牙规范,通过BluetoothGatt可以连接设备(连接),发现服务(discoverServices),并把相应地属性返回到BluetoothGattCallback,可以看成蓝牙设备从连接到断开的生命周期
  5. BluetoothGattCharacteristic:相当于一个数据类型,可以看成一个特征或能力,它包括一个值和0~n个值的描述(BluetoothGattDescriptor)。
  6. BluetoothGattDescriptor: 描述符,对特征的描述,包括范围,计量单位等。
  7. BluetoothGattService: 服务,特征的集合。
  8. BluetoothGattCallback:已经连接上设备,对设备的某些操作后返回的结果。- onConnectionStateChange:指示GATT客户端何时与远程GATT服务器连接/断开连接的回调。

四,操作流程

google GitHub例子连接BluetoothLe
google Developers蓝牙SDK参考
首先权限不能忘:

uses-permission android:name =“android.permission.ACCESS _ COARSE _ LOCATION”/>
uses-permission android:name=”android.permission.BLUETOOTH” />
uses-permission android:name=”android.permission.BLUETOOTH_ADMIN” />

除了蓝牙权限外,如果需要BLE feature则还需要声明uses-feature:

uses-feature android:name=”android.hardware.bluetooth_le” android:required=”true” />

检查当前手机是否支持ble 蓝牙,如果不支持退出程序

        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
            finish();
        }

        //初始化 Bluetooth adapter, 通过蓝牙管理器得到一个参考蓝牙适配器(API必须在以上android4.3或以上和版本)
        final BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);

        // 检查设备上是否支持蓝牙
        mBluetoothAdapter = bluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
            finish();
            return;
        }

        //如果 API level 是大于等于 23(Android 6.0) 时判断是否具有权限,自Android 6.0开始需要打开位置权限才可以搜索到Ble设备
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            //判断是否具有权限
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
                //请求权限
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},REQUEST_CODE_ACCESS_COARSE_LOCATION);
            }
        }

        Boolean result =isLocationEnable(this);
        if (result){
            Log.e(TAG, "用户以打开定位功能===");
        }else {
            setLocationService();
        }
    }

执行完上面的请求权限后,系统会弹出提示框让用户选择是否允许改权限。选择的结果可以在回到接口中得知:

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == REQUEST_CODE_ACCESS_COARSE_LOCATION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //用户允许改权限,0表示允许,-1表示拒绝 PERMISSION_GRANTED = 0, PERMISSION_DENIED = -1
                //permission was granted, yay! Do the contacts-related task you need to do.
                //这里进行授权被允许的处理
                Log.e(TAG, "允许定位权限===");
            } else {
                //permission denied, boo! Disable the functionality that depends on this permission.
                //这里进行权限被拒绝的处理
                Log.e(TAG, "不允许定位权限===");
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

 //判断定位
    public final boolean isLocationEnable(Context context) {
        LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        boolean networkProvider = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        boolean gpsProvider = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        if (networkProvider || gpsProvider) {
            return true;
        }

        return false;
    }

    private void setLocationService() {
        Intent locationIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
        startActivityForResult(locationIntent, REQUEST_CODE_LOCATION_SETTINGS);
    }

执行完上面的请求权限后回调:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // 用户没有开启蓝牙
        if (requestCode == REQUEST_CODE_LOCATION_SETTINGS) {
            if (isLocationEnable(this)) {
                //定位已打开的处理
                Toast.makeText(this, "定位已经打开", Toast.LENGTH_SHORT).show();
            }
            else {
                //定位依然没有打开的处理
                Toast.makeText(this, "定位没有打开", Toast.LENGTH_SHORT).show();
            }
        }
        else if (requestCode == REQUEST_ENABLE_BT && resultCode == RESULT_CANCELED) {
            finish();
            return;
        }

        super.onActivityResult(requestCode, resultCode, data);
    }
五、设备搜索
  • BluetoothAdapter.startDiscovery在大多数手机上是可以同时发现经典蓝牙和低功耗蓝牙,但是startDiscovery的回调无法返回低功耗蓝牙提取的广播,所以无法通过广播识别设备,且startDiscovery扫描低功耗蓝牙提取的效率比StartLeScan低很多。所以在实际应用中,还是StartDiscovery和StartLeScan分开扫,前者扫传统蓝牙,后者扫低功耗蓝牙。

  • 由于搜索需要尽量减少功耗,因此在实际使用时需注意:当找到对应的设备后,立即停止扫描;不要循环搜索设备,为每次搜索设置适合的时间限制,避免设备不在可用范围的时候持续不停扫描,消耗电量。

  • 通过调用BluetoothAdapter的startLeScan()搜索BLE设备。调用此方法时需要传入BluetoothAdapter .LeScanCallback参数。具体代码示例如下:

/**
     * Device scan callback.
     * BluetoothAdapter.LeScanCallback :public static interface
     * onLeScan:Callback reporting an LE device found during a device scan initiated by the startLeScan(BluetoothAdapter.LeScanCallback) function.
     */
    private final BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mLeDeviceListAdapter.addDevice(device);
                mLeDeviceListAdapter.notifyDataSetChanged();
            }
        });
    }

/**
     * BluetoothGattCallback: public abstract class
     * onConnectionStateChange:Callback indicating when GATT client has connected/disconnected to/from a remote GATT server.
     * onServicesDiscovered:Callback invoked when the list of remote services, characteristics and descriptors for the remote device have been updated, ie new services have been discovered.
     * onCharacteristicChanged:Callback triggered as a result of a remote characteristic notification.
     * onCharacteristicRead:Callback reporting the result of a characteristic read operation.
     * onCharacteristicWrite:Callback indicating the result of a characteristic write operation.
     */
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback(){

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            Log.i(TAG, "onConnectionStateChange.newState = " + newState);
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                Log.i(TAG, "Connected to GATT server.");
                // Attempts to discover services after successful connection.
                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) {
            super.onServicesDiscovered(gatt, status);
            Log.i(TAG, "onServicesDiscovered.");

            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            Log.i(TAG, "onCharacteristicChanged.");

            //broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            Log.i(TAG, "onCharacteristicRead.status = " + status);

            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }

        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            Log.i(TAG, "onCharacteristicWrite.status = " + status);

            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }
    };
/**
     * Connects to the GATT server hosted on the Bluetooth LE device.
     *
     * @param address The device address of the destination device.
     *
     * @return Return true if the connection is initiated successfully. The connection result
     *         is reported asynchronously through the
     *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     *         callback.
     */
    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        // Previously connected device.  Try to reconnect.
        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;
        }
        // We want to directly connect to the device, so we are setting the autoConnect
        // parameter to false.
        //Connect to GATT Server hosted by this device.
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        Log.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值