Xây dựng ứng dụng Bluetooth Low Energy hiệu quả trên Android với RxAndroidBle

Lập trình Bluetooth Low Energy (BLE) trên nền tảng Android thường được coi là một thách thức lớn đối với các nhà phát triển. Những vấn đề như quản lý trạng thái phức tạp, hiện tượng "callback hell", xử lý đa luồng và các lỗi không xác định từ hệ thống khiến việc duy trì mã nguồn trở nên khó khăn. Thư viện RxAndroidBle ra đời để giải quyết những bài toán này bằng cách áp dụng mô hình lập trình phản ứng (Reactive Programming). RxAndroidBle bao bọc các API BLE gốc của Android vào các Observable của RxJava, giúp chuyển đổi các thao tác Bluetooth phức tạp thành những luồng dữ liệu (streams) mạch lạc và dễ quản lý.

Ưu điểm của việc sử dụng RxAndroidBle

So với framework BLE tiêu chuẩn của Android, RxAndroidBle mang lại nhiều lợi ích vượt trội:
  • API đồng nhất: Thay thế các hàm gọi ngược (callbacks) rời rạc bằng các toán tử RxJava mạnh mẽ.
  • Quản lý luồng tự động: Tự động điều phối việc thực thi trên các luồng phù hợp, tránh gây treo giao diện (UI thread).
  • Xử lý lỗi tập trung: Cung cấp cơ chế bắt lỗi và thử lại (retry) đồng nhất cho mọi thao tác.
  • Hỗ trợ đa phiên bản: Tương thích tốt từ Android API 18 cho đến các phiên bản mới nhất.

Cài đặt và cấu hình

Để bắt đầu, bạn cần thêm thư viện vào tệp build.gradle. Tùy thuộc vào phiên bản RxJava bạn đang sử dụng:

// Đối với RxJava 3
implementation "com.polidea.rxandroidble3:rxandroidble:1.18.1"

// Đối với RxJava 2
implementation "com.polidea.rxandroidble2:rxandroidble:1.18.1"
Tiếp theo, khai báo các quyền cần thiết trong AndroidManifest.xml. Lưu ý từ Android 12 (API 31), các quyền Bluetooth đã có sự thay đổi lớn:

<!-- Quyền cơ bản -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

<!-- Cần thiết để quét thiết bị trên Android 6.0 - 11 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- Quyền mới cho Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

Quét tìm thiết bị BLE

Việc tìm kiếm thiết bị trở nên đơn giản hơn với các cài đặt quét có thể tùy chỉnh:

// Khởi tạo client
RxBleClient bleManager = RxBleClient.create(context);

// Thực hiện quét
Disposable scannerDisposable = bleManager.scanBleDevices(
        new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .build()
    )
    .subscribe(
        result -> {
            String name = result.getBleDevice().getName();
            String address = result.getBleDevice().getMacAddress();
            // Cập nhật danh sách thiết bị tìm thấy
        },
        error -> {
            // Xử lý lỗi khi quét
        }
    );

// Dừng quét khi không cần thiết
scannerDisposable.dispose();

Thiết lập kết nối

RxAndroidBle hỗ trợ cả kết nối trực tiếp và kết nối tự động (auto-connect):

RxBleDevice targetDevice = bleManager.getBleDevice("00:11:22:33:44:55");

targetDevice.establishConnection(false) // false = kết nối trực tiếp
    .subscribe(
        connection -> {
            // Kết nối thành công, có thể thực hiện đọc/ghi GATT
        },
        throwable -> {
            // Xử lý lỗi kết nối (ví dụ: thiết bị ngoài phạm vi)
        }
    );

Thao tác với dữ liệu: Đọc, Ghi và Thông báo

1. Đọc dữ liệu từ Characteristic


targetDevice.establishConnection(false)
    .flatMapSingle(conn -> conn.readCharacteristic(uuidCharacteristic))
    .subscribe(
        bytes -> {
            // Xử lý mảng byte nhận được
        },
        err -> { /* Xử lý lỗi */ }
    );

2. Ghi dữ liệu


byte[] payload = new byte[]{0x01, 0x02};
targetDevice.establishConnection(false)
    .flatMapSingle(conn -> conn.writeCharacteristic(uuidCharacteristic, payload))
    .subscribe(
        result -> {
            // Xác nhận ghi thành công
        },
        err -> { /* Xử lý lỗi */ }
    );

3. Đăng ký nhận thông báo (Notifications)


targetDevice.establishConnection(false)
    .flatMap(conn -> conn.setupNotification(uuidCharacteristic))
    .flatMap(notificationObservable -> notificationObservable) // Chuyển đổi sang luồng dữ liệu thực tế
    .subscribe(
        value -> {
            // Nhận dữ liệu cập nhật từ thiết bị theo thời gian thực
        },
        err -> { /* Xử lý lỗi */ }
    );

Tính năng nâng cao

Điều chỉnh MTU (Maximum Transmission Unit)

Để truyền tải các gói dữ liệu lớn hơn mặc định (23 bytes), bạn cần yêu cầu thay đổi MTU:

targetDevice.establishConnection(false)
    .flatMapSingle(conn -> conn.requestMtu(256))
    .subscribe(
        mtu -> {
            // MTU đã được nâng cấp lên 256
        },
        err -> { /* Xử lý lỗi */ }
    );

Ghi dữ liệu khối lượng lớn (Long Write)

Khi cần gửi một tệp tin hoặc gói tin vượt quá giới hạn MTU, RxAndroidBle cung cấp giải pháp ghi theo lô (batch):

targetDevice.establishConnection(false)
    .flatMap(conn -> conn.createNewLongWriteBuilder()
        .setCharacteristicUuid(uuidCharacteristic)
        .setBytes(largeByteArray)
        .setMaxBatchSize(20)
        .build()
    )
    .subscribe(
        finalResult -> {
            // Hoàn tất việc ghi dữ liệu lớn
        },
        err -> { /* Xử lý lỗi */ }
    );

Theo dõi trạng thái hệ thống

Bạn có thể quan sát sự thay đổi trạng thái của Bluetooth (Bật/Tắt) hoặc quyền truy cập vị trí để điều chỉnh hành vi ứng dụng:

bleManager.observeStateChanges()
    .subscribe(state -> {
        if (state == RxBleClient.State.READY) {
            // Sẵn sàng để quét và kết nối
        } else if (state == RxBleClient.State.BLUETOOTH_NOT_ENABLED) {
            // Yêu cầu người dùng bật Bluetooth
        }
    });

Kinh nghiệm triển khai thực tế

  • Quản lý Instance: Luôn sử dụng RxBleClient như một Singleton trong toàn bộ ứng dụng để tránh rò rỉ bộ nhớ và xung đột tài nguyên.
  • Giải phóng tài nguyên: Đảm bảo gọi dispose() trên các Subscription khi Activity hoặc ViewModel bị hủy để đóng kết nối và dừng quét.
  • Xử lý lỗi 133: Đây là lỗi GATT phổ biến trên Android. Hãy thử triển khai toán tử retry() với khoảng thời gian chờ (delay) để tăng tỉ lệ kết nối thành công.
  • Cấu hình Log: Trong quá trình phát triển, hãy bật log mức độ DEBUG để dễ dàng theo dõi các gói tin trao đổi: RxBleClient.setLogLevel(RxBleLog.DEBUG);

Thẻ: android-ble RxJava rxandroidble bluetooth-low-energy mobile-development

Đăng vào ngày 19 tháng 9 lúc 16:13