Android蓝牙开发前,首先要区分是经典蓝牙开发还是BLE(低功耗)蓝牙开发,它们的开发是有区别的,如果还分不清经典蓝牙和BLE(低功耗)蓝牙的小伙伴,可以先看Android蓝牙开发—经典蓝牙和BLE(低功耗)蓝牙的区别

本文是针对BLE蓝牙开发的,如果是经典蓝牙开发,可以看Android蓝牙开发—经典蓝牙详细开发流程

注意:蓝牙4.0只有android4.3或4.3以上才支持

简单介绍

基本概念

1、Generic Access Profile(GAP)

用来控制设备连接和广播,GAP使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。

2、Generic Attribute Profile(GATT)

通过BLE连接,读写属性类数据的Profile通用规范,现在所有的BLE应用Profile都是基于GATT的。

3、Attribute Protocol (ATT)

GATT是基于ATTProtocol的,ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据,每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。

4、Characteristic

Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。

5、Descriptor

对Characteristic的描述,例如范围、计量单位等。

6、Service

Characteristic的集合。例如一个service叫做“Heart Rate Monitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart ratemeasurement”的Characteristic。

7、UUID

唯一标示符,每个Service,Characteristic,Descriptor,都是由一个UUID定义。

Android ble api

1、BluetoothManager

通过BluetoothManager来获取BluetoothAdapter。 

2、BluetoothAdapter

代表了移动设备的本地的蓝牙适配器, 通过该蓝牙适配器可以对蓝牙进行基本操作,一个Android系统只有一个BluetoothAdapter,通过BluetoothManager获取。 

3、BluetoothDevice

扫描后发现可连接的设备,获取已经连接的设备,通过它可以获取到BluetoothGatt。

4、BluetoothGatt

继承BluetoothProfile,通过BluetoothGatt可以连接设备(connect),发现服务(discoverServices),并把相应地属性返回到BluetoothGattCallback,可以看成蓝牙设备从连接到断开的生命周期。

5、BluetoothGattService

服务,Characteristic的集合。

6、BluetoothGattCharacteristic

相当于一个数据类型,可以看成一个特征或能力,它包括一个value和0~n个value的描述(BluetoothGattDescriptor)。

7、BluetoothGattDescriptor

描述符,对Characteristic的描述,包括范围、计量单位等。

8、BluetoothProfile

一个通用的规范,按照这个规范来收发数据。 

9、BluetoothGattCallback

已经连接上设备,对设备的某些操作后返回的结果。

简单总结:当我们扫描后发现多个设备BluetoothDevice,每个设备下会有很多服务BluetoothGattService,这些服务通过service_uuid(唯一标识符)来区分,每个服务下又会有很多特征BluetoothGattCharacteristic,这些特征通过uuid来区分的,它是手机与BLE终端设备交换数据的关键。而BluetoothGatt可以看成手机与BLE终端设备建立通信的一个管道,只有有了这个管道,才有了通信的前提。

开发流程

开发的流程和经典蓝牙是一样的,都需要以下这几个步骤。

  • 开启蓝牙
  • 扫描蓝牙
  • 绑定蓝牙
  • 连接蓝牙
  • 通信

开启蓝牙

1.获取BluetoothAdapter对象

这里有两种方法可以获取到BluetoothAdapter对象(区别在于版本不同而已,没有太大的区别)

第一种通过BluetoothManager对象获取

final BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();

第二种通过单例获取

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

2.判断是否有蓝牙功能模块

    if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {            //有蓝牙功能模块        }

3.判断设备是否支持蓝牙

/** * 设备是否支持蓝牙  true为支持 * @return */public boolean isSupportBlue(){    return mBluetoothAdapter != null;}

4.判断蓝牙是否开启

/** * 蓝牙是否打开   true为打开 * @return */public boolean isBlueEnable(){    return isSupportBlue() && mBluetoothAdapter.isEnabled();}

5.开启蓝牙

  • 异步自动开启蓝牙
/** * 自动打开蓝牙(异步:蓝牙不会立刻就处于开启状态) * 这个方法打开蓝牙不会弹出提示 */public void openBlueAsyn(){    if (isSupportBlue()) {        mBluetoothAdapter.enable();    }}
  • 同步提示开启蓝牙
/** * 自动打开蓝牙(同步) * 这个方法打开蓝牙会弹出提示 * 需要在onActivityResult 方法中判断resultCode == RESULT_OK  true为成功 */public void openBlueSync(Activity activity, int requestCode){    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);    activity.startActivityForResult(intent, requestCode);}

