zoukankan      html  css  js  c++  java
  • Android项目实战(三十四):蓝牙4.0 BLE 多设备连接

      最近项目有个需求,手机设备连接多个蓝牙4.0 设备 并获取这些设备的数据。

      查询了很多资料终于实现,现进行总结。

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------

        从零开始实现一个连接多个蓝牙4.0 设备并获取数据的 Demo

      注:如果不想看实现过程的,直接看最下面的demo源码即可,或每一步后相关操作步骤的完整代码。

      

      一、Demo需求

        1、搜索设备 , 选择多个要连接的设备。

        2、开始连接,显示数据。

      

      二、项目知识储备

        项目中需要用到的三方:

        1、RecyclerView 

           列表,用于显示扫描得到的所有蓝牙设备

        2、BaseRecyclerViewAdapterHelper

           Recyclerview 帮助框架,快速实现列表操作 

        3、eventbus

          用于消息传递,获取到蓝牙传送的数据之后,刷新界面显示数据时使用

        4、bluetooth-manager

          蓝牙4.0框架

        5、permissionsdispatcher

         权限管理,适配6.0+设备

         

        添加依赖 gradle.bulld文件

        compile 'com.android.support:appcompat-v7:25.3.1'    compile 'com.blakequ.androidblemanager:bluetooth-manager-lib:2.1.5'
        compile 'com.github.hotchemi:permissionsdispatcher:2.1.3'
        compile 'de.greenrobot:eventbus:2.4.0'
        compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.18'
        compile 'com.android.support:design:25.3.1'

      三、项目实现,布局文件

        1、demo中一共用到两个activity 对应两个布局文件

     

         先看扫描设备界面

         包含:

          1、一个列表,显示 所有扫描到的设备的MAC地址,点击状态在 ''已选择' or '‘未选择’ 之间改变,表明当前设备有没有加入到需要连接的设备集合中

         2、扫描按钮

         3、结束扫描按钮

         4、完成选择按钮,将选择的设备MAC地址传回 

         

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_select_device"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.maiji.magkareble40.SelectDeviceActivity">
    
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            >
    
        </android.support.v7.widget.RecyclerView>
    
    
        <Button
            android:id="@+id/btnScan"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="开始扫描"
            />
    
        <Button
            android:id="@+id/btnStopScan"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="停止扫描"
            />
        <Button
            android:id="@+id/btnOk"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="完成选择设备"
            />
    </LinearLayout>

         

        连接界面。

        包含:

        1、选择需要连接的传感器设备 按钮

        2、开始连接 按钮

        3、数据展示

        

        

        布局文件代码:

      

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.maiji.magkareble40.XBleActivity">
    
    
        <Button
            android:id="@+id/btnSelectDevice"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="选择需要连接的传感器设备"
            />
    
        <Button
            android:id="@+id/btnStartConnect"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="开始连接"
            />
    
        <TextView
            android:id="@+id/txtContentMac"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text=""/>
    
    </LinearLayout>

        四、Activity实现

        1、扫描 设备 选择设备Activity

        (1)、变量声明

       private Button btnScan;        //开始扫描按钮
        private Button btnStopScan;   //停止扫描按钮
        private Button btnOk;   //选择好了需要连接的mac设备
    
        BluetoothScanManager scanManager ;  // 设备扫描管理器
    
    
        /* 列表相关 */
        private RecyclerView recyclerView ; //列表
        private ScanDeviceAdapter adapter;  //设备扫描适配器
        private ArrayList<String> deviceMacs ; // 数据源 : 所有扫描到的设备mac地址
    
        private ArrayList<String> selectDeviceMacs; // 选择的需要连接的设备的mac集合

        关键代码:

        (1)、蓝牙扫描的初始化设置

    /**
         * 初始化蓝牙相关配置
         */
        private void initBle() {
            scanManager = BleManager.getScanManager(this);
    
            scanManager.setScanOverListener(new ScanOverListener() {
                @Override
                public void onScanOver() {
                }
            });
    
    
            scanManager.setScanCallbackCompat(new ScanCallbackCompat() {
                @Override
                public void onBatchScanResults(List<ScanResultCompat> results) {
                    super.onBatchScanResults(results);
                }
    
                @Override
                public void onScanFailed(final int errorCode) {
                    super.onScanFailed(errorCode);
    
                }
    
                @Override
                public void onScanResult(int callbackType, ScanResultCompat result) {
                    super.onScanResult(callbackType, result);
                    //scan result
                    // 只有当前列表中没有该mac地址的时候 添加
                    if (!deviceMacs.contains(result.getDevice().getAddress())) {
                        deviceMacs.add(result.getDevice().getAddress());
                        adapter.notifyDataSetChanged();
                    }
                }
            });
    
        }
    蓝牙扫描设置初始化

        (2)、开始扫描按钮 操作

    //                scanManager.startCycleScan(); //不会立即开始,可能会延时
                    scanManager.startScanNow(); //立即开始扫描

        (3)、停止扫描按钮 操作

     // 如果正在扫描中 停止扫描
                    if (scanManager.isScanning()) {
                        scanManager.stopCycleScan();
                    }

        (4)、RecyclerView初始化 ,点击事件操作

        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    
            // 列表相关初始化
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            adapter = new ScanDeviceAdapter(deviceMacs);
    
            adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
                @Override
                public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                    if (!selectDeviceMacs.contains(deviceMacs.get(position))){
                        //如果改item的mac不在已选中的mac集合中 说明没有选中,添加进已选中mac集合中,状态改为"已选择"
                        selectDeviceMacs.add(deviceMacs.get(position));
                        ((TextView)view.findViewById(R.id.txtState)).setText("已选择");
                    }else {
                        selectDeviceMacs.remove(deviceMacs.get(position));
                        ((TextView)view.findViewById(R.id.txtState)).setText("未选择");
                    }
                }
            });
    
            recyclerView.setAdapter(adapter);

      

      activity全部代码:

    package com.maiji.magkareble40;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    import com.blakequ.bluetooth_manager_lib.BleManager;
    import com.blakequ.bluetooth_manager_lib.BleParamsOptions;
    import com.blakequ.bluetooth_manager_lib.connect.ConnectConfig;
    import com.blakequ.bluetooth_manager_lib.scan.BluetoothScanManager;
    import com.blakequ.bluetooth_manager_lib.scan.ScanOverListener;
    import com.blakequ.bluetooth_manager_lib.scan.bluetoothcompat.ScanCallbackCompat;
    import com.blakequ.bluetooth_manager_lib.scan.bluetoothcompat.ScanFilterCompat;
    import com.blakequ.bluetooth_manager_lib.scan.bluetoothcompat.ScanResultCompat;
    import com.chad.library.adapter.base.BaseQuickAdapter;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
    * @author xqx
    * @email djlxqx@163.com
    * blog:http://www.cnblogs.com/xqxacm/
    * createAt 2017/9/6
    * description:  扫描蓝牙设备  选择需要连接的传感器
    */
    
    public class SelectDeviceActivity extends Activity implements View.OnClickListener {
    
        private Button btnScan;        //开始扫描按钮
        private Button btnStopScan;   //停止扫描按钮
        private Button btnOk;   //选择好了需要连接的mac设备
    
        BluetoothScanManager scanManager ;
    
    
        /* 列表相关 */
        private RecyclerView recyclerView ; //列表
        private ScanDeviceAdapter adapter;
        private ArrayList<String> deviceMacs ; // 数据源 : 所有扫描到的设备mac地址
    
        private ArrayList<String> selectDeviceMacs; // 选择的需要连接的设备的mac集合
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_select_device);
    
            deviceMacs = new ArrayList<>();
            selectDeviceMacs = new ArrayList<>();
            initView();
            initEvent();
            initBle();
    
    
        }
    
        /**
         * 初始化蓝牙相关配置
         */
        private void initBle() {
            scanManager = BleManager.getScanManager(this);
    
            scanManager.setScanOverListener(new ScanOverListener() {
                @Override
                public void onScanOver() {
                }
            });
    
    
            scanManager.setScanCallbackCompat(new ScanCallbackCompat() {
                @Override
                public void onBatchScanResults(List<ScanResultCompat> results) {
                    super.onBatchScanResults(results);
                }
    
                @Override
                public void onScanFailed(final int errorCode) {
                    super.onScanFailed(errorCode);
    
                }
    
                @Override
                public void onScanResult(int callbackType, ScanResultCompat result) {
                    super.onScanResult(callbackType, result);
                    //scan result
                    // 只有当前列表中没有该mac地址的时候 添加
                    if (!deviceMacs.contains(result.getDevice().getAddress())) {
                        deviceMacs.add(result.getDevice().getAddress());
                        adapter.notifyDataSetChanged();
                    }
                }
            });
    
        }
    
        private void initEvent() {
            btnScan.setOnClickListener(this);
            btnStopScan.setOnClickListener(this);
            btnOk.setOnClickListener(this);
        }
    
        private void initView() {
            btnScan = (Button) findViewById(R.id.btnScan);
            btnStopScan = (Button) findViewById(R.id.btnStopScan);
            btnOk = (Button) findViewById(R.id.btnOk);
            recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    
            // 列表相关初始化
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            adapter = new ScanDeviceAdapter(deviceMacs);
    
            adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
                @Override
                public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                    if (!selectDeviceMacs.contains(deviceMacs.get(position))){
                        //如果改item的mac不在已选中的mac集合中 说明没有选中,添加进已选中mac集合中,状态改为"已选择"
                        selectDeviceMacs.add(deviceMacs.get(position));
                        ((TextView)view.findViewById(R.id.txtState)).setText("已选择");
                    }else {
                        selectDeviceMacs.remove(deviceMacs.get(position));
                        ((TextView)view.findViewById(R.id.txtState)).setText("未选择");
                    }
                }
            });
    
            recyclerView.setAdapter(adapter);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.btnScan:
                    //开始 扫描
    //                scanManager.startCycleScan(); //不会立即开始,可能会延时
                    scanManager.startScanNow(); //立即开始扫描
                    break;
    
                case R.id.btnStopScan:
                    // 如果正在扫描中 停止扫描
                    if (scanManager.isScanning()) {
                        scanManager.stopCycleScan();
                    }
                    break;
                case R.id.btnOk:
                    Intent intent = new Intent();
                    intent.putExtra("data",selectDeviceMacs);                // 设置结果,并进行传送
                    this.setResult(1, intent);
                    this.finish();
                    break;
    
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            // 如果正在扫描中 停止扫描
            if (scanManager.isScanning()) {
                scanManager.stopCycleScan();
            }
        }
    }
    SelectDeviceActivity.class

      适配器相关代码:

    package com.maiji.magkareble40;
    
    import android.widget.ImageView;
    
    import com.chad.library.adapter.base.BaseQuickAdapter;
    import com.chad.library.adapter.base.BaseViewHolder;
    
    import java.util.ArrayList;
    
    /**
    * @author xqx
    * @email djlxqx@163.com
    * blog:http://www.cnblogs.com/xqxacm/
    * createAt 2017/9/6
    * description:  扫描得到的蓝牙设备列表适配器
    */
    
    public class ScanDeviceAdapter extends BaseQuickAdapter<String , BaseViewHolder> {
    
        public ScanDeviceAdapter(ArrayList<String> datas) {
            super(R.layout.item_device, datas);
        }
    
        @Override
        protected void convert(BaseViewHolder helper, String item) {
            helper.setText(R.id.txtMac,item);
    
        }
    }
    ScanDeviceAdapter.class

      适配器布局代码:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="16dp"
        android:paddingRight="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginBottom="16dp"
        >
    
        <TextView
            android:id="@+id/txtMac"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            android:layout_centerVertical="true"
            />
    
        <TextView
            android:id="@+id/txtState"
            android:text="未选择"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            />
    
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#fff"
            android:layout_alignParentBottom="true"
            ></View>
    </RelativeLayout>
    item_device.xml

        2、连接多设备,获取数据并展示Activity 

        (1)、变量声明

        private Button btnSelectDevice ;  //选择需要绑定的设备
        private Button btnStartConnect ;  //开始连接按钮
    
        private TextView txtContentMac ; //获取到的数据解析结果显示
    
        private final int REQUEST_CODE_PERMISSION = 1; // 权限请求码  用于回调
    
        MultiConnectManager multiConnectManager ;  //多设备连接
        private BluetoothAdapter bluetoothAdapter;   //蓝牙适配器
    
    
        private ArrayList<String> connectDeviceMacList ; //需要连接的mac设备集合
        ArrayList<BluetoothGatt> gattArrayList; //设备gatt集合

      

        2、关键代码

        1、权限适配

        注意:不止蓝牙权限,位置权限也需要打开

    /**
         * @author xqx
         * @email djlxqx@163.com
         * blog:http://www.cnblogs.com/xqxacm/
         * createAt 2017/8/30
         * description:  权限申请相关,适配6.0+机型 ,蓝牙,文件,位置 权限
         */
    
        private String[] allPermissionList = {Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN,
                Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
    
    
        /**
         * 遍历出需要获取的权限
         */
        private void requestWritePermission() {
            ArrayList<String> permissionList = new ArrayList<>();
            // 将需要获取的权限加入到集合中  ,根据集合数量判断 需不需要添加
            for (int i = 0; i < allPermissionList.length; i++) {
                if (PackageManager.PERMISSION_DENIED == ContextCompat.checkSelfPermission(this, allPermissionList[i])){
                    permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
                }
            }
    
            String permissionArray[] = new String[permissionList.size()];
            for (int i = 0; i < permissionList.size(); i++) {
                permissionArray[i] = permissionList.get(i);
            }
            if (permissionList.size() > 0)
                ActivityCompat.requestPermissions(this, permissionArray, REQUEST_CODE_PERMISSION);
        }
    
    
        /**
         * 权限申请的回调
         * @param requestCode
         * @param permissions
         * @param grantResults
         */
        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            if (requestCode == REQUEST_CODE_PERMISSION){
                if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        &&grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    //用户同意使用write
    
                }else{
                    //用户不同意,自行处理即可
                    Toast.makeText(XBleActivity.this,"您取消了权限申请,可能会影响软件的使用,如有问题请退出重试",Toast.LENGTH_SHORT).show();
                }
            }
        }
    权限适配

        2、蓝牙开启、连接等 初始化设置

    /**
         * 对蓝牙的初始化操作
         */
        private void initConfig() {
            multiConnectManager = BleManager.getMultiConnectManager(this);
            // 获取蓝牙适配器
    
            try {
                // 获取蓝牙适配器
                bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
                //
                if (bluetoothAdapter == null) {
                    Toast.makeText(this, "蓝牙不可用", Toast.LENGTH_LONG).show();
                    return;
                }
    
                // 蓝牙没打开的时候打开蓝牙
                if (!bluetoothAdapter.isEnabled())
                    bluetoothAdapter.enable();
            }catch (Exception err){};
            BleManager.setBleParamsOptions(new BleParamsOptions.Builder()
                    .setBackgroundBetweenScanPeriod(5 * 60 * 1000)
                    .setBackgroundScanPeriod(10000)
                    .setForegroundBetweenScanPeriod(2000)
                    .setForegroundScanPeriod(10000)
                    .setDebugMode(BuildConfig.DEBUG)
                    .setMaxConnectDeviceNum(7)            //最大可以连接的蓝牙设备个数
                    .setReconnectBaseSpaceTime(1000)
                    .setReconnectMaxTimes(Integer.MAX_VALUE)
                    .setReconnectStrategy(ConnectConfig.RECONNECT_LINE_EXPONENT)
                    .setReconnectedLineToExponentTimes(5)
                    .setConnectTimeOutTimes(20000)
                    .build());
        }
    initBle

        3、开始连接操作

     /**
         * 连接需要连接的传感器
         * @param
         */
        private void connentBluetooth(){
    
            String[] objects = connectDeviceMacList.toArray(new String[connectDeviceMacList.size()]);
            multiConnectManager.addDeviceToQueue(objects);
            multiConnectManager.addConnectStateListener(new ConnectStateListener() {
                @Override
                public void onConnectStateChanged(String address, ConnectState state) {
                    switch (state){
                        case CONNECTING:
                            Log.i("connectStateX","设备:"+address+"连接状态:"+"正在连接");
                            break;
                        case CONNECTED:
                            Log.i("connectStateX","设备:"+address+"连接状态:"+"成功");
                            break;
                        case NORMAL:
                            Log.i("connectStateX","设备:"+address+"连接状态:"+"失败");
                            break;
                    }
                }
            });
    
            /**
             * 数据回调
             */
    
            multiConnectManager.setBluetoothGattCallback(new BluetoothGattCallback() {
                @Override
                public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                    super.onCharacteristicChanged(gatt, characteristic);
                    dealCallDatas(gatt , characteristic);
                }
            });
    
            multiConnectManager.setServiceUUID("0000ffe5-0000-1000-8000-00805f9a34fb");
            multiConnectManager.addBluetoothSubscribeData(
                    new BluetoothSubScribeData.Builder().setCharacteristicNotify(UUID.fromString("0000ffe4-0000-1000-8000-00805f9a34fb")).build());
    
            //还有读写descriptor
            //start descriptor(注意,在使用时当回调onServicesDiscovered成功时会自动调用该方法,所以只需要在连接之前完成1,3步即可)
            for (int i = 0; i < gattArrayList.size(); i++) {
                multiConnectManager.startSubscribe(gattArrayList.get(i));
            }
    
            multiConnectManager.startConnect();
    
        }
    
        /**
         * 处理回调的数据
         * @param gatt
         * @param characteristic
         */
    
        float[][] floats = new float[7][30];
    
        private void dealCallDatas(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            int position = connectDeviceMacList.indexOf(gatt.getDevice().getAddress());
            //第一个传感器数据
            byte[] value = characteristic.getValue();
            if (value[0] != 0x55) {
                //开头不是0x55的数据删除
                return;
            }
            switch (value[1]) {
                case 0x61:
                    //加速度数据
                    floats[position][3] = ((((short) value[3]) << 8) | ((short) value[2] & 0xff)) / 32768.0f * 16;   //x轴
                    floats[position][4] = ((((short) value[5]) << 8) | ((short) value[4] & 0xff)) / 32768.0f * 16;   //y轴
                    floats[position][5] = ((((short) value[7]) << 8) | ((short) value[6] & 0xff)) / 32768.0f * 16;   //z轴
                    //角速度数据
                    floats[position][6] = ((((short) value[9]) << 8) | ((short) value[8] & 0xff)) / 32768.0f * 2000;  //x轴
                    floats[position][7] = ((((short) value[11]) << 8) | ((short) value[10] & 0xff)) / 32768.0f * 2000;  //x轴
                    floats[position][8] = ((((short) value[13]) << 8) | ((short) value[12] & 0xff)) / 32768.0f * 2000;  //x轴
                    break;
                case 0x62:
                    //四元素
                    floats[position][13] = ((((short) value[3]) << 8) | ((short) value[2] & 0xff)) / 32768.0f; // q1
                    floats[position][14] = ((((short) value[5]) << 8) | ((short) value[4] & 0xff)) / 32768.0f; // q2
                    floats[position][15] = ((((short) value[7]) << 8) | ((short) value[6] & 0xff)) / 32768.0f; // q3
                    floats[position][16] = ((((short) value[9]) << 8) | ((short) value[8] & 0xff)) / 32768.0f; // q4
                    //电池电压
                    floats[position][21] = (float) 1.2 * 4 * (((value[11] << 8) | value[10]) + 1) / 1024;
                    //充电状态
                    floats[position][22] = value[12];
                    //低电压报警
                    floats[position][23] = value[14];
                    break;
            }
            EventBus.getDefault().post(new RefreshDatas()); // 发送消息,更新UI 显示数据
        }
    connectBle

    activity全部代码:

    package com.maiji.magkareble40;
    
    import android.Manifest;
    import android.app.Activity;
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothGatt;
    import android.bluetooth.BluetoothGattCallback;
    import android.bluetooth.BluetoothGattCharacteristic;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.blakequ.bluetooth_manager_lib.BleManager;
    import com.blakequ.bluetooth_manager_lib.BleParamsOptions;
    import com.blakequ.bluetooth_manager_lib.connect.BluetoothSubScribeData;
    import com.blakequ.bluetooth_manager_lib.connect.ConnectConfig;
    import com.blakequ.bluetooth_manager_lib.connect.ConnectState;
    import com.blakequ.bluetooth_manager_lib.connect.ConnectStateListener;
    import com.blakequ.bluetooth_manager_lib.connect.multiple.MultiConnectManager;
    
    import java.util.ArrayList;
    import java.util.UUID;
    
    import de.greenrobot.event.EventBus;
    
    /**
    * @author xqx
    * @email djlxqx@163.com
    * blog:http://www.cnblogs.com/xqxacm/
    * createAt 2017/9/6
    * description:  ble 4.0 多设备连接
    */
    
    public class XBleActivity extends Activity implements View.OnClickListener {
    
        private Button btnSelectDevice ;  //选择需要绑定的设备
        private Button btnStartConnect ;  //开始连接按钮
    
        private TextView txtContentMac ; //获取到的数据解析结果显示
    
        private final int REQUEST_CODE_PERMISSION = 1; // 权限请求码  用于回调
    
        MultiConnectManager multiConnectManager ;  //多设备连接
        private BluetoothAdapter bluetoothAdapter;   //蓝牙适配器
    
    
        private ArrayList<String> connectDeviceMacList ; //需要连接的mac设备集合
        ArrayList<BluetoothGatt> gattArrayList; //设备gatt集合
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_xble);
    
            initVariables();
            initView();
            initEvent();
            requestWritePermission();
            initConfig();  // 蓝牙初始设置
            EventBus.getDefault().register(this);
        }
    
        private void initVariables() {
            connectDeviceMacList = new ArrayList<>();
            gattArrayList = new ArrayList<>();
    
        }
    
        private void initEvent() {
            btnSelectDevice.setOnClickListener(this);
            btnStartConnect.setOnClickListener(this);
        }
    
        private void initView() {
            btnSelectDevice = (Button) findViewById(R.id.btnSelectDevice);
            btnStartConnect = (Button) findViewById(R.id.btnStartConnect);
            txtContentMac = (TextView) findViewById(R.id.txtContentMac);
    
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.btnSelectDevice:
                    // 扫描并选择需要连接的设备
                    Intent intent = new Intent();
                    intent.setClass(this,SelectDeviceActivity.class);
                    startActivityForResult(intent,1);
                    break;
                case R.id.btnStartConnect:
                    connentBluetooth();
                    break;
            }
        }
    
    
        /**
         * 连接需要连接的传感器
         * @param
         */
        private void connentBluetooth(){
    
            String[] objects = connectDeviceMacList.toArray(new String[connectDeviceMacList.size()]);
            multiConnectManager.addDeviceToQueue(objects);
            multiConnectManager.addConnectStateListener(new ConnectStateListener() {
                @Override
                public void onConnectStateChanged(String address, ConnectState state) {
                    switch (state){
                        case CONNECTING:
                            Log.i("connectStateX","设备:"+address+"连接状态:"+"正在连接");
                            break;
                        case CONNECTED:
                            Log.i("connectStateX","设备:"+address+"连接状态:"+"成功");
                            break;
                        case NORMAL:
                            Log.i("connectStateX","设备:"+address+"连接状态:"+"失败");
                            break;
                    }
                }
            });
    
            /**
             * 数据回调
             */
    
            multiConnectManager.setBluetoothGattCallback(new BluetoothGattCallback() {
                @Override
                public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                    super.onCharacteristicChanged(gatt, characteristic);
                    dealCallDatas(gatt , characteristic);
                }
            });
    
            multiConnectManager.setServiceUUID("0000ffe5-0000-1000-8000-00805f9a34fb");
            multiConnectManager.addBluetoothSubscribeData(
                    new BluetoothSubScribeData.Builder().setCharacteristicNotify(UUID.fromString("0000ffe4-0000-1000-8000-00805f9a34fb")).build());
    
            //还有读写descriptor
            //start descriptor(注意,在使用时当回调onServicesDiscovered成功时会自动调用该方法,所以只需要在连接之前完成1,3步即可)
            for (int i = 0; i < gattArrayList.size(); i++) {
                multiConnectManager.startSubscribe(gattArrayList.get(i));
            }
    
            multiConnectManager.startConnect();
    
        }
    
        /**
         * 处理回调的数据
         * @param gatt
         * @param characteristic
         */
    
        float[][] floats = new float[7][30];
    
        private void dealCallDatas(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            int position = connectDeviceMacList.indexOf(gatt.getDevice().getAddress());
            //第一个传感器数据
            byte[] value = characteristic.getValue();
            if (value[0] != 0x55) {
                //开头不是0x55的数据删除
                return;
            }
            switch (value[1]) {
                case 0x61:
                    //加速度数据
                    floats[position][3] = ((((short) value[3]) << 8) | ((short) value[2] & 0xff)) / 32768.0f * 16;   //x轴
                    floats[position][4] = ((((short) value[5]) << 8) | ((short) value[4] & 0xff)) / 32768.0f * 16;   //y轴
                    floats[position][5] = ((((short) value[7]) << 8) | ((short) value[6] & 0xff)) / 32768.0f * 16;   //z轴
                    //角速度数据
                    floats[position][6] = ((((short) value[9]) << 8) | ((short) value[8] & 0xff)) / 32768.0f * 2000;  //x轴
                    floats[position][7] = ((((short) value[11]) << 8) | ((short) value[10] & 0xff)) / 32768.0f * 2000;  //x轴
                    floats[position][8] = ((((short) value[13]) << 8) | ((short) value[12] & 0xff)) / 32768.0f * 2000;  //x轴
                    break;
                case 0x62:
                    //四元素
                    floats[position][13] = ((((short) value[3]) << 8) | ((short) value[2] & 0xff)) / 32768.0f; // q1
                    floats[position][14] = ((((short) value[5]) << 8) | ((short) value[4] & 0xff)) / 32768.0f; // q2
                    floats[position][15] = ((((short) value[7]) << 8) | ((short) value[6] & 0xff)) / 32768.0f; // q3
                    floats[position][16] = ((((short) value[9]) << 8) | ((short) value[8] & 0xff)) / 32768.0f; // q4
                    //电池电压
                    floats[position][21] = (float) 1.2 * 4 * (((value[11] << 8) | value[10]) + 1) / 1024;
                    //充电状态
                    floats[position][22] = value[12];
                    //低电压报警
                    floats[position][23] = value[14];
                    break;
            }
            EventBus.getDefault().post(new RefreshDatas()); // 发送消息,更新UI 显示数据
        }
    
        /**
         * 对蓝牙的初始化操作
         */
        private void initConfig() {
            multiConnectManager = BleManager.getMultiConnectManager(this);
            // 获取蓝牙适配器
    
            try {
                // 获取蓝牙适配器
                bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
                //
                if (bluetoothAdapter == null) {
                    Toast.makeText(this, "蓝牙不可用", Toast.LENGTH_LONG).show();
                    return;
                }
    
                // 蓝牙没打开的时候打开蓝牙
                if (!bluetoothAdapter.isEnabled())
                    bluetoothAdapter.enable();
            }catch (Exception err){};
            BleManager.setBleParamsOptions(new BleParamsOptions.Builder()
                    .setBackgroundBetweenScanPeriod(5 * 60 * 1000)
                    .setBackgroundScanPeriod(10000)
                    .setForegroundBetweenScanPeriod(2000)
                    .setForegroundScanPeriod(10000)
                    .setDebugMode(BuildConfig.DEBUG)
                    .setMaxConnectDeviceNum(7)            //最大可以连接的蓝牙设备个数
                    .setReconnectBaseSpaceTime(1000)
                    .setReconnectMaxTimes(Integer.MAX_VALUE)
                    .setReconnectStrategy(ConnectConfig.RECONNECT_LINE_EXPONENT)
                    .setReconnectedLineToExponentTimes(5)
                    .setConnectTimeOutTimes(20000)
                    .build());
        }
    
        /**
         * @author xqx
         * @email djlxqx@163.com
         * blog:http://www.cnblogs.com/xqxacm/
         * createAt 2017/8/30
         * description:  权限申请相关,适配6.0+机型 ,蓝牙,文件,位置 权限
         */
    
        private String[] allPermissionList = {Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN,
                Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
    
    
        /**
         * 遍历出需要获取的权限
         */
        private void requestWritePermission() {
            ArrayList<String> permissionList = new ArrayList<>();
            // 将需要获取的权限加入到集合中  ,根据集合数量判断 需不需要添加
            for (int i = 0; i < allPermissionList.length; i++) {
                if (PackageManager.PERMISSION_DENIED == ContextCompat.checkSelfPermission(this, allPermissionList[i])){
                    permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
                }
            }
    
            String permissionArray[] = new String[permissionList.size()];
            for (int i = 0; i < permissionList.size(); i++) {
                permissionArray[i] = permissionList.get(i);
            }
            if (permissionList.size() > 0)
                ActivityCompat.requestPermissions(this, permissionArray, REQUEST_CODE_PERMISSION);
        }
    
    
        /**
         * 权限申请的回调
         * @param requestCode
         * @param permissions
         * @param grantResults
         */
        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            if (requestCode == REQUEST_CODE_PERMISSION){
                if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        &&grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    //用户同意使用write
    
                }else{
                    //用户不同意,自行处理即可
                    Toast.makeText(XBleActivity.this,"您取消了权限申请,可能会影响软件的使用,如有问题请退出重试",Toast.LENGTH_SHORT).show();
                }
            }
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (data!=null){
                switch (requestCode){
                    case 1:
                        connectDeviceMacList = data.getStringArrayListExtra("data");
                        Log.i("xqxinfo","需要连接的mac"+connectDeviceMacList.toString());
                        //获取设备gatt对象
                        for (int i = 0; i < connectDeviceMacList.size(); i++) {
                            BluetoothGatt gatt = bluetoothAdapter.getRemoteDevice(connectDeviceMacList.get(i)).connectGatt(this, false, new BluetoothGattCallback() {
                            });
                            gattArrayList.add(gatt);
                            Log.i("xqxinfo","添加了"+connectDeviceMacList.get(i));
                        }
                        break;
                }
            }
        }
    
        public void onEventMainThread(RefreshDatas event) {
            txtContentMac.setText("");
            for (int i = 0; i < connectDeviceMacList.size(); i++) {
                txtContentMac.append("Mac:"+connectDeviceMacList.get(i)+"
    ");
                txtContentMac.append("加速度:"+floats[i][3]+"-"+floats[i][4]+"-"+floats[i][5]+"
    ");
                txtContentMac.append("角速度:"+floats[i][6]+"-"+floats[i][7]+"-"+floats[i][8]+"
    ");
                txtContentMac.append("四元素:"+floats[i][13]+"-"+floats[i][14]+"-"+floats[i][15]+"-"+floats[i][16]+"
    ");
                txtContentMac.append("电池电压:"+floats[i][21]+"
    ");
                txtContentMac.append("充电状态:"+floats[i][22]+"
    ");
                txtContentMac.append("低电压报警:"+floats[i][23]+"
    
    ");
            }
        }
    
    
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            EventBus.getDefault().unregister(this);
        }
    }
    XBleActivity.class

    ----------------------------------------------------------------------------------------------------------------------------------------------------------

    项目地址:

    https://github.com/BestCoderXQX/MagkareBle4.0

    项目使用说明:

    1、点击按钮:'选择需要连接的传感器设备'、跳转新界面
    2、点击'开始扫描'按钮,会出现很多设备的mac地址 ,以列表的新式展现
    3、对列表item操作,更改状态'已选择'or'未选择'
    4、点击按钮'完成选择设备'按钮,将列表中状态为'已选择'的mac集合传回上个界面

    5、点击'开始连接'按钮。连接开始,显示连接设备的数据。(注意,这里是按我的传感器来的。实际需要换成你所用到的设备的 数据 转换公式!)

    框架使用说明:

    多设备连接切换设备的使用
    
    1、 关闭当前所有连接的设备
    multiConnectManager.closeAll();
    
    2、  讲设备清出队列
    
            // 当前注册队列中的设备
            List<String> allDevice = ultiConnectManager.getAllDevice();
            Log.i("connentBluetoothX", "所有的的传感器" +allDevice.toString());
    
            for (int i = 0; i < allDevice.size(); i++) {
                multiConnectManager.removeDeviceFromQueue(allDevice.get(i));
                Log.i("connentBluetoothX", "断开的传感器" + allDevice.get(i));
            }
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(3000);
                         // 等待3秒之后连接
                        EventBus.getDefault().post(new ConnectBle(1));
    
                     }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();    
    
    
    当有数据,但是该设备不在已连接的设备集合中时,异常处理
     if (multiConnectManager.getAllConnectingDevice().contains(gatt.getDevice().getAddress())){
                // 如果正在连接中的设备列表中有当前传输数据的传感器
                gatt.disconnect();
                return;
            }
    View Code

    如有问题,欢迎右侧加群。

      

  • 相关阅读:
    android ble 蓝牙4.0开发日志(三)
    android ble 蓝牙4.0开发日志(一)
    android ble 蓝牙4.0开发日志(二)
    Android的界面设计工具——DroidDraw
    discuz x2.5论坛 欢迎新会员不更新解决方法
    Andorid BLE开发
    Android蓝牙聊天,蓝牙通讯
    office tab 9.2和office2013完美结合
    android蓝牙主动发起配对实例
    数据库备份与还原
  • 原文地址:https://www.cnblogs.com/xqxacm/p/7488444.html
Copyright © 2011-2022 走看看