最近项目有个需求,手机设备连接多个蓝牙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=""
> </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(, 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 = ; // 权限请求码 用于回调 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 = ; 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 = ; i < permissionList.size(); i++) {
permissionArray[i] = permissionList.get(i);
}
if (permissionList.size() > )
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[].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)
&&grantResults[] == 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( * * )
.setBackgroundScanPeriod()
.setForegroundBetweenScanPeriod()
.setForegroundScanPeriod()
.setDebugMode(BuildConfig.DEBUG)
.setMaxConnectDeviceNum() //最大可以连接的蓝牙设备个数
.setReconnectBaseSpaceTime()
.setReconnectMaxTimes(Integer.MAX_VALUE)
.setReconnectStrategy(ConnectConfig.RECONNECT_LINE_EXPONENT)
.setReconnectedLineToExponentTimes()
.setConnectTimeOutTimes()
.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 = ; i < gattArrayList.size(); i++) {
multiConnectManager.startSubscribe(gattArrayList.get(i));
} multiConnectManager.startConnect(); } /**
* 处理回调的数据
* @param gatt
* @param characteristic
*/ float[][] floats = new float[][]; private void dealCallDatas(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
int position = connectDeviceMacList.indexOf(gatt.getDevice().getAddress());
//第一个传感器数据
byte[] value = characteristic.getValue();
if (value[] != 0x55) {
//开头不是0x55的数据删除
return;
}
switch (value[]) {
case 0x61:
//加速度数据
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //x轴
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //y轴
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //z轴
//角速度数据
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //x轴
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //x轴
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //x轴
break;
case 0x62:
//四元素
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f; // q1
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f; // q2
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f; // q3
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f; // q4
//电池电压
floats[position][] = (float) 1.2 * * (((value[] << ) | value[]) + ) / ;
//充电状态
floats[position][] = value[];
//低电压报警
floats[position][] = value[];
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 = ; // 权限请求码 用于回调 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,);
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 = ; i < gattArrayList.size(); i++) {
multiConnectManager.startSubscribe(gattArrayList.get(i));
} multiConnectManager.startConnect(); } /**
* 处理回调的数据
* @param gatt
* @param characteristic
*/ float[][] floats = new float[][]; private void dealCallDatas(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
int position = connectDeviceMacList.indexOf(gatt.getDevice().getAddress());
//第一个传感器数据
byte[] value = characteristic.getValue();
if (value[] != 0x55) {
//开头不是0x55的数据删除
return;
}
switch (value[]) {
case 0x61:
//加速度数据
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //x轴
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //y轴
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //z轴
//角速度数据
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //x轴
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //x轴
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f * ; //x轴
break;
case 0x62:
//四元素
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f; // q1
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f; // q2
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f; // q3
floats[position][] = ((((short) value[]) << ) | ((short) value[] & 0xff)) / 32768.0f; // q4
//电池电压
floats[position][] = (float) 1.2 * * (((value[] << ) | value[]) + ) / ;
//充电状态
floats[position][] = value[];
//低电压报警
floats[position][] = value[];
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( * * )
.setBackgroundScanPeriod()
.setForegroundBetweenScanPeriod()
.setForegroundScanPeriod()
.setDebugMode(BuildConfig.DEBUG)
.setMaxConnectDeviceNum() //最大可以连接的蓝牙设备个数
.setReconnectBaseSpaceTime()
.setReconnectMaxTimes(Integer.MAX_VALUE)
.setReconnectStrategy(ConnectConfig.RECONNECT_LINE_EXPONENT)
.setReconnectedLineToExponentTimes()
.setConnectTimeOutTimes()
.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 = ; 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 = ; i < permissionList.size(); i++) {
permissionArray[i] = permissionList.get(i);
}
if (permissionList.size() > )
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[].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)
&&grantResults[] == 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 :
connectDeviceMacList = data.getStringArrayListExtra("data");
Log.i("xqxinfo","需要连接的mac"+connectDeviceMacList.toString());
//获取设备gatt对象
for (int i = ; 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 = ; i < connectDeviceMacList.size(); i++) {
txtContentMac.append("Mac:"+connectDeviceMacList.get(i)+"\n");
txtContentMac.append("加速度:"+floats[i][]+"-"+floats[i][]+"-"+floats[i][]+"\n");
txtContentMac.append("角速度:"+floats[i][]+"-"+floats[i][]+"-"+floats[i][]+"\n");
txtContentMac.append("四元素:"+floats[i][]+"-"+floats[i][]+"-"+floats[i][]+"-"+floats[i][]+"\n");
txtContentMac.append("电池电压:"+floats[i][]+"\n");
txtContentMac.append("充电状态:"+floats[i][]+"\n");
txtContentMac.append("低电压报警:"+floats[i][]+"\n\n");
}
} @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、点击'开始连接'按钮。连接开始,显示连接设备的数据。(注意,这里是按我的传感器来的。实际需要换成你所用到的设备的 数据 转换公式!)

框架使用说明:

多设备连接切换设备的使用

