[desc]:修复BLE蓝牙BUG

[author]:Alex Wang
master
yimiao 3 years ago
parent a6a7b80b73
commit 9c88f40291

@ -38,6 +38,8 @@ dependencies {
implementation 'com.aill:AndroidSerialPort:1.0.8'
implementation 'com.airbnb.android:lottie:5.0.2'
implementation(rootProject.ext.dependencies.mmkv)
// kotlin

@ -38,7 +38,7 @@ interface IBluetoothClient {
* @param mac 需要连接蓝牙设备的地址
* @return 成功返回连接设备的地址
*/
fun connect(mac: String): Observable<String>
fun connect(mac: String, callback: BaseResultCallback<String>)
/**
* 断开蓝牙连接, 释放蓝牙连接占用的蓝牙服务

@ -114,6 +114,21 @@ public class BLEClientService extends Service {
}
};
/**
*
*/
private final BaseResultCallback<String> connectCallback = new BaseResultCallback<String>() {
@Override
public void onSuccess(String data) {
Log.d(TAG, "got connectCallback onSuccess = " + data);
}
@Override
public void onFail(String msg) {
Log.d(TAG, "got connectCallback onFail = " + msg);
}
};
/**
*
*
@ -127,49 +142,75 @@ public class BLEClientService extends Service {
if (TextUtils.isEmpty(connectMac)) {
connectMac = BtUtils.INSTANCE.getConnectMac(BLEClientService.this);
}
Observable<String>[] observables = new Observable[2];
observables[0] = mBtClient.connect(connectMac);
// 判断是否需要启动通知
if (enableNotify) {
observables[1] = mBtClient.registerNotify(connectMac, BtConstants.INSTANCE.getUUID_SERVICE(),
BtConstants.INSTANCE.getUUID_CHARACTERISTIC_READ_NOTIFY(), notifyCallback);
}
Observable.concatArray(observables).subscribe(new Observer<String>() {
mBtClient.connect(connectMac, new BaseResultCallback<String>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.d(TAG, "connect onSubscribe");
}
public void onSuccess(String data) {
Log.d(TAG, "got connectCallback onSuccess = " + data);
if (enableNotify) {
mBtClient.registerNotify(connectMac, BtConstants.INSTANCE.getUUID_SERVICE(),
BtConstants.INSTANCE.getUUID_CHARACTERISTIC_READ_NOTIFY(), notifyCallback).subscribe(new Observer<String>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.d(TAG, "connect onSubscribe");
}
@Override
public void onNext(@NonNull String value) {
Log.d(TAG, String.format("connect onNext: %s", value));
@Override
public void onNext(@NonNull String value) {
Log.d(TAG, String.format("connect onNext: %s", value));
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "connect onError: ", e);
// 链接过程报错,断开链接
mBtClient.disconnect(connectMac);
connectMac = "";
if (clientListener != null) {
clientListener.onResult(new CommonMsg(BtConstants.CONNECT_ERROR, e.getMessage()));
}
if (connectListener != null) {
connectListener.onFail(e.getMessage());
}
}
@Override
public void onComplete() {
Log.d(TAG, "connect onComplete");
// 将链接成功的MAC地址保存
BtUtils.INSTANCE.saveConnectMac(BLEClientService.this, mac);
if (clientListener != null) {
clientListener.onResult(new CommonMsg(CONNECT_SUCCESS, mac));
}
if (connectListener != null) {
connectListener.onSuccess();
}
}
});
} else {
// 将链接成功的MAC地址保存
BtUtils.INSTANCE.saveConnectMac(BLEClientService.this, mac);
if (clientListener != null) {
clientListener.onResult(new CommonMsg(CONNECT_SUCCESS, mac));
}
if (connectListener != null) {
connectListener.onSuccess();
}
}
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "connect onError: ", e);
public void onFail(String msg) {
Log.d(TAG, "got connectCallback onFail = " + msg);
// 链接过程报错,断开链接
mBtClient.disconnect(connectMac);
connectMac = "";
if (clientListener != null) {
clientListener.onResult(new CommonMsg(BtConstants.CONNECT_ERROR, e.getMessage()));
}
if (connectListener != null) {
connectListener.onFail(e.getMessage());
}
}
@Override
public void onComplete() {
Log.d(TAG, "connect onComplete");
// 将链接成功的MAC地址保存
BtUtils.INSTANCE.saveConnectMac(BLEClientService.this, mac);
if (clientListener != null) {
clientListener.onResult(new CommonMsg(CONNECT_SUCCESS, mac));
clientListener.onResult(new CommonMsg(BtConstants.CONNECT_ERROR, msg));
}
if (connectListener != null) {
connectListener.onSuccess();
connectListener.onFail(msg);
}
}
});

@ -46,7 +46,6 @@ public class BluetoothClientBLEAdapter implements IBluetoothClient {
public BluetoothClientBLEAdapter(BluetoothBLeClient client) {
mClient = client;
HandlerThread workThread = new HandlerThread("bluetooth ble worker");
workThread.start();
}
@ -101,34 +100,27 @@ public class BluetoothClientBLEAdapter implements IBluetoothClient {
mClient.getBluetoothSearcher(BtConstants.BLUETOOTH_TYPE.BLE).stopScan();
}
@NonNull
@Override
public Observable<String> connect(@NonNull final String mac) {
return Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(@NonNull final ObservableEmitter<String> emitter) {
BluetoothLeConnector connector = mClient.getBluetoothLeConnector(mac);
public void connect(@NonNull final String mac, BaseResultCallback<String> callback) {
BluetoothLeConnector connector = mClient.getBluetoothLeConnector(mac);
connector.connect(new BluetoothLeConnector.OnConnectListener() {
@Override
public void onConnect() {
}
connector.connect(new BluetoothLeConnector.OnConnectListener() {
@Override
public void onConnect() {
}
@Override
public void onDisconnect() {
}
@Override
public void onDisconnect() {
}
@Override
public void onServiceDiscover() {
emitter.onNext(mac);
emitter.onComplete();
}
@Override
public void onServiceDiscover() {
callback.onSuccess(mac);
}
@Override
public void onError(String msg) {
emitter.onError(new Throwable(msg));
}
});
@Override
public void onError(String msg) {
callback.onFail(msg);
}
});
}

@ -164,7 +164,8 @@ public class BluetoothLeConnector {
mConnectHandler.postDelayed(() ->
mWorkHandler.post(() -> {
if (!mIsStartService.get()) {
gatt.disconnect();
disconnectGatt();
mOnConnectListener.onError("disCoverServices time out");
}
}), 3000L);
@ -309,6 +310,11 @@ public class BluetoothLeConnector {
mIsStartService.set(false);
mConnectTime.set(SystemClock.elapsedRealtime());
// 再次链接前检查是否存在上一次的链接,如果存在,尝试断开连接
if (getBluetoothGatt() != null) {
getBluetoothGatt().disconnect();
getBluetoothGatt().close();
}
setBluetoothGatt(device.connectGatt(mContext, false, mGattCallback));
if (getBluetoothGatt() == null) {
String err = "bluetooth is not open!";
@ -320,7 +326,7 @@ public class BluetoothLeConnector {
// 自定义MTU
// getBluetoothGatt().requestMtu(512);
// 开一个定时器,如果超出 20s 就强制断开连接
// 开一个定时器,如果超出 10s 就强制断开连接
// 这个定时器必须在连接上设备之后清掉
mConnectHandler.removeCallbacksAndMessages(null);
mConnectHandler.postDelayed(() ->
@ -331,7 +337,7 @@ public class BluetoothLeConnector {
Log.e(TAG, err);
callback.onError(err);
}
}), 20000L);
}), 10000L);
});
}
@ -353,16 +359,12 @@ public class BluetoothLeConnector {
return;
}
if (mConnectStatus.get() == BluetoothGatt.STATE_DISCONNECTED) {
close();
return;
if (mConnectStatus.get() == BluetoothGatt.STATE_CONNECTING) {
mConnectHandler.removeCallbacksAndMessages(null);
}
getBluetoothGatt().disconnect();
// 确保 Gatt 一定会被 close
if (mConnectStatus.get() == BluetoothGatt.STATE_CONNECTING) {
mConnectHandler.removeCallbacksAndMessages(null);
if (mConnectStatus.get() != BluetoothGatt.STATE_DISCONNECTING) {
close();
}
}
@ -378,6 +380,7 @@ public class BluetoothLeConnector {
Log.e(TAG, "BluetoothAdapter not initialized");
return;
}
getBluetoothGatt().disconnect();
getBluetoothGatt().close();
setBluetoothGatt(null);
mConnectStatus.set(BluetoothGatt.STATE_DISCONNECTED);

@ -134,7 +134,7 @@ class BtBleSearcher(
if (mScanning.get()) {
mScanning.set(false)
launch(Dispatchers.Main) {
mScanCallback!!.onComplete()
mScanCallback?.onComplete()
}
mBluetoothAdapter?.bluetoothLeScanner?.stopScan(mScanCallback)
mScanCallback = null

@ -28,14 +28,11 @@ class BluetoothClientClassicAdapter(private var mClient: BluetoothClassicClient)
mClient.getBluetoothSearcher(BtConstants.BLUETOOTH_TYPE.CLASSIC).stopScan()
}
override fun connect(mac: String): Observable<String> {
return Observable.create {
if (!mClient.isConnected((mac))) {
mClient.connect(mac)
}
it.onNext(mac)
it.onComplete()
override fun connect(mac: String, callback: BaseResultCallback<String>) {
if (!mClient.isConnected((mac))) {
mClient.connect(mac)
}
callback.onSuccess(mac)
}
override fun disconnect(mac: String) {

@ -9,7 +9,6 @@ import com.common.bluetooth.DeviceJson
import com.common.bluetooth.bean.*
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.util.ArrayList
/**
* 蓝牙工具类
@ -36,10 +35,15 @@ object BtUtils {
const val MAC_KEY = "mac"
/**
* MAC地址的KEY
* MAC名称的KEY
*/
const val MAC_AND_NAME_KEY = "mac_and_name"
/**
* MAC名字的KEY
*/
const val NAME_KEY = "name"
enum class EVENT_TYPE(val type: String, val index: String) {
/**
* 点击事件
@ -374,7 +378,7 @@ object BtUtils {
* @param mac 设备MAC
* @param context 上下文
*/
fun saveConnectMacAndName(context: Context, mac: String,name:String) {
fun saveConnectMacAndName(context: Context, mac: String, name: String) {
val sharedPreferences: SharedPreferences =
context.getSharedPreferences(SP_DEVICE_NAME, Context.MODE_PRIVATE)
val edit = sharedPreferences.edit()
@ -382,14 +386,19 @@ object BtUtils {
var deviceNameList: ArrayList<String> = ArrayList()
var deviceMacList1: ArrayList<String> = ArrayList()
var deviceNameList1: ArrayList<String> = ArrayList()
var seatNameList: ArrayList<String> = ArrayList()
var seatNameList1: ArrayList<String> = ArrayList()
val gson = Gson()
deviceMacList.clear()
deviceNameList.clear()
seatNameList.clear()
if (TextUtils.isEmpty(getConnectMacAndName(context, MAC_AND_NAME_KEY))) {
deviceMacList.add(mac)
deviceNameList.add(name)
seatNameList.add(name)
edit.putString(MAC_AND_NAME_KEY, gson.toJson(deviceNameList))
edit.putString(MAC_KEY, gson.toJson(deviceMacList))
edit.putString(NAME_KEY, gson.toJson(seatNameList))
} else {
val listType = object : TypeToken<List<String?>?>() {}.type
deviceMacList1 = gson.fromJson<ArrayList<String>>(
@ -401,17 +410,29 @@ object BtUtils {
getConnectMacAndName(context, MAC_AND_NAME_KEY),
listType2
)
val listType3 = object : TypeToken<List<String?>?>() {}.type
seatNameList1 = gson.fromJson<ArrayList<String>>(
getConnectMacAndName(context, NAME_KEY),
listType3
)
var i = 0
for (s in deviceMacList1) {
if (deviceMacList1.get(i) == mac) {
break
} else if (deviceMacList1.get(deviceMacList1.size - 1) != mac ) {
} else if (deviceMacList1.get(deviceMacList1.size - 1) != mac) {
deviceMacList.clear()
deviceMacList.add(mac)
deviceMacList.addAll(deviceMacList1)
deviceNameList.clear()
deviceNameList.add(name)
deviceNameList.addAll(deviceNameList1)
seatNameList.clear()
seatNameList.add(name)
seatNameList.addAll(seatNameList1)
edit.putString(MAC_AND_NAME_KEY, gson.toJson(deviceNameList))
edit.putString(MAC_KEY, gson.toJson(deviceMacList))
edit.putString(NAME_KEY, gson.toJson(seatNameList))
}
i++
}
@ -433,6 +454,8 @@ object BtUtils {
return sharedPreferences.getString(MAC_AND_NAME_KEY, "")
} else if (string == MAC_KEY) {
return sharedPreferences.getString(MAC_KEY, "")
} else if (string == NAME_KEY) {
return sharedPreferences.getString(NAME_KEY, "")
}
return ""
}
@ -456,21 +479,29 @@ object BtUtils {
* @param mac 设备MAC
* @param context 上下文
*/
fun changeConnectMacAndName(context: Context, device: MutableList<DeviceJson>) {
fun changeConnectMacAndName(
context: Context,
device: MutableList<DeviceJson>,
list: MutableList<String>
) {
val sharedPreferences: SharedPreferences =
context.getSharedPreferences(SP_DEVICE_NAME, Context.MODE_PRIVATE)
val edit = sharedPreferences.edit()
var deviceMacList: ArrayList<String> = ArrayList()
var deviceNameList: ArrayList<String> = ArrayList()
var seatNameList: ArrayList<String> = ArrayList()
val gson = Gson()
deviceMacList.clear()
deviceNameList.clear()
seatNameList.clear()
var i = 0
for (s in device) {
deviceMacList.add(device.get(i).mac)
deviceNameList.add(device.get(i).name)
seatNameList.add(list.get(i))
edit.putString(MAC_AND_NAME_KEY, gson.toJson(deviceNameList))
edit.putString(MAC_KEY, gson.toJson(deviceMacList))
edit.putString(NAME_KEY, gson.toJson(seatNameList))
i++
}

@ -0,0 +1,29 @@
package com.common.bluetooth.utils
import android.content.Context
import android.view.LayoutInflater
import android.widget.TextView
import android.widget.Toast
import com.common.bluetooth.R
/**
* toast工具类
*/
class ToastUtil {
companion object {
/**
* 展示自定义背景和信息的toast
*/
fun showCustomToast(context: Context, msg: String) {
val view = LayoutInflater.from(context).inflate(R.layout.my_toast_layout, null, false)
val textView = view.findViewById<TextView>(R.id.tv_toast_msg)
textView.text = msg
var toast: Toast = Toast(context)
toast.duration = Toast.LENGTH_SHORT
toast.view = view
toast.show()
}
}
}

@ -33,13 +33,13 @@ public class BtDeviceListAdapter extends RecyclerView.Adapter<BtDeviceListAdapte
@NonNull
@Override
public BtViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
public BtDeviceListAdapter.BtViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mBinding = RvBtDeviceItemBinding.inflate(LayoutInflater.from(context));
return new BtViewHolder(mBinding);
}
@Override
public void onBindViewHolder(@NonNull BtViewHolder holder, int position) {
public void onBindViewHolder(@NonNull BtDeviceListAdapter.BtViewHolder holder, int position) {
final int currentIndex = position;
BluetoothDevice device = bluetoothDevices.get(position);
holder.binding.btDeviceNameTv.setText("Device Name:" + device.getName());

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/color_E6565D65" />
<corners android:radius="4dp" />
<padding
android:bottom="15dp"
android:left="52dp"
android:right="52dp"
android:top="15dp" />
</shape>

@ -0,0 +1,19 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_toast_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/custom_toast_layout"
android:textColor="@color/white"
android:textSize="18dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -18,4 +18,5 @@
<color name="composing_color">#ff000000</color>
<color name="composing_color_hl">#ffffffff</color>
<color name="composing_color_idle">#ff777777</color>
<color name="color_E6565D65">#E6565D65</color>
</resources>

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Progress.Dialog" parent="@android:style/Theme.Holo.Light.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
@ -7,4 +8,12 @@
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">true</item>
</style>
<style name="PregressCircle" parent="@android:style/Widget.ProgressBar.Large">
<item name="android:maxWidth">72dp</item>
<item name="minWidth">72dp</item>
<item name="android:maxHeight">72dp</item>
<item name="minHeight">72dp</item>
<item name="android:indeterminateDuration">800</item>
</style>
</resources>
Loading…
Cancel
Save