最近项目有个需求,手机设备连接多个蓝牙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.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"></android.support.v7.widget.RecyclerView><Buttonandroid:id="@+id/btnScan"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="开始扫描"/><Buttonandroid:id="@+id/btnStopScan"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="停止扫描"/><Buttonandroid: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"><Buttonandroid:id="@+id/btnSelectDevice"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="选择需要连接的传感器设备"/><Buttonandroid:id="@+id/btnStartConnect"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="开始连接"/><TextViewandroid: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() {@Overridepublic void onScanOver() {}});scanManager.setScanCallbackCompat(new ScanCallbackCompat() {@Overridepublic void onBatchScanResults(List<ScanResultCompat> results) {super.onBatchScanResults(results);}@Overridepublic void onScanFailed(final int errorCode) {super.onScanFailed(errorCode);}@Overridepublic 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() {@Overridepublic 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集合
@Overrideprotected 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() {@Overridepublic void onScanOver() {}});scanManager.setScanCallbackCompat(new ScanCallbackCompat() {@Overridepublic void onBatchScanResults(List<ScanResultCompat> results) {super.onBatchScanResults(results);}@Overridepublic void onScanFailed(final int errorCode) {super.onScanFailed(errorCode);}@Overridepublic 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() {@Overridepublic 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);}@Overridepublic 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;}}@Overrideprotected 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);}@Overrideprotected 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"><TextViewandroid:id="@+id/txtMac"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text=""android:layout_centerVertical="true"/><TextViewandroid:id="@+id/txtState"android:text="未选择"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"/><Viewandroid: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*/@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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; // q1floats[position][14] = ((((short) value[5]) << 8) | ((short) value[4] & 0xff)) / 32768.0f; // q2floats[position][15] = ((((short) value[7]) << 8) | ((short) value[6] & 0xff)) / 32768.0f; // q3floats[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集合
@Overrideprotected 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);}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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; // q1floats[position][14] = ((((short) value[5]) << 8) | ((short) value[4] & 0xff)) / 32768.0f; // q2floats[position][15] = ((((short) value[7]) << 8) | ((short) value[6] & 0xff)) / 32768.0f; // q3floats[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*/@Overridepublic 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();}}}@Overrideprotected 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");}}@Overrideprotected 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、点击'开始连接'按钮。连接开始,显示连接设备的数据。(注意,这里是按我的传感器来的。实际需要换成你所用到的设备的 数据 转换公式!)

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

  

Android项目实战(三十四):蓝牙4.0 BLE 多设备连接相关推荐

  1. Android项目实战(十六):QQ空间实现(一)—— 展示说说中的评论内容并有相应点击事件...

    大家都玩QQ空间客户端,对于每一个说说,我们都可以评论,那么,对于某一条评论: 白雪公主 回复 小矮人 : 你们好啊~ 我们来分析一下: 1.QQ空间允许我们 点击 回复人和被回复人的名字就可以进入对 ...

  2. Android Studio第三十四期 - git企业级应用命令

    1.git status 2.git add -A 3.git commit -m "merger" 4.git pull 5.git push origin master 6.g ...

  3. Android项目实战(十五):自定义不可滑动的ListView和GridView

    不可滑动的ListView (RecyclweView类似) public class NoScrollListView extends ListView {public NoScrollListVi ...

  4. Android开发:IBeacon系列——安卓蓝牙4.0(BLE)开发之检测IBeacon热点初步

    检测ibeacon热点信号 软硬件要求:Android4.3及以上中支持BLE技术,同时蓝牙需要满足Bluetooth4.0及以上. iBeacon的工作原理是基于Bluetooth Low Ener ...

  5. android圆角对话框,Android项目实战(三十二):圆角对话框Dialog

    原文: Android项目实战(三十二):圆角对话框Dialog 前言:html 项目中多处用到对话框,用系统对话框太难看,就本身写一个自定义对话框.android 对话框包括:一.圆角程序员 二.a ...

  6. Vue + Spring Boot 项目实战(十五):动态加载后台菜单

    重要链接: 「系列文章目录」 「项目源码(GitHub)」 本篇目录 前言 一.后端实现 1.表设计 2.pojo 3.菜单查询接口(树结构查询) 二.前端实现 1.后台页面设计 2.数据处理 3.添 ...

  7. Android项目实战(三十八):2017最新 将AndroidLibrary提交到JCenter仓库(图文教程)...

    Android项目实战(三十八):2017最新 将AndroidLibrary提交到JCenter仓库(图文教程) 原文:Android项目实战(三十八):2017最新 将AndroidLibrary ...

  8. Android项目实战(三十二):圆角对话框Dialog

    原文:Android项目实战(三十二):圆角对话框Dialog 前言: 项目中多处用到对话框,用系统对话框太难看,就自己写一个自定义对话框. 对话框包括:1.圆角 2.app图标 , 提示文本,关闭对 ...

  9. Android项目实战(四十):在线生成按钮Shape的网站

    原文: Android项目实战(四十):在线生成按钮Shape的网站 Android Button Make  右侧设置按钮的属性,可以即时看到效果,并即时生成对应的.xml 代码,非常高效(当然熟练 ...

  10. 【Android项目实战 | 从零开始写app(十二)】实现app首页智慧服务热门推荐热门主题、新闻

    说在前面,由于各种adapter,xml布局,bean实体类,Activity,也为了让看懂,代码基本都是"简单粗暴直接不好看",没啥okhttp和util工具类之类的封装,本篇幅 ...

最新文章

  1. Python3 pathlib让编程更美好
  2. 360无痕浏览器_高效使用浏览器,探索你不知道的实用功能
  3. Android之对Volley网络框架的一些理解
  4. 国内可用的NTP服务器地址列表 网络时间 服务器
  5. .net 基元类型,引用类型和值类型
  6. MapReduce详解和WordCount模拟
  7. excel 字符串拼接_Excel教程:应收账款自动弹窗提醒
  8. Android 驱动(8)---简单实例讲解linux的module模块编译步骤
  9. python打开jpg照片_python打开图像
  10. python与r语言处理excel数据_【R语言】批量读取Excel数据并合并(升级版)
  11. 设计模式——责任链模式(职责链模式)
  12. Pspice轨迹命令
  13. 了解一下PMO项目管理岗
  14. 易基因|表观发育:ChIP-seq揭示精子H3K4me3可传递到胚胎并与代谢功能障碍遗传有关
  15. 牛牛战队的比赛地 (三分)
  16. 我的世界 服务器文件ess,求助服务器ess插件报错怎么解
  17. 华为服务器在哪里看型号,服务器型号怎么看
  18. APP逆向之易班(第一篇)
  19. 【C语言】图像处理-揭秘电影特效,随心所欲,合成图像
  20. Vue CKEditor5 快速了解并使用

热门文章

  1. 黑马程序员————java中面向对象的三大特性
  2. js动态修改onclick的响应函数后,IE无效的解决方案
  3. 问题记录:图片加载快速滑动闪动问题(Android-APP)
  4. Linux设备驱动模型-Driver
  5. Android 四大组件学习之ContentProvider二
  6. Docker容器实战(七) - 容器中进程视野下的文件系统
  7. 用glew,glfw,FreeImag实现opengl画图-第五课 摄像机
  8. c++中assert
  9. 金币 详解(C++)
  10. 计算机网络暗地里范围,《计算机网络应用技术教程》期中试题.doc