【问题标题】:How to continue running Bluetooth activity in background?如何继续在后台运行蓝牙活动?
【发布时间】:2016-04-04 23:29:50
【问题描述】:

我在一个 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 回调对象传递给服务,最好的方法是什么?

--更新- 我认为最好的方法是通过服务,但是如何将复杂的参数传递给它,例如“回调”对象?我将如何使用 Parcelable 接口执行此操作?

【问题讨论】:

  • 使用服务并将活动绑定到它

标签: java android android-intent service bluetooth


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2017-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-25
      • 2018-07-20
      相关资源
      最近更新 更多