6.权限处理

  • 处理6.0以下版本的权限

    在AndroidManifest里面添加权限

  • 处理6.0及以上版本的权限

    (1)在AndroidManifest里面添加权限

    (2)动态检查权限

/** * 检查权限 */private void checkPermissions() {    String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};    List permissionDeniedList = new ArrayList<>();    for (String permission : permissions) {        int permissionCheck = ContextCompat.checkSelfPermission(this, permission);        if (permissionCheck == PackageManager.PERMISSION_GRANTED) {            onPermissionGranted(permission);        } else {            permissionDeniedList.add(permission);        }    }    if (!permissionDeniedList.isEmpty()) {        String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]);        ActivityCompat.requestPermissions(this, deniedPermissions, REQUEST_CODE_PERMISSION_LOCATION);    }}/** * 权限回调 * @param requestCode * @param permissions * @param grantResults */@Overridepublic final void onRequestPermissionsResult(int requestCode,                                             @NonNull String[] permissions,                                             @NonNull int[] grantResults) {    super.onRequestPermissionsResult(requestCode, permissions, grantResults);    switch (requestCode) {        case REQUEST_CODE_PERMISSION_LOCATION:            if (grantResults.length > 0) {                for (int i = 0; i < grantResults.length; i++) {                    if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {                        onPermissionGranted(permissions[i]);                    }                }            }            break;    }}

    (3)开启GPS

/** * 开启GPS * @param permission */private void onPermissionGranted(String permission) {    switch (permission) {        case Manifest.permission.ACCESS_FINE_LOCATION:            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !checkGPSIsOpen()) {                new AlertDialog.Builder(this)                        .setTitle("提示")                        .setMessage("当前手机扫描蓝牙需要打开定位功能。")                        .setNegativeButton("取消",                                new DialogInterface.OnClickListener() {                                    @Override                                    public void onClick(DialogInterface dialog, int which) {                                        finish();                                    }                                })                        .setPositiveButton("前往设置",                                new DialogInterface.OnClickListener() {                                    @Override                                    public void onClick(DialogInterface dialog, int which) {                                        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);                                        startActivityForResult(intent, REQUEST_CODE_OPEN_GPS);                                    }                                })                        .setCancelable(false)                        .show();            } else {                //GPS已经开启了            }            break;    }}

    (4)检查GPS是否开启

/** * 检查GPS是否打开 * @return */private boolean checkGPSIsOpen() {    LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);    if (locationManager == null)        return false;    return locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER);}

扫描蓝牙

1.扫描周围蓝牙设备(配对上的设备有可能扫描不出来)

调用BluetoothAdapter的startLeScan()方法来实现开始搜索。此方法时需要传入 BluetoothAdapter.LeScanCallback参数。搜索到的蓝牙设备都会通过这个回调返回。

    private BluetoothAdapter mBluetoothAdapter;    private boolean isScanning;//是否正在搜索    private Handler mHandler;    //15秒搜索时间    private static final long SCAN_PERIOD = 15000;    private void scanLeDevice(final boolean enable) {        if (enable) {//true            //15秒后停止搜索            mHandler.postDelayed(new Runnable() {                @Override                public void run() {                    isScanning = false;                    mBluetoothAdapter.stopLeScan(mLeScanCallback);                }            }, SCAN_PERIOD);            isScanning = true;            mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索        } else {//false            isScanning = false;            mBluetoothAdapter.stopLeScan(mLeScanCallback);//停止搜索        }    }

