杰瑞科技汇

Java如何实现蓝牙通信?

Of course! Here is a comprehensive guide to using Bluetooth in Java, covering both the classic Bluetooth (RFCOMM) and the newer Bluetooth Low Energy (BLE) APIs.

Java如何实现蓝牙通信?-图1
(图片来源网络,侵删)

Key Concepts

First, let's understand the two main ways to use Bluetooth in Java:

  1. Classic Bluetooth (RFCOMM): This is the traditional, well-established Bluetooth. It's great for streaming audio (A2DP), file transfer (OBEX), and creating simple, point-to-point connections (like a serial port). It's typically used for devices like headsets, speakers, and older printers.
  2. Bluetooth Low Energy (BLE / Bluetooth Smart): This is a modern, low-power version of Bluetooth. It's designed for small data transmissions and is used by fitness trackers, smart home sensors, heart rate monitors, and beacons. It's more complex but much more power-efficient.

The available Java API depends on the platform you're targeting:

  • Java SE (Desktop - Windows, macOS, Linux): You can use the JSR-82 (Java API for Bluetooth) specification. This is part of the standard Java library (javax.bluetooth), but it's often considered legacy and can be tricky to set up.
  • Android: Google provides its own, much more powerful and modern android.bluetooth and android.bluetooth.le packages. This is the recommended approach for Android development.
  • Java ME (Embedded/Mobile): Also uses JSR-82, similar to Java SE.

Part 1: Classic Bluetooth (RFCOMM) using JSR-82 (Java SE / Android Legacy)

This example demonstrates how to create a simple server that waits for a client to connect over an RFCOMM channel.

Step 1: Check if Bluetooth is Available and Enabled

import javax.bluetooth.*;
// This check is crucial. The LocalDevice might not be available.
LocalDevice localDevice = LocalDevice.getLocalDevice();
if (localDevice == null) {
    System.out.println("Bluetooth is not available.");
    // Exit or handle error
} else {
    System.out.println("Bluetooth is available.");
    System.out.println("Address: " + localDevice.getBluetoothAddress());
    System.out.println("Name: " + localDevice.getFriendlyName());
}

Step 2: Create a Server Connection

The server needs to listen for incoming connections on a specific Service UUID. A UUID is a unique identifier for your service. You can generate your own or use a standard one.

