依赖FastBle开源库
implementation ‘com.github.Jasonchenlijian:FastBle:2.4.0’
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_CODE_OPEN_GPS = 1;
private static final int REQUEST_CODE_PERMISSION_LOCATION = 2;
private DeviceAdapter mDeviceAdapter;
private BleDevice bleDevice;
private String serviceUUID, notifyUUID, writeUUID;
int a = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BleInstance.getInstance().initBle();
Button btn_scan = (Button) findViewById(R.id.btn_scan);
btn_scan.setText("开始扫描");
btn_scan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (btn_scan.getText().equals("开始扫描")) {
checkPermissions();
} else if (btn_scan.getText().equals("停止扫描")) {
BleManager.getInstance().cancelScan();
}
}
});
Button btn_check = (Button) findViewById(R.id.btn_check);
btn_check.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// BleInstance.getInstance().bleTestConnect(bleDevice,serviceUUID,writeUUID);
// BleInstance.getInstance().heartbeat(bleDevice,serviceUUID,writeUUID);
// if (a == 1){
// a = 2;
// BleInstance.getInstance().timeBack(bleDevice,serviceUUID,writeUUID);
// } else {
// a = 1;
// BleInstance.getInstance().cancelTimeBack(bleDevice,serviceUUID,writeUUID);
// }
BleInstance.getInstance().getBleData(bleDevice, serviceUUID, writeUUID);
}
});
mDeviceAdapter = new DeviceAdapter(this);
mDeviceAdapter.setOnDeviceClickListener(new DeviceAdapter.OnDeviceClickListener() {
@Override
public void onConnect(BleDevice bleDevice) {
if (!BleManager.getInstance().isConnected(bleDevice)) {
BleManager.getInstance().cancelScan();
connect(bleDevice);
}
}
@Override
public void onDisConnect(final BleDevice bleDevice) {
if (BleManager.getInstance().isConnected(bleDevice)) {
BleManager.getInstance().disconnect(bleDevice);
}
}
@Override
public void onDetail(BleDevice bleDevice) {
BleInstance.getInstance().heartbeat(bleDevice, serviceUUID, writeUUID);
// if (BleManager.getInstance().isConnected(bleDevice)) {
// Intent intent = new Intent(MainActivity.this, OperationActivity.class);
// intent.putExtra(OperationActivity.KEY_DATA, bleDevice);
// startActivity(intent);
// }
}
});
ListView listView_device = (ListView) findViewById(R.id.list_device);
listView_device.setAdapter(mDeviceAdapter);
}
private void connect(final BleDevice bleDevice) {
BleManager.getInstance().connect(bleDevice, gattCallback);
}
/**
* 连接回调 获取特性值信息
*/
private BleGattCallback gattCallback = new BleGattCallback() {
@Override
public void onStartConnect() {
// progressDialog.show();
}
@Override
public void onConnectFail(BleDevice bleDevice, BleException exception) {
// img_loading.clearAnimation();
// img_loading.setVisibility(View.INVISIBLE);
// btn_scan.setText(getString(R.string.start_scan));
// progressDialog.dismiss();
Toast.makeText(MainActivity.this, getString(R.string.connect_fail), Toast.LENGTH_LONG).show();
}
@Override
public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
// progressDialog.dismiss();
mDeviceAdapter.addDevice(bleDevice);
mDeviceAdapter.notifyDataSetChanged();
Toast.makeText(MainActivity.this, getString(R.string.connect_success), Toast.LENGTH_LONG).show();
for (int i = 0; i < gatt.getServices().size(); i++) {
BluetoothGattService service = gatt.getServices().get(i);
for (int j = 0; j < service.getCharacteristics().size(); j++) {
BluetoothGattCharacteristic characteristic = service.getCharacteristics().get(j);
Log.i("haha", "======characteristic.getProperties()==========>" + characteristic.getProperties());
int charaProp = characteristic.getProperties();
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
writeUUID = characteristic.getUuid().toString();
Log.i("haha", "======writeUUID==========>" + writeUUID);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
Log.i("haha", "======service==========>" + service.getUuid());
Log.i("haha", "======characteristic==========>" + characteristic.getUuid());
MainActivity.this.bleDevice = bleDevice;
serviceUUID = service.getUuid().toString();
notifyUUID = characteristic.getUuid().toString();
//启用通知
BleManager.getInstance().notify(bleDevice, serviceUUID, notifyUUID, BleInstance.getInstance().notifyCallback);
//验证连接状态
new Handler().postDelayed(() -> BleInstance.getInstance().bleTestConnect(bleDevice, serviceUUID, writeUUID), 200);
}
}
}
}
@Override
public void onDisConnected(boolean isActiveDisConnected, BleDevice bleDevice, BluetoothGatt gatt, int status) {
// progressDialog.dismiss();
mDeviceAdapter.removeDevice(bleDevice);
mDeviceAdapter.notifyDataSetChanged();
if (isActiveDisConnected) {
Toast.makeText(MainActivity.this, getString(R.string.active_disconnected), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, getString(R.string.disconnected), Toast.LENGTH_LONG).show();
// ObserverManager.getInstance().notifyObserver(bleDevice);
}
}
};
private void checkPermissions() {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!bluetoothAdapter.isEnabled()) {
Toast.makeText(this, "请先打开蓝牙", Toast.LENGTH_LONG).show();
return;
}
String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};
List<String> permissionDeniedList = new ArrayList<>();
for (String permission : permissions) {
int permissionCheck = ContextCompat.checkSelfPermission(this, permission);
if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
onPermissionGranted(permission);
} else {
permissionDeniedList.add(permission);
}
}
if (!permissionDeniedList.isEmpty()) {
String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]);
ActivityCompat.requestPermissions(this, deniedPermissions, REQUEST_CODE_PERMISSION_LOCATION);
}
}
@Override
public final void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_CODE_PERMISSION_LOCATION:
if (grantResults.length > 0) {
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
onPermissionGranted(permissions[i]);
}
}
}
break;
}
}
private void onPermissionGranted(String permission) {
switch (permission) {
case Manifest.permission.ACCESS_FINE_LOCATION:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !checkGPSIsOpen()) {
new AlertDialog.Builder(this)
.setTitle(R.string.notifyTitle)
.setMessage(R.string.gpsNotifyMsg)
.setNegativeButton(R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setPositiveButton(R.string.setting,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivityForResult(intent, REQUEST_CODE_OPEN_GPS);
}
})
.setCancelable(false)
.show();
} else {
startScan();
}
break;
}
}
private boolean checkGPSIsOpen() {
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
if (locationManager == null)
return false;
return locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_OPEN_GPS) {
if (checkGPSIsOpen()) {
startScan();
}
}
}
private void startScan() {
BleInstance.getInstance().setScanRule();
BleInstance.getInstance().startScan(scanCallback);
}
private BleScanCallback scanCallback = new BleScanCallback() {
@Override
public void onScanStarted(boolean success) {
mDeviceAdapter.clearScanDevice();
mDeviceAdapter.notifyDataSetChanged();
// img_loading.startAnimation(operatingAnim);
// img_loading.setVisibility(View.VISIBLE);
// btn_scan.setText(getString(R.string.stop_scan));
Log.i("lala", "=onScanStarted====>" + success);
}
@Override
public void onLeScan(BleDevice bleDevice) {
super.onLeScan(bleDevice);
// Log.i("lala", "=onLeScan==Name====>" + bleDevice.getName());
}
@Override
public void onScanning(BleDevice bleDevice) {
mDeviceAdapter.addDevice(bleDevice);
mDeviceAdapter.notifyDataSetChanged();
Log.e("lala", "=onScanning==Name====>" + bleDevice.getName());
}
@Override
public void onScanFinished(List<BleDevice> scanResultList) {
// img_loading.clearAnimation();
// img_loading.setVisibility(View.INVISIBLE);
// btn_scan.setText(getString(R.string.start_scan));
}
};
@Override
protected void onDestroy() {
super.onDestroy();
BleInstance.getInstance().closeBle();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout
android:id="@+id/rl_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/btn_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始扫描" />
<Button
android:id="@+id/btn_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="测试按钮" />
<ImageView
android:id="@+id/img_loading"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:src="@mipmap/ic_loading"
android:visibility="invisible" />
</RelativeLayout>
<ListView
android:id="@+id/list_device"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:divider="@android:color/darker_gray"
android:dividerHeight="0.5dp"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:scrollbars="none"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rl_title"/>
</androidx.constraintlayout.widget.ConstraintLayout>
BleInstance.java
public class BleInstance {
private List<Byte> mBytes = new ArrayList<>();
public static BleInstance getInstance() {
return BleInstance.BleManagerHolder.sBleManager;
}
private static class BleManagerHolder {
private static final BleInstance sBleManager = new BleInstance();
}
public void initBle() {
BleManager.getInstance().init(App.getInstance());
BleManager.getInstance()
.enableLog(true)
.setReConnectCount(1, 5000)
.setConnectOverTime(20000)
.setOperateTimeout(5000);
}
public void closeBle() {
BleManager.getInstance().disconnectAllDevice();
BleManager.getInstance().destroy();
}
public void setScanRule() {
// String[] uuids;
// String str_uuid = et_uuid.getText().toString();
// if (TextUtils.isEmpty(str_uuid)) {
// uuids = null;
// } else {
// uuids = str_uuid.split(",");
// }
// UUID[] serviceUuids = null;
// if (uuids != null && uuids.length > 0) {
// serviceUuids = new UUID[uuids.length];
// for (int i = 0; i < uuids.length; i++) {
// String name = uuids[i];
// String[] components = name.split("-");
// if (components.length != 5) {
// serviceUuids[i] = null;
// } else {
// serviceUuids[i] = UUID.fromString(uuids[i]);
// }
// }
// }
//
// String[] names;
// String str_name = et_name.getText().toString();
// if (TextUtils.isEmpty(str_name)) {
// names = null;
// } else {
// names = str_name.split(",");
// }
//
// String mac = et_mac.getText().toString();
//
// boolean isAutoConnect = sw_auto.isChecked();
BleScanRuleConfig scanRuleConfig = new BleScanRuleConfig.Builder()
// .setServiceUuids(serviceUuids) // 只扫描指定的服务的设备,可选
.setDeviceName(true, "AIXQ") // 只扫描指定广播名的设备,可选
// .setDeviceMac(mac) // 只扫描指定mac的设备,可选
// .setAutoConnect(isAutoConnect) // 连接时的autoConnect参数,可选,默认false
// .setScanTimeOut(10000) // 扫描超时时间,可选,默认10秒
.build();
BleManager.getInstance().initScanRule(scanRuleConfig);
}
public void startScan(BleScanCallback scanCallback) {
BleManager.getInstance().scan(scanCallback);
}
/**
* 写数据
*
* @param bleDevice
* @param serviceUUID
* @param writeUUID
* @param data
*/
public void writeData(BleDevice bleDevice, String serviceUUID, String writeUUID, byte[] data) {
BleManager.getInstance().write(bleDevice, serviceUUID, writeUUID, data, writeCallback);
}
/**
* ble 写数据回调
*/
private BleWriteCallback writeCallback = new BleWriteCallback() {
@Override
public void onWriteSuccess(int current, int total, byte[] justWrite) {
Log.i("haha", "======onWriteSuccess==========>" + Arrays.toString(justWrite));
}
@Override
public void onWriteFailure(BleException exception) {
Log.i("haha", "======onWriteFailure==========>" + exception.getDescription());
}
};
/**
* ble 设置通知回调
*/
public BleNotifyCallback notifyCallback = new BleNotifyCallback() {
@Override
public void onNotifySuccess() {
Log.i("haha", "======onNotifySuccess==========>");
}
@Override
public void onNotifyFailure(BleException exception) {
Log.i("haha", "======onNotifyFailure==========>");
}
@Override
public void onCharacteristicChanged(byte[] data) {
String ble = ByteUtil.hexBytesToString(data).trim();
Log.i("haha", "======onCharacteristicChanged==========>" + ble);
receiveData(data);
}
};
/**
* 解析 ble data
*
* @param data
*/
private void receiveData(byte[] data) {
if (data.length >= 2 && data[0] == (byte) 0x5A && data[1] == (byte) 0xA5) {
mBytes.clear();
}
mBytes.addAll(Bytes.asList(data));
Log.i("haha", "======mBytes==========>" + mBytes.toString());
if (mBytes.size() >= 7 && mBytes.get(2) + mBytes.get(3) * 256 == mBytes.size() - 5 && mBytes.get(mBytes.size() - 1) == (byte) 0x0D) {
if (mBytes.get(4) == (byte) 0x45) {//接收特定的指令号
if (judgeByte(mBytes)) {
analysisBleData(mBytes);
mBytes.clear();
} else {
mBytes.clear();
}
}
} else if (mBytes.size() >= 4 && mBytes.get(2) + mBytes.get(3) * 256 > mBytes.size() - 5) {//没接收完
// "心跳没接收完".loge()
} else if (mBytes.size() < 4) {//没接收完
// "心跳没接收完".loge()
} else {
// "心跳接收:数据长度不匹配,${bytes2HexString(mBytes.toByteArray())}".loge()
mBytes.clear();
}
}
/**
* 判断是否符合校验
* 和校验、与校验
* @param data
* @return
*/
private boolean judgeByte(List<Byte> data) {
ReentrantLock lock = new ReentrantLock();
lock.lock();
byte xor = data.get(0);
byte and = data.get(0);
try {
for (int i = 1; i < data.size() - 3; i++) {
xor = (byte) (xor ^ data.get(i));
and = (byte) (and + data.get(i));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
if (data.get(data.size() - 3) == xor && data.get(data.size() - 2) == and) {
return true;
}
return false;
}
/**
* 包装要发送的数据
* @param num
* @param valueByteList
* @return
*/
private byte[] build(int num, List<Byte> valueByteList) {
int payloadLength = 0;
if (valueByteList != null && valueByteList.size() > 0) {
payloadLength = valueByteList.size();
}
int length = payloadLength + 2 + 2 + 2 + 1;
byte[] typeArray = new byte[length];
int indexOfTypeArray = 0;
typeArray[indexOfTypeArray++] = 0x5A;
typeArray[indexOfTypeArray++] = (byte) 0xA5;
int low = (length - 4) % 256;
int high = (length - 4) / 256;
typeArray[indexOfTypeArray++] = (byte) low;
typeArray[indexOfTypeArray++] = (byte) high;
typeArray[indexOfTypeArray++] = (byte) num;
if (valueByteList != null && valueByteList.size() > 0) {
for (int i = 0; i < valueByteList.size(); i++) {
typeArray[indexOfTypeArray++] = valueByteList.get(i);
}
}
byte xor = typeArray[0];
byte and = typeArray[0];
for (int i = 1; i < length - 2; i++) {
xor = (byte) (xor ^ typeArray[i]);
and = (byte) (and + typeArray[i]);
}
typeArray[indexOfTypeArray++] = xor;
typeArray[indexOfTypeArray++] = and;
return typeArray;
}
/**
* 测试ble 连接 (设备“嘀”声响)
*
* @param bleDevice
* @param serviceUUID
* @param writeUUID
*/
public void bleTestConnect(BleDevice bleDevice, String serviceUUID, String writeUUID) {
byte[] data = build(0x43, new ArrayList<>());
writeData(bleDevice, serviceUUID, writeUUID, data);
}
/**
* 给设备发心跳
* @param bleDevice
* @param serviceUUID
* @param writeUUID
*/
public void heartbeat(BleDevice bleDevice, String serviceUUID, String writeUUID) {
byte[] data = build(0x4C, new ArrayList<>());
writeData(bleDevice, serviceUUID, writeUUID, data);
}
/**
* 查询设备数据
*
* @param bleDevice
* @param serviceUUID
* @param writeUUID
*/
public void getBleData(BleDevice bleDevice, String serviceUUID, String writeUUID) {
byte[] data = build(0x45, new ArrayList<>());
writeData(bleDevice, serviceUUID, writeUUID, data);
}
/**
* 定时回传使能
*
* @param bleDevice
* @param serviceUUID
* @param writeUUID
*/
public void timeBack(BleDevice bleDevice, String serviceUUID, String writeUUID) {
ArrayList<Byte> array = new ArrayList<>();
array.add(0, (byte) 0X01);
//10ms
array.add(1, (byte) 10);
array.add(2, (byte) 0X00);
byte[] data = build(0x47, array);
writeData(bleDevice, serviceUUID, writeUUID, data);
}
/**
* 取消定时回传使能
*
* @param bleDevice
* @param serviceUUID
* @param writeUUID
*/
public void cancelTimeBack(BleDevice bleDevice, String serviceUUID, String writeUUID) {
ArrayList<Byte> array = new ArrayList<>();
array.add(0, (byte) 0X00);
array.add(1, (byte) 0X08);
array.add(2, (byte) 0X00);
byte[] data = build(0x47, array);
writeData(bleDevice, serviceUUID, writeUUID, data);
}
}```