我正在开发一款适用于蓝牙LE的Android应用。它适用于Android 4.3或5.0.1以及各种设备,但在Android M(6.0.1)中它不稳定。我写了一个具有相同行为和问题的精简示例项目,下面是我的所有代码;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MainActivity extends ActionBarActivity {
private BluetoothAdapter mBluetoothAdapter;
private int REQUEST_ENABLE_BT = 1;
private Handler mHandler;
private static final long SCAN_PERIOD = 2000;
private BluetoothLeScanner mLEScanner;
private ScanSettings settings;
private List<ScanFilter> filters;
private BluetoothGatt mGatt;
private boolean connectionState = false, isScanning = false;
private TextView rssiTxt;
private RSSITimer rssiTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler();
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported",
Toast.LENGTH_SHORT).show();
finish();
}
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// VIEW
rssiTxt = (TextView) findViewById(R.id.rssiTxt);
(rssiTimer = new RSSITimer()).startTimer();
}
@Override
protected void onResume() {
super.onResume();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
filters = new ArrayList<ScanFilter>();
}
scanLeDevice(true);
}
}
@Override
protected void onPause() {
super.onPause();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
//scanLeDevice(false);
}
}
@Override
protected void onDestroy() {
rssiTimer.stopTimerTask();
if (mGatt == null) {
return;
}
mGatt.close();
mGatt = null;
super.onDestroy();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == Activity.RESULT_CANCELED) {
//Bluetooth not enabled.
finish();
return;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void scanLeDevice(final boolean enable) {
if(isScanning) {
return;
} else {
isScanning = true;
}
if (enable) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
isScanning = false;
} else {
mLEScanner.stopScan(mScanCallback);
isScanning = false;
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(filters, settings, mScanCallback);
}
} else {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
BluetoothDevice btDevice = result.getDevice();
if ("PRODi".equals(device.getName()) || "PRODi(TEST)".equals(device.getName())) {
connectToDevice(btDevice);
}
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
@Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i("onLeScan", device.toString());
if ("PRODi".equals(device.getName()) || "PRODi(TEST)".equals(device.getName())) {
connectToDevice(device);
}
}
});
}
};
public void connectToDevice(BluetoothDevice device) {
if (mGatt == null) {
mGatt = device.connectGatt(getApplicationContext(), false, gattCallback);
scanLeDevice(false);// will stop after first device detection
return;
} else {
mGatt.connect();
scanLeDevice(false);// will stop after first device detection
}
}
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("gattCallback", "STATE_CONNECTED");
gatt.discoverServices();
connectionState = true;
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "STATE_CONNECTED", Toast.LENGTH_SHORT).show();
}
});
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e("gattCallback", "STATE_DISCONNECTED");
connectionState = false;
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "STATE_DISCONNECTED", Toast.LENGTH_SHORT).show();
}
});
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
Log.i("onServicesDiscovered", services.toString());
gatt.readCharacteristic(services.get(1).getCharacteristics().get(0));
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic, int status) {
Log.i("onCharacteristicRead", characteristic.toString());
gatt.disconnect();
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
final int rssi2 = rssi;
runOnUiThread(new Runnable() {
@Override
public void run() {
rssiTxt.setText("RSSI: " + rssi2);
}
});
}
};
/*TIMER for Read RSSI*/////////////////////////////////////////////////////////////////////////////////////////
class RSSITimer {
//we are going to use a handler to be able to run in our TimerTask
final Handler handler = new Handler();
private Timer timer;
private TimerTask timerTask;
private int interval = 1000;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, after the first 1000ms the TimerTask will run every [INTERVAL]ms
timer.schedule(timerTask, 1000, interval); //
}
public void stopTimerTask() {
//stop the timer, if it's not already null
if (timer != null)
{
timer.cancel();
timer = null;
}
}
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
handler.post(new Runnable() {
public void run() {
if (mBluetoothAdapter.isEnabled()) {
if (connectionState == false) {
scanLeDevice(true);
} else {
mGatt.readRemoteRssi();
}
}
}
});
}
};
}
}
}
它有各种各样的问题;
我知道在DeadObjectException
期间BluetthGatt
发生readRemoteRssi()
,interval
返回false,但无法解决。
我知道Android 6在BLE上有一些变化,我搜索了很多并研究了文档,但没有结果!但我发现,当我将100
设置为1000
而不是interval
时,它会更好,但不是绝对的(在Android 6.0.1上)。是的,我确信cellForItemAtIndexPath
以及如何实现它(Runnable,AlarmManager,TimerTask,ScheduledExecutorService,...)对此有效,但是为什么?!这是我的生气。
有什么想法吗?谢谢,对不起我的英语。
更新:当我将申请置于某种压力之下时,我的日志是a link。对不起,它比stackoverflow容量大!!我清楚地看到蓝牙重启了......
答案 0 :(得分:1)
启用&#34; Bluetooth HCI snoop log&#34;并使用Wireshark来分析数据包。 这样您就可以看到蓝牙堆栈事件。 Bluetooth HCI snoop log Menu
答案 1 :(得分:0)
断开连接后再次连接
执行device.connectGatt(getApplicationContext(),true,gattCallback);而不是device.connectGatt(getApplicationContext(),false,gattCallback);自动重新连接。
当应用程序转到后台时不起作用
您需要在应用程序进程中安装前台服务或其他东西,以防止您的进程被终止。这是因为BLE apis基于回调而不是意图。
蓝牙芯片在断开连接和再次尝试连接之间复位!! 几秒后没有收到readRemoteRssi()的回调
绝对是Android中的一个错误。提交错误报告。你可以发布FULL logcat输出(不仅是从你的应用程序过滤掉的输出)吗?
几秒后不接收readRemoteRssi()的回调
不知道这个......
答案 2 :(得分:0)
看起来您正在同时扫描和连接/读/写。在Android上,这会导致稳定性问题,蓝牙堆栈可能会崩溃。尝试在蓝牙堆栈上更轻松,不要同时扫描和连接,只连接到一个设备并等待回调之前再做下一件事,你甚至应该在你的ble任务之间添加一些中断。 在同时在ble堆栈上做太多时,我们遇到了很多问题,比如崩溃的蓝牙堆栈,在重启设备之前无法连接到设备等等。