android BLE4.0开发实现与蓝牙设备对接

前言:

      不知不觉一年又要到头了,每天上班,吃饭,睡觉三点一线的匆匆而过。最近一个同事买房了,年龄跟我差不多,想想现在的房价,现在的消费,再看下自己的月薪,完全不敢想象自己能在这个城市奋斗到什么样子。有时候在想自己是不是应该换行了,但是换行自己能做什么?适合做什么?这些感觉在脑海里面都是模糊的,想想都觉得自卑。

     再说说目前android开发行业吧,确实现在移动端开发大大不如前几年了,各行各业的竞争逐渐变大,想要在行业里面脱颖而出,那必定是精英干将了,就android版本不断更新,技术不断优化,导致了大多数人跟不上节奏,随后而慢慢被抛在后面(我也是菜鸟),其实说这些我个人感觉就是多余的,只要做这个行业的都知道这个行情,总之一句话:对得起这份工作,对得起自己!

1.Ble4.0开发

     对于蓝牙开发从开始入行就听说过,而真正应用到项目上很少,其实蓝牙开发流程就那么几点:

  1. 检查权限(蓝牙扫描的需要加定位权限,android6.0以上需要手动获取权限)
  2. 判断设备是否支持蓝牙(移动设备)
  3. 打开蓝
  4. 打开蓝牙后可以进行扫描了(蓝牙扫描最好不要放在oncreat()里面)
  5. 监听扫描结果,进行连接
  6. 连接回调获取到gatt状态,连接状态,断开状态,读写通知特性等(在获取设备特性的时候我们需要对其设置uuid下面代码中会提到)

   对于这套流程我就列出我所遇到的坑,希望各位网友能帮忙看下:

  1. 时不时扫描不到设备
  2. 读写特性不回调

一套蓝牙从打开到对接大概就这些流程吧,当然其中有很多细节问题需要我们注意的,这里我就不列出来了,大家有什么问题,或者疑问可以提出来讨论,下面我就列出我最近写的一套ble4.0代码,特别提出如果有不好或者疑问的欢迎提出:

2.从打开蓝牙到对接数据

       1.对于每一个功能开发我们应当首先想到的就是权限问题,对于android6.0以上对于权限问题更加严谨,需要我们在代码中申请一些权限,让用户手动获取,那么蓝牙这块具体需要哪些权限呐

          在清单文件中:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
 <!-- Android6.0 蓝牙扫描才需要-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

          代码中

if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED
        || ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED
        || ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
        ) {
    requestPermissions(new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_COARSE_LOCATION}, RequestCode_Bluetooth);
} else {
    BluetoothIsopen(bluetoothAdapter);
}
/**
 * 权限申请结果
 *
 * @param requestCode
 * @param permissions
 * @param grantResults
 */
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == RequestCode_Bluetooth) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED && grantResults[2] == PackageManager.PERMISSION_GRANTED) {
            BluetoothIsopen(bluetoothAdapter);

        } else {
            Toast.makeText(mActivity, "蓝牙权限被拒绝,请手动开启", Toast.LENGTH_SHORT).show();

        }

    }

}代码申请中我把三项都申请了,其实只需要定位申请就行.

   2.我们有了权限,就可以开始搞事了,搞事之前是不是得判断这个事情是否可以搞呐?没错接下来就是判断蓝牙是否可用,这里我把打开蓝牙也一起写下了

     

if (bluetoothAdapter == null) {
    bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (bluetoothAdapter == null) {
        LogUtils.e("设备不支持蓝牙");
    } else {
        if (!bluetoothAdapter.isEnabled()) {
            //--如果蓝牙没有打开就提示用户打开
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, 1);
        } 

    }
}

  3.蓝牙也打开了,那么接下来就是重点了,开启扫描!!!我在网上看到有说开启扫描最好开启新线程,具体原因我也不清楚

       

new Thread(new Runnable() {
    @Override
    public void run() {
        if (null != mBluetoothGatt) {
            mBluetoothGatt.close();
        }
        //开始搜索
        bluetoothAdapter.startLeScan(mLeScanCallback);
    }
}).start();

 4.扫描到蓝牙设备就可用开始连接了,那么我们怎么监听扫描到的设备呐,请看上面搜索中的mLeScanCallback参数,当扫描到设备会回调mLeScanCallback接口,这里我设置了adress连接,表示只连接某一个设备.

    

mActivity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        if (!TextUtils.isEmpty(device.getName())) {
            if (device.getAddress().equals("0C:B2:B7:54:27:1C")) {
                if (connect(device.getAddress()))
                    bluetoothAdapter.stopLeScan(mLeScanCallback);
                else connect(device.getAddress());
            }
        }
    }
});切换到主线程

5.现在我们扫描到了设备就可用进行连接了

