• Android与蓝牙Ble之间的通信


    首先,先简单介绍一下ble的特征(注意:蓝牙4.0只有android4.3或4.3以上才支持

    1.BLE(Bluetooth Low Energy),蓝牙4.0核心profile,主要特点是快速搜索,快速连接,超低功耗保持连接和数据传输,缺点:数据传输速率低,由于其具有低功耗特点,所以经常用在可穿戴设备之中。

    2.关于BLE数据传输:

    a.profile可以理解为一种规范,一个标准的通信协议,其存在于手机中,蓝牙组织规定了一些标准的profile:HID OVER GATT ,防丢器等,每个profile中包含了多个service。

    b.service 可以理解为一个服务,在BLE从机中有多个服务,电量信息,系统服务信息等,每一个service中包含了多个characteristic特征值,每一个具体的characteristic特征值才是BLE通信的主题。

    c.characteristic特征值:BLE主机从机通信均是通过characteristic进行,可以将其理解为一个标签,通过该标签可以读取或写入相关信息。

    d. UUID(统一标识码):service和characteristic均需要这个唯一的UUID进行标识。

    e. 角色和职责,看到这里我觉得您老人家应该知道了,就是中心设备和外围设备(GATT server vs. GATT client.),不过呢在这里我就不这样说了,其实也不太懂那么的官方说法,我就简单粗暴点就是运用了ble的app和ble设备之间的通信,当app搜索到了ble设备,app会收到ble反馈过来的信息比如电量什么的,对于怎么得来的不用说都知道肯定是在一个规范好的一个对象中啦(先不需要知道这个对象是什么鬼东西因为我也不知道,官方叫characteristic也就是上面说的特征值),相反我们app也可以通过写入一些信息到这个对象(characteristic)发送给设备,设备收到之后就会执行我们的要它做的动作了。其实也就和平常我们的对象赋值一样(setValue("fuzhi")),只是方法不同而已,一个是set一个是write。

    最后来举个例子简答说明一下:其实说白了characteristic特征值就好比一支球队的各个球员,service好比这个球队的名称,profile就当国家吧,假如我们要找到某一个球员,世界这么多国家第一需要知道是哪个国家的,第二需要知道是哪个球队的,最后才知道是那个球员,只有这样最后才能了解这个球员的一些信息(类似从ble设备获取信息),相反也只有这样找到球员后告诉这个球员应该怎么打球的(类似对ble设备设置信息)。例子很难懂吧,如果你看懂了说明你和我一样小学毕业,看不懂的话就直接看代码吧,因为本人小学毕业,识字不多,脑袋不能转太快,怕翻车,,,

    /****************************************************代码块*****************************************************

    1、 权限和相关属性这是最基本的啦

    <uses-featureandroid:name="android.hardware.bluetooth_le"android:required="true"/>

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

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

    2、初始化蓝牙,这个方法一般可以写在项目的启动activity,不然怕在写完ble代码执行后发现没有打开蓝牙还需要手动去打开对项目体验感不好,当然只要您老人家开心,写不写都可以。

    private void initBluetooth() {
                 BluetoothManager mBluetoothManager = (BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE);
                 if (mBluetoothManager != null) {
                           BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
                               if (mBluetoothAdapter != null) {
                                         if (!mBluetoothAdapter.isEnabled()) {
                                              mBluetoothAdapter.enable();  //打开蓝牙
                                 }
                         }
                   }
       }

    3、获取本地ble对象(BluetoothAdapter),它对应本地Android设备的蓝牙模块,可能这么称呼为本地ble对象不太准确,但我老人家开心这么称呼。从此段代码开始可以把这些有关ble通信的代码写到一个class中当做一个ble工具class,以便代码清晰查看和方便调用。这里我们就当这个工具类叫BleManager

    private BluetoothAdapter mBluetoothAdapter; 

    private BluetoothDevice mBluetoothDevice; 

    private BluetoothGatt mBluetoothGatt;

    private boolean isScanning = false;

    //以上所定义的对象在下面的方法中都有用到,(建议在看蓝牙这方面的东西时,不管看谁的文章,都先把以上或者还有些蓝牙基本用的对象先熟悉是什么意思和基本作用)

    private BleManager(Context context) {
               this.mContext = context;
               BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);  //BluetoothManager只在android4.3以上有
               if (bluetoothManager == null) {
                           TLog.e(TAG, "Unable to initialize BluetoothManager.");
                            return;
               }

              mBluetoothAdapter = bluetoothManager.getAdapter();
    }

    4、既然获得了BluetoothAdapter对象,那么接下来就可以搜索ble设备了,这时就需要用到BluetoothAdapter的startLeScan()这个方法了

    public void startLeScan() {
    if (mBluetoothAdapter == null) {
              return;
       }

      if (isScanning) {
        return;
      }
        isScanning = true;

       mBluetoothAdapter.startLeScan(mLeScanCallback);   //此mLeScanCallback为回调函数

       mHandler.sendEmptyMessageDelayed(STOP_LESCAN, 10000);  //这个搜索10秒,如果搜索不到则停止搜索
    }

    在4.3之前的api是通过注册广播来处理搜索时发生的一些事件,而支持ble的新的api中,是通过回调的方式来处理的,而mLeScanCallback就是一个接口对象

    private LeScanCallback mLeScanCallback = new LeScanCallback() {
        @Override
              public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
                       TLog.i(TAG, "onLeScan() DeviceName------>"+device.getName());  //在这里可通过device这个对象来获取到搜索到的ble设备名称和一些相关信息
                       if(device.getName() == null){  
                               return;
                       }
                      if (device.getName().contains("Ble_Name")) {    //判断是否搜索到你需要的ble设备
                                 TLog.i(TAG, "onLeScan() DeviceAddress------>"+device.getAddress());
                                 mBluetoothDevice = device;   //获取到周边设备
                                 stopLeScan();   //1、当找到对应的设备后,立即停止扫描;2、不要循环搜索设备,为每次搜索设置适合的时间限制。避免设备不在可用范围的时候持续不停扫描,消耗电量。

                                 connect();  //连接
                     } 
          }
    };

    //

    private Handler mHandler = new Handler() {
    public void handleMessage(android.os.Message msg) {
    switch (msg.what) {
    case STOP_LESCAN:
    T.showLong(mContext, mContext.getResources().getString(R.string.msg_connect_failed));
    mBluetoothAdapter.stopLeScan(mLeScanCallback);
    broadcastUpdate(Config.ACTION_GATT_DISCONNECTED);
    isScanning = false;
    TLog.i(TAG, "Scan time is up");
    break;
    }
    };
    };

     

    5、搜索到当然就是连接了,就是上面那个connect()方法了

    public boolean connect() {
           if (mBluetoothDevice == null) {
           TLog.i(TAG, "BluetoothDevice is null.");
           return false;
    }

    //两个设备通过BLE通信,首先需要建立GATT连接。这里我们讲的是Android设备作为client端,连接GATT Server

    mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false, mGattCallback);  //mGattCallback为回调接口

    if (mBluetoothGatt != null) {

    if (mBluetoothGatt.connect()) {
    TLog.d(TAG, "Connect succeed.");
    return true;
    } else {
    TLog.d(TAG, "Connect fail.");
    return false;
    }
    } else {
    TLog.d(TAG, "BluetoothGatt null.");
    return false;
    }
    }

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                        if (newState == BluetoothProfile.STATE_CONNECTED) {
                                      gatt.discoverServices(); //执行到这里其实蓝牙已经连接成功了

                                      TLog.i(TAG, "Connected to GATT server.");
                               } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                                          if(mBluetoothDevice != null){
                                                  TLog.i(TAG, "重新连接");
                                                        connect();
                                                   }else{
                                                       TLog.i(TAG, "Disconnected from GATT server.");
                              }
                   }
    }

    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
               if (status == BluetoothGatt.GATT_SUCCESS) {
                    TLog.i(TAG, "onServicesDiscovered");
                    getBatteryLevel();  //获取电量
          } else {
                  TLog.i(TAG, "onServicesDiscovered status------>" + status);
         }
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
           TLog.d(TAG, "onCharacteristicRead------>" + Utils.bytesToHexString(characteristic.getValue()));

    //判断UUID是否相等
           if (Values.UUID_KEY_BATTERY_LEVEL_CHARACTERISTICS.equals(characteristic.getUuid().toString())) { 
    }
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
          TLog.d(TAG, "onCharacteristicChanged------>" + Utils.bytesToHexString(characteristic.getValue()));

    //判断UUID是否相等
          if (Values.UUID_KEY_BATTERY_LEVEL_CHARACTERISTICS.equals(characteristic.getUuid().toString())) {
    }
    }

    //接受Characteristic被写的通知,收到蓝牙模块的数据后会触发onCharacteristicWrite
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
           TLog.d(TAG,"status = " + status);
           TLog.d(TAG, "onCharacteristicWrite------>" + Utils.bytesToHexString(characteristic.getValue()));
    }
    };

    public void getBatteryLevel() {
    BluetoothGattCharacteristic batteryLevelGattC = getCharcteristic(
    Values.UUID_KEY_BATTERY_LEVEL_SERVICE, Values.UUID_KEY_BATTERY_LEVEL_CHARACTERISTICS);
    if (batteryLevelGattC != null) {
    readCharacteristic(batteryLevelGattC);
    setCharacteristicNotification(batteryLevelGattC, true); //设置当指定characteristic值变化时,发出通知。
    }
    }

    6、获取服务与特征

    //a.获取服务

    public BluetoothGattService getService(UUID uuid) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
    TLog.e(TAG, "BluetoothAdapter not initialized");
    return null;
    }
    return mBluetoothGatt.getService(uuid);
    }

    //b.获取特征

    private BluetoothGattCharacteristic getCharcteristic(String serviceUUID, String characteristicUUID) {

    //得到服务对象
    BluetoothGattService service = getService(UUID.fromString(serviceUUID));  //调用上面获取服务的方法

    if (service == null) {
    TLog.e(TAG, "Can not find 'BluetoothGattService'");
    return null;
    }

    //得到此服务结点下Characteristic对象
    final BluetoothGattCharacteristic gattCharacteristic = service.getCharacteristic(UUID.fromString(characteristicUUID));
    if (gattCharacteristic != null) {
    return gattCharacteristic;
    } else {
    TLog.e(TAG, "Can not find 'BluetoothGattCharacteristic'");
    return null;
    }
    }

    //获取数据

    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
    TLog.e(TAG, "BluetoothAdapter not initialized");
    return;
    }
    mBluetoothGatt.readCharacteristic(characteristic);
    }

    public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
    TLog.e(TAG, "BluetoothAdapter not initialized");
    return false;
    }
    return mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    }

    7、写入数据,在上面的方法中我们已经得到了设备服务和服务里的特征characteristic,那么就可以对这个特征写入或者说是赋值

    public void write(byte[] data) {   //一般都是传byte
              //得到可写入的characteristic Utils.isAIRPLANE(mContext) &&
                if(!mBleManager.isEnabled()){
                    TLog.e(TAG, "writeCharacteristic 开启飞行模式");
                    //closeBluetoothGatt();
                    isGattConnected = false;
                    broadcastUpdate(Config.ACTION_GATT_DISCONNECTED);
                    return;
               }
             BluetoothGattCharacteristic writeCharacteristic = getCharcteristic(Values.UUID_KEY_SERVICE, Values.UUID_KEY_WRITE);  //这个UUID都是根据协议号的UUID
             if (writeCharacteristic == null) {
             TLog.e(TAG, "Write failed. GattCharacteristic is null.");
             return;
         }
         writeCharacteristic.setValue(data); //为characteristic赋值
         writeCharacteristicWrite(writeCharacteristic);

    }

    public void writeCharacteristicWrite(BluetoothGattCharacteristic characteristic) {
                if (mBluetoothAdapter == null || mBluetoothGatt == null) {
                    TLog.e(TAG, "BluetoothAdapter not initialized");
                    return;
                 }
                  TLog.e(TAG, "BluetoothAdapter 写入数据");
                  boolean isBoolean = false;
                  isBoolean = mBluetoothGatt.writeCharacteristic(characteristic);
                 TLog.e(TAG, "BluetoothAdapter_writeCharacteristic = " +isBoolean);  //如果isBoolean返回的是true则写入成功
    }

    8、写到这其实基本差不多了,最后再就是在activity中调用了,其实在调用的时候直接在需要搜索蓝牙的activity调用startLeScan()这个方法就可以了,至于搜索到的结果也显示在这个方法里面了,找到了对应的设备名称然后再连接,对于中间一系列的回调函数,不管是成功还是失败都是根据您老人家的实际项目需求去写的,你可以用一个广播把回调信息发送出来到activity,告诉activity应该做什么........

    举个简单的例子:

                 在MainActivity调用搜索蓝牙的方法

                public class MainActivity extends Activity{ 

                private BleManager manager;            

                      @Override
                 protected void onCreate(Bundle savedInstanceState) {
                 super.onCreate(savedInstanceState);

                 manager = new BleManager(this);  //这个this是一个上下文,只要在上面的BleManager工具类定义一个有参数就好

                 manager.startLeScan();  //调用此方法,如果搜索到有设备则自然的去连接它了,到时候连接时回回调mGattCallback 这个回调函数,如果成功你可以发送一个广播出来提醒用户蓝牙与设备连接成功

             }

             搜索成功后,你就可以根据你的需要写入信息传送了。简单说说,就是你在一个button事件中调用上面的write(byte[] byte)这个方法,对于里面的参数byte就是根据你们所设置的byte了,最后写入的成功与否就看writeCharacteristicWrite这个方法下面的返回值是true还是false了,true表示成成功。

    9、总结了,菜鸟我其实也刚接触蓝牙不久,也是看了一些文章,然后结合自己的一些总结,写出了这么一篇小学生文章(也是想为自己增加一些印象),请各位老人家们多多理解,当然要是有哪位大神看不过去或者觉得那里不对了请指教出来,不要让小菜鸟一直错下去,谢谢!

  • 相关阅读:
    githubz在add ssh key报错 Key is invalid. Ensure you've copied the file correctly 解决办法
    iOS 更改状态栏颜色
    iOS 加载图片选择imageNamed 方法还是 imageWithContentsOfFile?
    iOS UIWebView加载网页、文件、HTML
    XXX is undefine,全局搜索却只得到一个结果
    接口调用报错,全局搜索却找不到?vscode vue
    elementui下拉框选择一次以后再选,多项被选中的情况
    VUE+ elementui 表单rules validator 邮箱验证、电话号码验证、身份证验证、账号验证
    JS 验证input内容框 Demo(复制可测试)
    Vscode setting.json个人设置(包含保存格式化,空格、换行,标点符号自动增删)
  • 原文地址:https://www.cnblogs.com/xxzjyf/p/x_x_z_j_y_f.html
Copyright © 2020-2023  润新知