原文: Android項目實戰(三十四):藍牙4.0 BLE 多裝置連接配接
最近項目有個需求,手機裝置連接配接多個藍牙4.0 裝置 并擷取這些裝置的資料。
查詢了很多資料終于實作,現進行總結。
---------------------------------------------------------------------------------------------------------------------------------------------------------------
從零開始實作一個連接配接多個藍牙4.0 裝置并擷取資料的 Demo
注:如果不想看實作過程的,直接看最下面的demo源碼即可,或每一步後相關操作步驟的完整代碼。
一、Demo需求
1、搜尋裝置 , 選擇多個要連接配接的裝置。
2、開始連接配接,顯示資料。
二、項目知識儲備
項目中需要用到的三方:
1、RecyclerView
清單,用于顯示掃描得到的所有藍牙裝置
2、
BaseRecyclerViewAdapterHelperRecyclerview 幫助架構,快速實作清單操作
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 [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
擴充卡相關代碼:
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
擴充卡布局代碼:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
>
<TextView
android:id="@+id/txtMac"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:layout_centerVertical="true"
/>
<TextView
android:id="@+id/txtState"
android:text="未選擇"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#fff"
android:layout_alignParentBottom="true"
></View>
</RelativeLayout>
item_device.xml
2、連接配接多裝置,擷取資料并展示Activity
(1)、變量聲明
private Button btnSelectDevice ; //選擇需要綁定的裝置
private Button btnStartConnect ; //開始連接配接按鈕
private TextView txtContentMac ; //擷取到的資料解析結果顯示
private final int REQUEST_CODE_PERMISSION = 1; // 權限請求碼 用于回調
MultiConnectManager multiConnectManager ; //多裝置連接配接
private BluetoothAdapter bluetoothAdapter; //藍牙擴充卡
private ArrayList<String> connectDeviceMacList ; //需要連接配接的mac裝置集合
ArrayList<BluetoothGatt> gattArrayList; //裝置gatt集合
2、關鍵代碼
1、權限适配
注意:不止藍牙權限,位置權限也需要打開
/**
* @author xqx
* @email [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、藍牙開啟、連接配接等 初始化設定
/**
* 對藍牙的初始化操作
*/
private void initConfig() {
multiConnectManager = BleManager.getMultiConnectManager(this);
// 擷取藍牙擴充卡
try {
// 擷取藍牙擴充卡
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
//
if (bluetoothAdapter == null) {
Toast.makeText(this, "藍牙不可用", Toast.LENGTH_LONG).show();
return;
}
// 藍牙沒打開的時候打開藍牙
if (!bluetoothAdapter.isEnabled())
bluetoothAdapter.enable();
}catch (Exception err){};
BleManager.setBleParamsOptions(new BleParamsOptions.Builder()
.setBackgroundBetweenScanPeriod(5 * 60 * 1000)
.setBackgroundScanPeriod(10000)
.setForegroundBetweenScanPeriod(2000)
.setForegroundScanPeriod(10000)
.setDebugMode(BuildConfig.DEBUG)
.setMaxConnectDeviceNum(7) //最大可以連接配接的藍牙裝置個數
.setReconnectBaseSpaceTime(1000)
.setReconnectMaxTimes(Integer.MAX_VALUE)
.setReconnectStrategy(ConnectConfig.RECONNECT_LINE_EXPONENT)
.setReconnectedLineToExponentTimes(5)
.setConnectTimeOutTimes(20000)
.build());
}
initBle
3、開始連接配接操作
/**
* 連接配接需要連接配接的傳感器
* @param
*/
private void connentBluetooth(){
String[] objects = connectDeviceMacList.toArray(new String[connectDeviceMacList.size()]);
multiConnectManager.addDeviceToQueue(objects);
multiConnectManager.addConnectStateListener(new ConnectStateListener() {
@Override
public void onConnectStateChanged(String address, ConnectState state) {
switch (state){
case CONNECTING:
Log.i("connectStateX","裝置:"+address+"連接配接狀态:"+"正在連接配接");
break;
case CONNECTED:
Log.i("connectStateX","裝置:"+address+"連接配接狀态:"+"成功");
break;
case NORMAL:
Log.i("connectStateX","裝置:"+address+"連接配接狀态:"+"失敗");
break;
}
}
});
/**
* 資料回調
*/
multiConnectManager.setBluetoothGattCallback(new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
dealCallDatas(gatt , characteristic);
}
});
multiConnectManager.setServiceUUID("0000ffe5-0000-1000-8000-00805f9a34fb");
multiConnectManager.addBluetoothSubscribeData(
new BluetoothSubScribeData.Builder().setCharacteristicNotify(UUID.fromString("0000ffe4-0000-1000-8000-00805f9a34fb")).build());
//還有讀寫descriptor
//start descriptor(注意,在使用時當回調onServicesDiscovered成功時會自動調用該方法,是以隻需要在連接配接之前完成1,3步即可)
for (int i = 0; i < gattArrayList.size(); i++) {
multiConnectManager.startSubscribe(gattArrayList.get(i));
}
multiConnectManager.startConnect();
}
/**
* 處理回調的資料
* @param gatt
* @param characteristic
*/
float[][] floats = new float[7][30];
private void dealCallDatas(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
int position = connectDeviceMacList.indexOf(gatt.getDevice().getAddress());
//第一個傳感器資料
byte[] value = characteristic.getValue();
if (value[0] != 0x55) {
//開頭不是0x55的資料删除
return;
}
switch (value[1]) {
case 0x61:
//加速度資料
floats[position][3] = ((((short) value[3]) << 8) | ((short) value[2] & 0xff)) / 32768.0f * 16; //x軸
floats[position][4] = ((((short) value[5]) << 8) | ((short) value[4] & 0xff)) / 32768.0f * 16; //y軸
floats[position][5] = ((((short) value[7]) << 8) | ((short) value[6] & 0xff)) / 32768.0f * 16; //z軸
//角速度資料
floats[position][6] = ((((short) value[9]) << 8) | ((short) value[8] & 0xff)) / 32768.0f * 2000; //x軸
floats[position][7] = ((((short) value[11]) << 8) | ((short) value[10] & 0xff)) / 32768.0f * 2000; //x軸
floats[position][8] = ((((short) value[13]) << 8) | ((short) value[12] & 0xff)) / 32768.0f * 2000; //x軸
break;
case 0x62:
//四元素
floats[position][13] = ((((short) value[3]) << 8) | ((short) value[2] & 0xff)) / 32768.0f; // q1
floats[position][14] = ((((short) value[5]) << 8) | ((short) value[4] & 0xff)) / 32768.0f; // q2
floats[position][15] = ((((short) value[7]) << 8) | ((short) value[6] & 0xff)) / 32768.0f; // q3
floats[position][16] = ((((short) value[9]) << 8) | ((short) value[8] & 0xff)) / 32768.0f; // q4
//電池電壓
floats[position][21] = (float) 1.2 * 4 * (((value[11] << 8) | value[10]) + 1) / 1024;
//充電狀态
floats[position][22] = value[12];
//低電壓報警
floats[position][23] = value[14];
break;
}
EventBus.getDefault().post(new RefreshDatas()); // 發送消息,更新UI 顯示資料
}
connectBle
activity全部代碼:
package com.maiji.magkareble40;
import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.blakequ.bluetooth_manager_lib.BleManager;
import com.blakequ.bluetooth_manager_lib.BleParamsOptions;
import com.blakequ.bluetooth_manager_lib.connect.BluetoothSubScribeData;
import com.blakequ.bluetooth_manager_lib.connect.ConnectConfig;
import com.blakequ.bluetooth_manager_lib.connect.ConnectState;
import com.blakequ.bluetooth_manager_lib.connect.ConnectStateListener;
import com.blakequ.bluetooth_manager_lib.connect.multiple.MultiConnectManager;
import java.util.ArrayList;
import java.util.UUID;
import de.greenrobot.event.EventBus;
/**
* @author xqx
* @email [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 = 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、點選'開始連接配接'按鈕。連接配接開始,顯示連接配接裝置的資料。(注意,這裡是按我的傳感器來的。實際需要換成你所用到的裝置的 資料 轉換公式!)
如有問題,歡迎右側加群。