public boolean connect(final String address) {
    final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
    if (device == null) {
        Log.w("msg", "Device not found.  Unable to connect.");
        return false;
    }
    if (mBluetoothGatt != null) {
        mBluetoothGatt.close();
        mBluetoothGatt.disconnect();
    }
    //这里才是真正连接
    mBluetoothGatt = device.connectGatt(mActivity, false, mGattCallback);
    return true;
}

  这里的mGattCallback参数就是我们连接的设备状态,读写通知特性等了,我们在这定义一个mGattCallback并实现接口回调

    

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    /**
     * 当GATT客户端已连接到GATT服务器或者从GATT服务器断开连接
     * 时回调。
     *
     * @param gatt
     * @param status 连接或断开操作的状态。BluetoothGatt.GATT_SUCCESS表示操作成功
     *
     * @param newState 返回新的连接状态。 如 BluetoothProfile.STATE_DISCONNECTED或
     * BluetoothProfile.STATE_CONNECTED
     */
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        //连接成功
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            //连接成功后就去找出该设备中的服务
            //但是这个方法是异步操作,在回调函数onServicesDiscovered中得到status,
            // 通过判断status是否等于BluetoothGatt.GATT_SUCCESS来判断查找Service是否成功
            mBluetoothGatt.discoverServices();
        } //连接失败
        else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            mActivity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (!bluetoothAdapter.isEnabled()) {
                        Toast.makeText(mActivity, "蓝牙关闭了", Toast.LENGTH_SHORT).show();
                        return;
                    }
                }
            });
            mBluetoothGatt.connect();
        }
    }

    /**
     * 当远程设备的远程服务列表,特征和描述符已被更新,即已发现新服务时,调用回调。
     *
     * @param gatt
     * @param status BluetoothGatt.GATT_SUCCESS 远程设备的远程服务列表可被发现
     */
    @Override
    public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
        //找到服务了
        if (status == BluetoothGatt.GATT_SUCCESS) {
            //在这里可以对服务进行解析,寻找到你需要的服务
            //--读取数据
            readData(serviceUuid, characteristicUuid);
        } else {
            Log.w("msg", "onServicesDiscovered received: " + status);
        }
    }

    //当读取设备时会回调该函数
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        Log.e("msg", "出来的数据:");
        if (status == BluetoothGatt.GATT_SUCCESS) {
        }
    }

    //当向设备Descriptor中写数据时,会回调该函数
    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        System.out.println("onDescriptorWriteonDescriptorWrite = " + status + ", descriptor =" + descriptor.getUuid().toString());
    }

    //设备发出通知时会调用到该接口
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic
            characteristic) {
        if (characteristic.getValue() != null && characteristic.getUuid().equals(characteristicUuid)) {
            byte[] bytes = characteristic.getValue();
            StringBuffer sBuffer = new StringBuffer();
            for (int i = 0; i < bytes.length; i++) {
                String s = Integer.toHexString(bytes[i] & 0xff);
                if (s.length() < 2)
                    sBuffer.append('0');
                sBuffer.append(s + " ");
            }
            parseData(sBuffer.toString());(自定义解析数据函数)

        }
    }

    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        System.out.println("rssi = " + rssi);
    }

    //当向Characteristic写数据时会回调该函数
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic
            characteristic, int status) {
        System.out.println("--------write success----- status:" + status);
    }
  这里每个函数回调都有详细说明,特别注意上面的 readData(serviceUuid, characteristicUuid);这个方法就是设置特性的,没有这个方法上面的读写通知函数就不会回调了,我们需要设备方提供一个服务uuid,一个特性uuid

     

public void readData(UUID serviceUuid, UUID characteristicUuid) {
        BluetoothGattService service = mBluetoothGatt.getService(serviceUuid);
        if (service == null) {
            return;
        }
        BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUuid);
        if (characteristic == null) {
            return;
        }
        // 开启新数据接收通知
        mBluetoothGatt.setCharacteristicNotification(characteristic, true);
        List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
        for (BluetoothGattDescriptor dp : descriptors) {
            //设置特征的属性
            dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(dp);
            mBluetoothGatt.readDescriptor(dp);
        }
//        //--调用此方法会回调onCharacteristicRead
//        mBluetoothGatt.readCharacteristic(characteristic);
//        mBluetoothGatt.writeCharacteristic(characteristic);
    }

    从上面看出我们设置了通知特性,要传入两个uuid,然后当设备有数据的时候就会回调onCharacteristicChanged()函数,接下来就是数据解析等等处理了,到了这里ble4.0一套流程就基本完事了.

   上面我设置了读写特性,但是无论怎么设置都不回调onCharacteristicRead(),与onDescriptorWrite()函数,找了半天也找不出什么原因,欢迎提出,感激不尽。

       

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值