首页 > 解决方案 > android BLE 服务器代码应该作为服务在后台运行

问题描述

我创建了 BLE 应用程序,其中包括客户端和服务器应用程序。代码运行成功。现在我想将服务器代码作为服务运行,以便每次都打开蓝牙,并且可以在需要时由客户端应用程序搜索。用于服务器的代码如下。请指导我将此代码用作服务。

public class ServerActivity extends AppCompatActivity {

private static final String TAG = "ServerActivity";

private ActivityServerBinding mBinding;

private Handler mHandler;
private Handler mLogHandler;
private List<BluetoothDevice> mDevices;
private Map<String, byte[]> mClientConfigurations;

private BluetoothGattServer mGattServer;
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;

// Lifecycle

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mHandler = new Handler();
    mLogHandler = new Handler(Looper.getMainLooper());
    mDevices = new ArrayList<>();
    mClientConfigurations = new HashMap<>();

    mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
    mBluetoothAdapter = mBluetoothManager.getAdapter();

    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_server);
    mBinding.restartServerButton.setOnClickListener(v -> restartServer());
}

@Override
protected void onResume() {
    super.onResume();

    // Check if bluetooth is enabled
    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
        // Request user to enable it
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivity(enableBtIntent);
        finish();
        return;
    }

    // Check low energy support
    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
        // Get a newer device
        log("No LE Support.");
        finish();
        return;
    }

    // Check advertising
    if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) {
        // Unable to run the server on this device, get a better device
        log("No Advertising Support.");
        finish();
        return;
    }

    mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
    GattServerCallback gattServerCallback = new GattServerCallback();
    mGattServer = mBluetoothManager.openGattServer(this, gattServerCallback);

    Bundle bundle = getIntent().getExtras();
    String plate_name = bundle.getString("PLATE_NAME");

    mBluetoothAdapter.setName(plate_name);
    Toast.makeText(getApplicationContext(),"PLATE NAME "+plate_name,Toast.LENGTH_LONG).show();

    @SuppressLint("HardwareIds")
    String deviceInfo = "Device Info"
            + "\nName: " + mBluetoothAdapter.getName()
            + "\nAddress: " + mBluetoothAdapter.getAddress();
    mBinding.serverDeviceInfoTextView.setText(deviceInfo);

    setupServer();
    startAdvertising();
}

@Override
protected void onPause() {
    super.onPause();
    stopAdvertising();
    stopServer();
}

// GattServer
private void setupServer() {
    BluetoothGattService service = new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);

    // Write characteristic
    BluetoothGattCharacteristic writeCharacteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_ECHO_UUID,
            BluetoothGattCharacteristic.PROPERTY_WRITE,
            // Somehow this is not necessary, the client can still enable notifications
        // | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
            BluetoothGattCharacteristic.PERMISSION_WRITE);

    // Characteristic with Descriptor
    BluetoothGattCharacteristic notifyCharacteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_TIME_UUID,
            // Somehow this is not necessary, the client can still enable notifications
        // BluetoothGattCharacteristic.PROPERTY_NOTIFY,
            0, 0);

    BluetoothGattDescriptor clientConfigurationDescriptor = new BluetoothGattDescriptor(CLIENT_CONFIGURATION_DESCRIPTOR_UUID,
            BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
    clientConfigurationDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);

    notifyCharacteristic.addDescriptor(clientConfigurationDescriptor);

    service.addCharacteristic(writeCharacteristic);
    service.addCharacteristic(notifyCharacteristic);

    mGattServer.addService(service);
}

private void stopServer() {
    if (mGattServer != null) {
        mGattServer.close();
    }
}

private void restartServer() {
    stopAdvertising();
    stopServer();
    setupServer();
    startAdvertising();
}

// Advertising

private void startAdvertising() {
    if (mBluetoothLeAdvertiser == null) {
        return;
    }

    AdvertiseSettings settings = new AdvertiseSettings.Builder()
            .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
            .setConnectable(true)
            .setTimeout(0)
            .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_LOW)
            .build();

    ParcelUuid parcelUuid = new ParcelUuid(SERVICE_UUID);
    AdvertiseData data = new AdvertiseData.Builder()
            .setIncludeDeviceName(true)
            .addServiceUuid(parcelUuid)
            .build();

    mBluetoothLeAdvertiser.startAdvertising(settings, data, mAdvertiseCallback);
}

private void stopAdvertising() {
    if (mBluetoothLeAdvertiser != null) {
        mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
    }
}

private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
    @Override
    public void onStartSuccess(AdvertiseSettings settingsInEffect) {
        log("Peripheral advertising started.");
    }

    @Override
    public void onStartFailure(int errorCode) {
        log("Peripheral advertising failed: " + errorCode);
    }
};

// Notifications

private void notifyCharacteristicTime(byte[] value) {
    notifyCharacteristic(value, CHARACTERISTIC_TIME_UUID);
}

private void notifyCharacteristic(byte[] value, UUID uuid) {
    mHandler.post(() -> {
        BluetoothGattService service = mGattServer.getService(SERVICE_UUID);
        BluetoothGattCharacteristic characteristic = service.getCharacteristic(uuid);
        log("Notifying characteristic " + characteristic.getUuid().toString()
                + ", new value: " + StringUtils.byteArrayInHexFormat(value));

        characteristic.setValue(value);
        // Indications require confirmation, notifications do not
        boolean confirm = BluetoothUtils.requiresConfirmation(characteristic);
        for (BluetoothDevice device : mDevices) {
            if (clientEnabledNotifications(device, characteristic)) {
                mGattServer.notifyCharacteristicChanged(device, characteristic, confirm);
            }
        }
    });
}