Java如何实现蓝牙通信?-图2
(图片来源网络,侵删)
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class BluetoothServer implements Runnable {
    // A standard UUID for a serial port service. You can generate your own.
    private static final String SERVER_UUID = "0000110100001000800000805F9B34FB";
    @Override
    public void run() {
        // The URL for the server connection
        String connectionString = "btspp://localhost:" + SERVER_UUID + ";name=BluetoothServer";
        try (StreamConnectionNotifier streamConnNotifier = (StreamConnectionNotifier) Connector.open(connectionString)) {
            System.out.println("Server started. Waiting for a client to connect...");
            // Wait for a client to connect
            StreamConnection connection = streamConnNotifier.acceptAndOpen();
            System.out.println("Client connected!");
            // Get input and output streams
            InputStream inputStream = connection.openInputStream();
            OutputStream outputStream = connection.openOutputStream();
            // Read data from the client
            byte[] buffer = new byte[1024];
            int bytesRead = inputStream.read(buffer);
            String receivedMessage = new String(buffer, 0, bytesRead);
            System.out.println("Received: " + receivedMessage);
            // Send a response back to the client
            String responseMessage = "Hello from the server!";
            outputStream.write(responseMessage.getBytes());
            System.out.println("Sent: " + responseMessage);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        // Note: On Java SE, you need to enable Bluetooth visibility first.
        // On Android, you would use the BluetoothAdapter.
        new Thread(new BluetoothServer()).start();
    }
}

Step 3: Create a Client Connection

The client needs to know the server's Bluetooth address and the same Service UUID to connect.

import javax.bluetooth.*;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class BluetoothClient {
    private static final String SERVER_UUID = "0000110100001000800000805F9B34FB";
    public static void main(String[] args) {
        // Replace with the actual address of your server device
        String serverAddress = "00:11:22:33:44:55"; 
        String connectionString = "btspp://" + serverAddress + ":" + SERVER_UUID + ";authenticate=true;encrypt=false";
        try (StreamConnection connection = (StreamConnection) Connector.open(connectionString)) {
            System.out.println("Connected to server.");
            // Get input and output streams
            OutputStream outputStream = connection.openOutputStream();
            InputStream inputStream = connection.openInputStream();
            // Send data to the server
            String message = "Hello from the client!";
            outputStream.write(message.getBytes());
            System.out.println("Sent: " + message);
            // Read the server's response
            byte[] buffer = new byte[1024];
            int bytesRead = inputStream.read(buffer);
            String response = new String(buffer, 0, bytesRead);
            System.out.println("Received: " + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Part 2: Bluetooth Low Energy (BLE) on Android

Modern Android development for BLE uses the android.bluetooth.le package. The process is more involved and revolves around a central (your app) and peripherals (the BLE devices).

Key Android Components:

  • BluetoothAdapter: Represents the device's Bluetooth adapter.
  • BluetoothManager: A system service that provides access to the BluetoothAdapter.
  • BluetoothGatt: Represents a GATT (Generic Attribute Profile) connection to a BLE device.
  • BluetoothGattCallback: A callback for GATT client events (like connection state changes, data read, data received).
  • BluetoothGattService: A collection of data and behaviors (services) offered by a BLE device.
  • BluetoothGattCharacteristic: A single data point within a service (e.g., a temperature reading).
  • ScanCallback: Used for scanning for nearby BLE devices.

Example: Scanning for a Device and Connecting

import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.ParcelUuid;
import android.util.Log;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class BleScanner {
    private static final String TAG = "BleScanner";
    private BluetoothLeScanner scanner;
    private ScanCallback scanCallback;
    // The UUID of the service you are looking for
    private static final UUID TARGET_SERVICE_UUID = UUID.fromString("0000180d-0000-1000-8000-00805f9b34fb"); // Example: Heart Rate Service
    public void startScan(Context context) {
        BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        if (bluetoothManager == null) {
            Log.e(TAG, "Unable to get BluetoothManager.");
            return;
        }
        BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
        if (bluetoothAdapter == null) {
            Log.e(TAG, "Unable to get BluetoothAdapter.");
            return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                Log.e(TAG, "Location permission is not granted.");
                // You should request the permission here
                return;
            }
        }
        scanner = bluetoothAdapter.getBluetoothLeScanner();
        if (scanner == null) {
            Log.e(TAG, "BluetoothLeScanner is null. Is Bluetooth on?");
            return;
        }
        // Create a scan filter for the specific service
        ScanFilter filter = new Scan.Builder()
                .setServiceUuid(new ParcelUuid(TARGET_SERVICE_UUID))
                .build();
        // Scan settings
        ScanSettings settings = new ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // High power, short scans
                .build();
        scanCallback = new ScanCallback() {
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                super.onScanResult(callbackType, result);
                BluetoothDevice device = result.getDevice();
                Log.i(TAG, "Found device: " + device.getName() + " " + device.getAddress());
                // Stop scanning once you find a device
                stopScan();
                // Now you can connect to the device
                connectToDevice(device);
            }
            @Override
            public void onScanFailed(int errorCode) {
                Log.e(TAG, "Scan failed with error code: " + errorCode);
            }
        };
        Log.i(TAG, "Starting BLE scan for service: " + TARGET_SERVICE_UUID);
        scanner.startScan(List.of(filter), settings, scanCallback);
    }
    public void stopScan() {
        if (scanner != null && scanCallback != null) {
            scanner.stopScan(scanCallback);
            Log.i(TAG, "Scan stopped.");
        }
    }
    private void connectToDevice(BluetoothDevice device) {
        // This is where you would initiate the GATT connection
        // You need a BluetoothGattCallback to handle the connection process
        Log.i(TAG, "Connecting to " + device.getAddress());
        // device.connectGatt(context, false, gattCallback);
    }
}

Example: Handling a GATT Connection

You would create a class that extends BluetoothGattCallback to handle events after connecting.

import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.util.Log;
public class MyGattCallback extends BluetoothGattCallback {
    private static final String TAG = "MyGattCallback";
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        if (newState == BluetoothGatt.STATE_CONNECTED) {
            Log.i(TAG, "Connected to GATT server.");
            // Discover services after connecting
            gatt.discoverServices();
        } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
            Log.i(TAG, "Disconnected from GATT server.");
        }
    }
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG, "Services discovered.");
            // Find the characteristic you are interested in
            BluetoothGattCharacteristic characteristic = gatt.getService(TARGET_SERVICE_UUID)
                    .getCharacteristic(UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb")); // Heart Rate Measurement Char
            // Enable notifications for this characteristic
            gatt.setCharacteristicNotification(characteristic, true);
            // Write the client characteristic configuration descriptor (CCCD)
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            gatt.writeDescriptor(descriptor);
        } else {
            Log.w(TAG, "onServicesDiscovered received: " + status);
        }
    }
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);
        // This is where you receive the data that was being notified
        byte[] value = characteristic.getValue();
        Log.i(TAG, "Received characteristic update: " + bytesToHex(value));
    }
    // Helper method to convert bytes to hex string for logging
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString();
    }
}

Summary and Best Practices

Feature Classic Bluetooth (JSR-82) Bluetooth Low Energy (Android)
Use Case Audio, file transfer, simple serial-like connections IoT, sensors, beacons, health devices
Complexity Relatively simple for point-to-point More complex (client-server model with services/characteristics)
Power Usage Higher Very Low
Java API javax.bluetooth (JSR-82) android.bluetooth.le
Platform Java SE, Android Legacy Modern Android (API 18+)
Permissions Varies by OS BLUETOOTH, BLUETOOTH_ADMIN, ACCESS_FINE_LOCATION (for scanning)

Key Takeaways:

Java如何实现蓝牙通信?-图3
(图片来源网络,侵删)
  1. Choose the Right Technology: Use Classic Bluetooth for audio and legacy devices. Use BLE for new, battery-powered IoT devices.
  2. Permissions are Crucial: On mobile platforms, especially Android, you must request the necessary Bluetooth and location permissions from the user.
  3. Asynchronous Operations: Both JSR-82 and Android BLE APIs are asynchronous. Use callbacks or listeners to handle events like connection, data reception, and errors.
  4. UUIDs are Everything: Services and characteristics are identified by UUIDs. You must know the UUIDs of the services you want to interact with on the target device.
  5. Error Handling: Always check for nulls, handle IOExceptions, and listen for callback failure codes to make your application robust.
分享:
扫描分享到社交APP
上一篇
下一篇