Android 6中的不稳定BLE连接(Marshmallow)

时间:2016-07-18 09:30:52

标签: android bluetooth bluetooth-lowenergy rssi

我正在开发一款适用于蓝牙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();
                                }
                            }
                        }
                    });
                }
            };
        }
    }
}

它有各种各样的问题;

  • 几秒钟后才收到readRemoteRssi()的回调
  • 断开连接后不再连接
  • 在应用程序转到后台时不起作用
  • 蓝牙芯片在断开连接和再次尝试连接之间重置!!
  • ...

我知道在DeadObjectException期间BluetthGatt发生readRemoteRssi()interval返回false,但无法解决。

我知道Android 6在BLE上有一些变化,我搜索了很多并研究了文档,但没有结果!但我发现,当我将100设置为1000而不是interval时,它会更好,但不是绝对的(在Android 6.0.1上)。是的,我确信cellForItemAtIndexPath以及如何实现它(Runnable,AlarmManager,TimerTask,ScheduledExecutorService,...)对此有效,但是为什么?!这是我的生气。

有什么想法吗?谢谢,对不起我的英语。

更新:当我将申请置于某种压力之下时,我的日志是a link。对不起,它比stackoverflow容量大!!我清楚地看到蓝牙重启了......

3 个答案:

答案 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堆栈上做太多时,我们遇到了很多问题,比如崩溃的蓝牙堆栈,在重启设备之前无法连接到设备等等。