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();
        }
    }
}

  适配器相关代码:

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);

    }
}

  适配器布局代码:

<?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>

    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());
    }

    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 显示数据
    }

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)+"\n");
            txtContentMac.append("加速度:"+floats[i][3]+"-"+floats[i][4]+"-"+floats[i][5]+"\n");
            txtContentMac.append("角速度:"+floats[i][6]+"-"+floats[i][7]+"-"+floats[i][8]+"\n");
            txtContentMac.append("四元素:"+floats[i][13]+"-"+floats[i][14]+"-"+floats[i][15]+"-"+floats[i][16]+"\n");
            txtContentMac.append("电池电压:"+floats[i][21]+"\n");
            txtContentMac.append("充电状态:"+floats[i][22]+"\n");
            txtContentMac.append("低电压报警:"+floats[i][23]+"\n\n");
        }
    }



    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }
}

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

项目地址:

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

项目使用说明:

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

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏懒人开发

BottomNavigationView简单使用

之前见过类似这个库, 是带ripple效果的 不记得具体地址了,和这个类似 https://github.com/Ashok-Varma/BottomNav...

963
来自专栏腾讯Bugly的专栏

Android APP 快速 Pad 化实现

如何能在最快的时间内,实现一个最新版本 Android app 的 pad 化呢?从拿到一个大型手机 app 代码开始开发到第一个其全新 pad 版本的发布,我...

6046
来自专栏Android先生

Activity、View、Window的理解一篇文章就够了

要了解这三者之间的关系,我们带着问题通过分析源码一步一步来揭开它们的神秘面纱! 文章有点长,首先要理解Activity、View、Window,我提出了一些问题...

1041
来自专栏haifeiWu与他朋友们的专栏

Dagger Android支持库(译文)

与其他依赖注入框架相比,Dagger 2 最大的优点是他不使用反射,严格的生成实现类,这意味着他可以使用在 Android 应用上。但是在Android上使用仍...

1123
来自专栏Android干货

安卓开发_实现截图功能

3056
来自专栏Android随笔

一个基础的Android项目

AndroidBasicProject是一个简易的Android基础项目,方便您快速进行开发。 包含以下内容:

862
来自专栏Java学习网

Android实现图片异步加载操作

Android实现图片异步加载操作 在Android开发过程中,为了防止阻塞UI,图片加载时经常采用异步的方法来加载,异步加载图片的主要流程是进行判断缓存中是...

2449
来自专栏Android先生

Fragment全解析系列(二):正确的使用姿势

Fragment是可以让你的app纵享丝滑的设计,如果你的app想在现在基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fragme...

1533
来自专栏everhad

札记:Fragment基础

Fragment概述 在Fragment出现之前,Activity是app中界面的基本组成单位,值得一提的是,作为四大组件之一,它是需要“注册”的。组件的特性使...

2206
来自专栏梦里茶室

Activity四种launchMode

总共有四篇关于Activity,task,launchMode的文章,可以在我的博客中查看,或者在文章底部点下一篇。 1.Standard 默认模式,多次实例化...

1969

扫码关注云+社区

领取腾讯云代金券