Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

书史足自悦,安用勤与劬。这篇文章主要讲述Android项目实战(三十四):蓝牙4.0 BLE 多设备连接相关的知识,希望能为你提供帮助。
原文:Android项目实战(三十四):蓝牙4.0 BLE 多设备连接最近项目有个需求,手机设备连接多个蓝牙4.0 设备 并获取这些设备的数据。
查询了很多资料终于实现,现进行总结。
 
---------------------------------------------------------------------------------------------------------------------------------------------------------------
    从零开始实现一个连接多个蓝牙4.0 设备并获取数据的 Demo
注:如果不想看实现过程的,直接看最下面的demo源码即可,或每一步后相关操作步骤的完整代码。

【Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)】一、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地址传回 
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片

 
< ?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、数据展示

Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片

 
布局文件代码:

< ?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)、蓝牙扫描的初始化设置
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
/** * 初始化蓝牙相关配置 */ 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全部代码:
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

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

SelectDeviceActivity.class适配器相关代码:
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
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 [email  protected] * 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适配器布局代码:
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

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

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


2、关键代码
1、权限适配
注意:不止蓝牙权限,位置权限也需要打开
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
/** * @author xqx * @email [email  protected] * 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、蓝牙开启、连接等 初始化设置
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
/** * 对蓝牙的初始化操作 */ 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()); }

initBle3、开始连接操作
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
/** * 连接需要连接的传感器 * @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 = https://www.songbingjia.com/android/characteristic.getValue(); if (value[0] != 0x55) { //开头不是0x55的数据删除 return; } switch (value[1]) { case 0x61: //加速度数据 floats[position][3] = ((((short) value[3]) < < 8) | ((short) value[2] & 0xff)) / 32768.0f * 16; //x轴 floats[position][4] = ((((short) value[5]) < < 8) | ((short) value[4] & 0xff)) / 32768.0f * 16; //y轴 floats[position][5] = ((((short) value[7]) < < 8) | ((short) value[6] & 0xff)) / 32768.0f * 16; //z轴 //角速度数据 floats[position][6] = ((((short) value[9]) < < 8) | ((short) value[8] & 0xff)) / 32768.0f * 2000; //x轴 floats[position][7] = ((((short) value[11]) < < 8) | ((short) value[10] & 0xff)) / 32768.0f * 2000; //x轴 floats[position][8] = ((((short) value[13]) < < 8) | ((short) value[12] & 0xff)) / 32768.0f * 2000; //x轴 break; case 0x62: //四元素 floats[position][13] = ((((short) value[3]) < < 8) | ((short) value[2] & 0xff)) / 32768.0f; // q1 floats[position][14] = ((((short) value[5]) < < 8) | ((short) value[4] & 0xff)) / 32768.0f; // q2 floats[position][15] = ((((short) value[7]) < < 8) | ((short) value[6] & 0xff)) / 32768.0f; // q3 floats[position][16] = ((((short) value[9]) < < 8) | ((short) value[8] & 0xff)) / 32768.0f; // q4 //电池电压 floats[position][21] = (float) 1.2 * 4 * (((value[11] < < 8) | value[10]) + 1) / 1024; //充电状态 floats[position][22] = value[12]; //低电压报警 floats[position][23] = value[14]; break; } EventBus.getDefault().post(new RefreshDatas()); // 发送消息,更新UI 显示数据 }

connectBle 
activity全部代码:
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
Android项目实战(三十四)(蓝牙4.0 BLE 多设备连接)

文章图片
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 [email  protected] * 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 = https://www.songbingjia.com/android/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 [email  protected] * 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); } }

XBleActivity.class 
----------------------------------------------------------------------------------------------------------------------------------------------------------
 
项目地址:
https://github.com/BestCoderXQX/MagkareBle4.0
 
项目使用说明:
1、点击按钮:‘选择需要连接的传感器设备‘、跳转新界面
2、点击‘开始扫描‘按钮,会出现很多设备的mac地址 ,以列表的新式展现
3、对列表item操作,更改状态‘已选择‘or‘未选择‘
4、点击按钮‘完成选择设备‘按钮,将列表中状态为‘已选择‘的mac集合传回上个界面
5、点击‘开始连接‘按钮。连接开始,显示连接设备的数据。(注意,这里是按我的传感器来的。实际需要换成你所用到的设备的 数据 转换公式!)
 
如有问题,欢迎右侧加群。






    推荐阅读