2.BluetoothAdapter.LeScanCallback回调

    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {        @Override        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {            //这里是个子线程,下面把它转换成主线程处理            runOnUiThread(new Runnable() {                @Override                public void run() {                    //在这里可以把搜索到的设备保存起来                    //device.getName();获取蓝牙设备名字                    //device.getAddress();获取蓝牙设备mac地址                    //这里的rssi即信号强度,即手机与设备之间的信号强度。                }            });        }    };

3.停止扫描设备

 扫描设备是一个耗能的操作,在我们连接蓝牙之前如果扫描没有结束,应该先停止扫描。

mBluetoothAdapter.stopLeScan(mLeScanCallback);//停止搜索

绑定蓝牙

绑定蓝牙是在我们调用连接方法时去绑定的,不用我们手动的去调方法绑定。

连接蓝牙

1.调用BluetoothDevice的connectGatt()方法,此函数带三个参数:Context、autoConnect(boolean)和 BluetoothGattCallback 对象。调用后返回BluetoothGatt对象

//这个方法需要三个参数:一个Context对象,自动连接(boolean值,表示只要BLE设备可用是否自动连接它),和BluetoothGattCallback调用。BluetoothGatt mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback);

2.BluetoothGattCallback的回调

BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {        @Override        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {            super.onPhyUpdate(gatt, txPhy, rxPhy, status);        }        @Override        public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {            super.onPhyRead(gatt, txPhy, rxPhy, status);        }        //当连接状态发生改变        @Override        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {            super.onConnectionStateChange(gatt, status, newState);        }        //发现新服务,即调用了mBluetoothGatt.discoverServices()后,返回的数据        @Override        public void onServicesDiscovered(BluetoothGatt gatt, int status) {            super.onServicesDiscovered(gatt, status);        }        //调用mBluetoothGatt.readCharacteristic(characteristic)读取数据回调,在这里面接收数据        @Override        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {            super.onCharacteristicRead(gatt, characteristic, status);        }        //发送数据后的回调        @Override        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {            super.onCharacteristicWrite(gatt, characteristic, status);        }        @Override        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {            super.onCharacteristicChanged(gatt, characteristic);        }        @Override        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//descriptor读            super.onDescriptorRead(gatt, descriptor, status);        }        @Override        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//descriptor写            super.onDescriptorWrite(gatt, descriptor, status);        }        @Override        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {            super.onReliableWriteCompleted(gatt, status);        }        //调用mBluetoothGatt.readRemoteRssi()时的回调,rssi即信号强度        @Override        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {//读Rssi            super.onReadRemoteRssi(gatt, rssi, status);        }        @Override        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {            super.onMtuChanged(gatt, mtu, status);        }    };

3.断开连接

mBluetoothGatt.disconnect(); //主动断开连接

通信

1.写入数据,通过mBluetoothGatt.writeCharacteristic(characteristic)

/** * 向蓝牙发送数据 */public void dataSend(){    //byte[] send={(byte) 0xaa,0x01,0x01,(byte)0x81,(byte) 0xff};    byte[] send = new byte[20];    send = hexStringToBytes(et_send.getText().toString());    byte[] sendData=new byte[send.length+2];    sendData[0]=(byte) 0xaa;    sendData[sendData.length-1]=(byte) 0xff;    for(int i=1;i

注意:写入的数据长度是有限制的,如果要改变这个限制,可以通过MTU来改变。

2.读取数据,通过mBluetoothGatt.readCharacteristic(characteristic),在BluetoothGattCallback 回调方法onCharacteristicRead中获取到数据。

//读取数据时调用这个方法,数据会在回调接口中(BluetoothGattCallback )获取到mBluetoothGatt.readCharacteristic(characteristic)BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {             //调用mBluetoothGatt.readCharacteristic(characteristic)读取数据回调,在这里面接收数据        @Override        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {            super.onCharacteristicRead(gatt, characteristic, status);            //这里面就是数据            characteristic.getValue();        }}

总结

首先,我们要判断手机是否支持BLE,并且获得各种权限,才能让我们之后的程序能正常运行。 
然后,我们去搜索BLE设备,得到它的MAC地址。 
其次,我们通过这个MAC地址去连接,连接成功后,去遍历得到Characteristic的uuid。 
在我们需要发送数据的时候,通过这个uuid找到Characteristic,去设置其值,最后通过writeCharacteristic(characteristic)方法发送数据。 
如果我们想知道手机与BLE设备的距离,则可以通过readRemoteRssi()去得到rssi值,通过这个信号强度,就可以换算得到距离。 
只要我们连接上,我们就可以用BluetoothGatt的各种方法进行数据的读取等操作。

 

以上就是BLE(低功耗)蓝牙的开发流程和部分代码,后期会提供demo下载。若有不当之处,请留言讨论,一起学习进步。

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. python起点网月票榜字体反爬案例
  3. 《Android开发从零开始》——25.数据存储(4)
  4. Android系统配置数据库注释(settings.db)
  5. Android中不同应用间实现SharedPreferences数据共享
  6. android 获取唯一标识
  7. android图表ichartjs
  8. Android内容提供者源码
  9. android SharedPreferences

随机推荐

  1. Android xml manifest属性详解
  2. 相对布局属性
  3. Android:实现无标题的两种方法
  4. android 小游戏 ---- 数独(四)
  5. --android bitmap oom 分析
  6. Android 修改程序字体
  7. Android布局管理器总结
  8. 项目中那些事|控件之TextView
  9. EditText光标居上
  10. 《第一行代码 Android(安卓)》学习记录(一