private boolean clientEnabledNotifications(BluetoothDevice device, BluetoothGattCharacteristic characteristic) {
    List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
    BluetoothGattDescriptor descriptor = BluetoothUtils.findClientConfigurationDescriptor(descriptorList);
    if (descriptor == null) {
        // There is no client configuration descriptor, treat as true
        return true;
    }
    String deviceAddress = device.getAddress();
    byte[] clientConfiguration = mClientConfigurations.get(deviceAddress);
    if (clientConfiguration == null) {
        // Descriptor has not been set
        return false;
    }

    byte[] notificationEnabled = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
    return clientConfiguration.length == notificationEnabled.length
            && (clientConfiguration[0] & notificationEnabled[0]) == notificationEnabled[0]
            && (clientConfiguration[1] & notificationEnabled[1]) == notificationEnabled[1];
}

// Gatt Server Actions

public void log(String msg) {
    Log.d(TAG, msg);
    mLogHandler.post(() -> {
        mBinding.viewServerLog.logTextView.append(msg + "\n");
        mBinding.viewServerLog.logScrollView.post(() -> mBinding.viewServerLog.logScrollView.fullScroll(View.FOCUS_DOWN));
        if (msg.contains("Open")) {
            ToneGenerator toneGen1 = new ToneGenerator(AudioManager.STREAM_MUSIC, 100);
            toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 150);
        } else if (msg.contains("Close")) {
            ToneGenerator toneGen1 = new ToneGenerator(AudioManager.STREAM_MUSIC, 100);
            toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 150);
            toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 300);
        }
    });
}

public void addDevice(BluetoothDevice device) {
    log("Device added: " + device.getAddress());
    mHandler.post(() -> mDevices.add(device));
}

public void removeDevice(BluetoothDevice device) {
    log("Device removed: " + device.getAddress());
    mHandler.post(() -> {
        mDevices.remove(device);
        String deviceAddress = device.getAddress();
        mClientConfigurations.remove(deviceAddress);
    });
}

public void addClientConfiguration(BluetoothDevice device, byte[] value) {
    String deviceAddress = device.getAddress();
    mClientConfigurations.put(deviceAddress, value);

}

public void sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value) {
    mGattServer.sendResponse(device, requestId, status, 0, null);
}

public void notifyCharacteristicEcho(byte[] value) {
    notifyCharacteristic(value, CHARACTERISTIC_ECHO_UUID);
}

// Gatt Callback

private class GattServerCallback extends BluetoothGattServerCallback {

    @Override
    public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
        super.onConnectionStateChange(device, status, newState);
        log("onConnectionStateChange " + device.getAddress()
                + "\nstatus " + status
                + "\nnewState " + newState);

        if (newState == BluetoothProfile.STATE_CONNECTED) {
            addDevice(device);
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            removeDevice(device);
        }
    }

    // The Gatt will reject Characteristic Read requests that do not have the permission set,
    // so there is no need to check inside the callback
    @Override
    public void onCharacteristicReadRequest(BluetoothDevice device,
                                            int requestId,
                                            int offset,
                                            BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicReadRequest(device, requestId, offset, characteristic);

        log("onCharacteristicReadRequest " + characteristic.getUuid().toString());

        if (BluetoothUtils.requiresResponse(characteristic)) {
            // Unknown read characteristic requiring response, send failure
            sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, 0, null);
        }
        // Not one of our characteristics or has NO_RESPONSE property set
    }

    // The Gatt will reject Characteristic Write requests that do not have the permission set,
    // so there is no need to check inside the callback
    @Override
    public void onCharacteristicWriteRequest(BluetoothDevice device,
                                             int requestId,
                                             BluetoothGattCharacteristic characteristic,
                                             boolean preparedWrite,
                                             boolean responseNeeded,
                                             int offset,
                                             byte[] value) {
        super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
        log("onCharacteristicWriteRequest" + characteristic.getUuid().toString()
                + "\nReceived: " + StringUtils.stringFromBytes(value));

        if (CHARACTERISTIC_ECHO_UUID.equals(characteristic.getUuid())) {
            sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);

            // Reverse message to differentiate original message & response
            byte[] response = ByteUtils.reverse(value);
            characteristic.setValue(response);
            log("Sending: " + StringUtils.byteArrayInHexFormat(response));
            notifyCharacteristicEcho(response);
        }
    }

    // The Gatt will reject Descriptor Read requests that do not have the permission set,
    // so there is no need to check inside the callback
    @Override
    public void onDescriptorReadRequest(BluetoothDevice device,
                                        int requestId,
                                        int offset,
                                        BluetoothGattDescriptor descriptor) {
        super.onDescriptorReadRequest(device, requestId, offset, descriptor);
        log("onDescriptorReadRequest" + descriptor.getUuid().toString());
    }

    // The Gatt will reject Descriptor Write requests that do not have the permission set,
    // so there is no need to check inside the callback
    @Override
    public void onDescriptorWriteRequest(BluetoothDevice device,
                                         int requestId,
                                         BluetoothGattDescriptor descriptor,
                                         boolean preparedWrite,
                                         boolean responseNeeded,
                                         int offset,
                                         byte[] value) {
        super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
        log("onDescriptorWriteRequest: " + descriptor.getUuid().toString()
                + "\nvalue: " + StringUtils.stringFromBytes(value));

        if (CLIENT_CONFIGURATION_DESCRIPTOR_UUID.equals(descriptor.getUuid())) {
            addClientConfiguration(device, value);
            sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
        }
    }

    @Override
    public void onNotificationSent(BluetoothDevice device, int status) {
        super.onNotificationSent(device, status);
        log("onNotificationSent");
    }
}
}

标签: androidbluetoothbluetooth-lowenergy

解决方案


使用具有持久通知的前台服务。您的服务将继续运行。


推荐阅读