、 关闭当前所有连接的设备
multiConnectManager.closeAll(); 、 讲设备清出队列 // 当前注册队列中的设备
List<String> allDevice = ultiConnectManager.getAllDevice();
Log.i("connentBluetoothX", "所有的的传感器" +allDevice.toString()); for (int i = ; 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();
// 等待3秒之后连接
EventBus.getDefault().post(new ConnectBle()); }
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start(); 当有数据,但是该设备不在已连接的设备集合中时,异常处理
if (multiConnectManager.getAllConnectingDevice().contains(gatt.getDevice().getAddress())){
// 如果正在连接中的设备列表中有当前传输数据的传感器
gatt.disconnect();
return;
}

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

  

Android项目实战(三十四):蓝牙4.0 BLE 多设备连接的更多相关文章

  1. Android项目实战(十四):TextView显示html样式的文字

    项目需求: TextView显示一段文字,格式为:(消息个数,不确定)条消息 这段文字中名字和数字的长度是不确定的,还要求名字和数字各自有各自的颜色. 一开始我想的是用(转) SpannableStr ...

  2. 【慕课网实战】Spark Streaming实时流处理项目实战笔记十四之铭文升级版

    铭文一级: 第11章 Spark Streaming整合Flume&Kafka打造通用流处理基础 streaming.conf agent1.sources=avro-sourceagent1 ...

  3. android 项目学习随笔十四(WebView)

    1.布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro ...

  4. Android项目实战(十九):Android Studio 优秀插件: Parcelable Code Generator

    Android Studio 优秀插件系列: Android Studio 优秀插件(一):GsonFormat Android Studio 优秀插件(二): Parcelable Code Gen ...

  5. Android项目实战(十):自定义倒计时的TextView

    项目总结 -------------------------------------------------------------------------------------------- 有这 ...

  6. <Android 基础(三十四)> TabLayout 从头到脚

    1. 简介 1.TabLayout给我们提供的是一排横向的标签页 2.#newTab()这个方法来创建新的标签页,然后用过#setText()和#setIcon方法分别修改标签页的文本和图标,创建完成 ...

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

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

  8. Android项目实战(十二):解决OOM的一种偷懒又有效的办法

    在程序的manifest文件的application节点加入android:largeHeap=“true” 即可. 对,只需要一句话! 那么这行代码的意思是什么呢? 简单的说就是使该APP获取最大可 ...

  9. Android笔记(三十四) Android中线程之间的通信(六)Handle中的post()方法详解

    我们之前都是使用sendMessage()方法来发送消息,使用handleMessage来处理消息的,今天我们来看另外一种方法,先看代码: package cn.lixyz.handlertest; ...

随机推荐

  1. iOS开发实战-基于SpriteKit的FlappyBird小游戏

    写在前面 最近一直在忙自己的维P恩的事情 公司项目也是一团乱 于是...随手找了个游戏项目改了改就上线了,就当充数了. SpriteKit简介 SpriteKit是iOS 7之后苹果推出的2D游戏框架 ...

  2. Excel 一键上传到数据库

    <a class="edit"  id="batchImport">   批量导入  </a> js代码弹窗: $("#bat ...

  3. 测试环境-memcached安装与说明

    一,下载memcached和libevent安装包: 1,Memcache用到了libevent这个库用于Socket的处理,所以还需要安装libevent (这两个包百度上都能找到) 二,安装lib ...

  4. VM虚拟机的配置

    1.软件安装 点击如图所示文件安装虚拟机 点击下一步,再安装过程中输入密钥 1F04Z-6D111-7Z029-AV0Q4-3AEH8 设置相关内容完成安装 2.加载虚拟机 点击软件图标打开软件 软件 ...

  5. NYOJ 69 数的长度(数学)

    数的长度 时间限制:3000 ms  |  内存限制:65535 KB 难度:1   描述 N!阶乘是一个非常大的数,大家都知道计算公式是N!=N*(N-1)······*2*1.现在你的任务是计算出 ...

  6. Selenium对于对话框alert,confirm,prompt的处理

    html 源码: <html>                  <head>                      <title>Alert</titl ...

  7. 基于react全家桶+antd-design+webpack2+node+express+mongodb开发的前后台博客系统

    很久没更新博客,最近也有点忙,然后业余时间搞了一个比较完整基于react全家桶+antd-design+webpack2+node+express+mongodb开发的前后台博客系统的流程系统,希望对 ...

  8. C#使用Xamarin开发可移植移动应用(3.Xamarin.Views控件)附源码

    前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. .NET ...

  9. android学习笔记Fragment的使用

    Fragment的内容感觉好多啊,主要需要掌握Fragment静态加载,Fragment动态加载,Fragment的生命周期,Fragment与Activity的交互 1,Fragment的静态加载 ...

  10. Oracle外键需要建索引吗?

    关于Oracle中的外键,首先要说明一下. 1. 除非已定义了父表主键或唯一键约束,否则oracle将不允许创建子表的外键约束. 2. 在定义外键约束时,oracle不会自动创建索引,所以必须手动在与 ...