如何在后台继续运行蓝牙活动?

时间:2016-04-04 23:29:51

标签: java android android-intent service bluetooth

我在Android应用程序中有一个主要活动,可以自动连接到蓝牙Arduino设备,并与应用程序通信。我希望应用程序能够检测到何时失去蓝牙连接,然后提醒用户。当它完全在前台线程中运行时,这完全正常,但我希望能够关闭应用程序,并且如果连接丢失,用户仍然会收到通知。但是,我不确定实现这一目标的最佳方法。

// Main BTLE device callback where much of the logic occurs.
public BluetoothGattCallback callback = new BluetoothGattCallback() {
    // Called whenever the device connection state changes, i.e. from disconnected to connected.
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        if (newState == BluetoothGatt.STATE_CONNECTED) {
            writeLine("Connected!");
            // Discover services.
            if (!gatt.discoverServices()) {
                writeLine("Failed to start discovering services!");
            }
        } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
            writeLine("Disconnected!");
            // This is where the user is notified!
            pushNotification();

        } else {
            writeLine("Connection state changed.  New state: " + newState);
        }
    }

    // Called when services have been discovered on the remote device.
    // It seems to be necessary to wait for this discovery to occur before
    // manipulating any services or characteristics.
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        if (status == BluetoothGatt.GATT_SUCCESS) {
            writeLine("Service discovery completed!");
        } else {
            writeLine("Service discovery failed with status: " + status);
        }
        // Save reference to each characteristic.
        tx = gatt.getService(UART_UUID).getCharacteristic(TX_UUID);
        rx = gatt.getService(UART_UUID).getCharacteristic(RX_UUID);
        // Setup notifications on RX characteristic changes (i.e. data received).
        // First call setCharacteristicNotification to enable notification.
        if (!gatt.setCharacteristicNotification(rx, true)) {
            writeLine("Couldn't set notifications for RX characteristic!");
        }
        // Next update the RX characteristic's client descriptor to enable notifications.
        if (rx.getDescriptor(CLIENT_UUID) != null) {
            BluetoothGattDescriptor desc = rx.getDescriptor(CLIENT_UUID);
            desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            if (!gatt.writeDescriptor(desc)) {
                writeLine("Couldn't write RX client descriptor value!");
            }
        } else {
            writeLine("Couldn't get RX client descriptor!");
        }
    }

    // Called when a remote characteristic changes (like the RX characteristic).
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);
        // THIS LINE WRITES TO THE UI FROM THE ARDUINO
        writeLine(characteristic.getStringValue(0));
    }
};

// BTLE device scanning callback.
private LeScanCallback scanCallback = new LeScanCallback() {
    // Called when a device is found.
    @Override
    public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
        writeLine("Found device: " + bluetoothDevice.getAddress());
        // Check if the device has the UART service.
        if (parseUUIDs(bytes).contains(UART_UUID)) {
            // Found a device, stop the scan.
            adapter.stopLeScan(scanCallback);
            writeLine("Found UART service!");
            // Connect to the device.
            // Control flow will now go to the callback functions when BTLE events occur.
            gatt = bluetoothDevice.connectGatt(getApplicationContext(), false, callback);
        }
    }
};

// OnCreate, called once to initialize the activity.
@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Checks if Bluetooth is enabled
    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter == null) {
        bluetoothPopup("Your device does not support Bluetooth!");
    } else {
        if (!mBluetoothAdapter.isEnabled()) {
            Intent turnOn = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(turnOn, 0);;
        }
    }

    // Grab references to UI elements.
    messages = (TextView) findViewById(R.id.messages);

    //adapter = BluetoothAdapter.getDefaultAdapter();
    adapter = mBluetoothAdapter;
}

// OnResume, called right before UI is displayed.  Start the BTLE connection.
@Override
protected void onResume() {
    super.onResume();
    // Scan for all BTLE devices.
    // The first one with the UART service will be chosen--see the code in the scanCallback.
    writeLine("Scanning for devices...");
    adapter.startLeScan(scanCallback);
}

// OnStop, called right before the activity loses foreground focus.  Close the BTLE connection.
@Override
protected void onStop() {
    super.onStop();
    if (gatt != null) {
        // For better reliability be careful to disconnect and close the connection.
        gatt.disconnect();
        gatt.close();
        gatt = null;
        tx = null;
        rx = null;
    }
}

// Write some text to the messages text view.
// Care is taken to do this on the main UI thread so writeLine can be called
// from any thread (like the BTLE callback).
private void writeLine(final CharSequence text) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            messages.append(text);
            messages.append("\n");
        }
    });
}

// This is a workaround function from the SO thread to manually parse advertisement data.
private List<UUID> parseUUIDs(final byte[] advertisedData) {
    List<UUID> uuids = new ArrayList<UUID>();

    int offset = 0;
    while (offset < (advertisedData.length - 2)) {
        int len = advertisedData[offset++];
        if (len == 0)
            break;

        int type = advertisedData[offset++];
        switch (type) {
            case 0x02: // Partial list of 16-bit UUIDs
            case 0x03: // Complete list of 16-bit UUIDs
                while (len > 1) {
                    int uuid16 = advertisedData[offset++];
                    uuid16 += (advertisedData[offset++] << 8);
                    len -= 2;
                    uuids.add(UUID.fromString(String.format("%08x-0000-1000-8000-00805f9b34fb", uuid16)));
                }
                break;
            case 0x06:// Partial list of 128-bit UUIDs
            case 0x07:// Complete list of 128-bit UUIDs
                // Loop through the advertised 128-bit UUID's.
                while (len >= 16) {
                    try {
                        // Wrap the advertised bits and order them.
                        ByteBuffer buffer = ByteBuffer.wrap(advertisedData, offset++, 16).order(ByteOrder.LITTLE_ENDIAN);
                        long mostSignificantBit = buffer.getLong();
                        long leastSignificantBit = buffer.getLong();
                        uuids.add(new UUID(leastSignificantBit,
                                mostSignificantBit));
                    } catch (IndexOutOfBoundsException e) {
                        // Defensive programming.
                        //Log.e(LOG_TAG, e.toString());
                        continue;
                    } finally {
                        // Move the offset to read the next uuid.
                        offset += 15;
                        len -= 16;
                    }
                }
                break;
            default:
                offset += (len - 1);
                break;
        }
    }
    return uuids;
}

// Method called to start the service
public void startService(View view) {
    startService(new Intent(getBaseContext(), AppService.class));
}

// Method called to stop the service
public void stopService(View view) {
    stopService(new Intent(getBaseContext(), AppService.class));}

在服务开始之前已经建立了连接,但是如何确保在服务启动时仍然保持连接?我只需要将BluetoothGattCallback回调对象传递给服务,那么最好的方法是什么?

- 更新 - 我认为最好的方法是通过一个服务,但我怎么能传递一个复杂的参数,如&#34;回调&#34;宾语?我如何使用Parcelable接口执行此操作?

2 个答案:

答案 0 :(得分:1)

您需要创建Service 根据您的需要,显示您的应用程序作为通知运行可能是一个好主意 - 这样它不会被自动杀死,但您始终知道它正在运行。如果这是您想要的,您需要ForegroundService

完整教程: http://www.vogella.com/tutorials/AndroidServices/article.html

答案 1 :(得分:0)

Activities与其可见部分绑定。如果它们不可见,它们就会被摧毁。对于后台任务,您需要一个无头组件。您通常使用Service