Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีตรวจสอบการเชื่อมต่ออุปกรณ์ Bluetooth ใน Android

ภาพรวม

บทความนี้สาธิตวิธีตรวจสอบสถานะการเชื่อมต่ออุปกรณ์ Bluetooth (Connected/Disconnected) ใน Android โดยใช้ BroadcastReceiver ฟัง BluetoothDevice.ACTION_ACL_CONNECTED และ ACTION_ACL_DISCONNECTED พร้อมแนวทาง Modern API สำหรับ Bluetooth LE และ Permission ใหม่

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

สร้างโปรเจกต์ Android ใหม่ใน Android Studio

ขั้นตอนที่ 2: Layout (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    android:gravity="center">

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="สถานะ Bluetooth: กำลังตรวจสอบ..."
        android:textSize="18sp"
        android:textStyle="bold"
        android:gravity="center" />

    <TextView
        android:id="@+id/tvDeviceInfo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="อุปกรณ์: -"
        android:textSize="14sp"
        android:gravity="center" />

    <Button
        android:id="@+id/btnOpenSettings"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="เปิด Bluetooth Settings" />

</LinearLayout>

ขั้นตอนที่ 3: MainActivity.java - รองรับ Modern Bluetooth API

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private TextView tvStatus, tvDeviceInfo;
    private Button btnOpenSettings;
    private BluetoothAdapter bluetoothAdapter;
    private BroadcastReceiver bluetoothReceiver;
    private BroadcastReceiver connectionReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tvStatus = findViewById(R.id.tvStatus);
        tvDeviceInfo = findViewById(R.id.tvDeviceInfo);
        btnOpenSettings = findViewById(R.id.btnOpenSettings);

        // Initialize Bluetooth Adapter
        BluetoothManager bluetoothManager = getSystemService(BluetoothManager.class);
        bluetoothAdapter = bluetoothManager.getAdapter();

        if (bluetoothAdapter == null) {
            tvStatus.setText("อุปกรณ์ไม่รองรับ Bluetooth");
            btnOpenSettings.setEnabled(false);
            return;
        }

        // ตรวจสอบสถานะปัจจุบัน
        updateBluetoothState();

        // Register Receivers
        registerReceivers();

        btnOpenSettings.setOnClickListener(v -> {
            startActivity(new Intent(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS));
        });
    }

    private void registerReceivers() {
        // 1. Bluetooth Adapter State (On/Off)
        bluetoothReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
                    int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                    updateBluetoothState();
                }
            }
        };
        registerReceiver(bluetoothReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));

        // 2. Device Connection State (Connected/Disconnected)
        connectionReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                
                if (device == null) return;

                String deviceName = device.getName() != null ? device.getName() : "Unknown Device";
                String deviceAddress = device.getAddress();

                if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
                    tvStatus.setText("🟢 Bluetooth: Connected");
                    tvDeviceInfo.setText("อุปกรณ์: " + deviceName + " (" + deviceAddress + ")");
                    Toast.makeText(context, "เชื่อมต่อกับ " + deviceName, Toast.LENGTH_SHORT).show();
                } else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
                    tvStatus.setText("🔴 Bluetooth: Disconnected");
                    tvDeviceInfo.setText("อุปกรณ์: - (Last: " + deviceName + ")");
                    Toast.makeText(context, "ตัดการเชื่อมต่อ: " + deviceName, Toast.LENGTH_SHORT).show();
                } else if (BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED.equals(action)) {
                    tvStatus.setText("🟡 Bluetooth: Disconnecting...");
                }
            }
        };

        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
        registerReceiver(connectionReceiver, filter);
    }

    private void updateBluetoothState() {
        if (bluetoothAdapter == null) return;

        int state = bluetoothAdapter.getState();
        switch (state) {
            case BluetoothAdapter.STATE_ON:
                tvStatus.setText("🟢 Bluetooth: เปิดอยู่");
                checkConnectedDevices();
                break;
            case BluetoothAdapter.STATE_OFF:
                tvStatus.setText("🔴 Bluetooth: ปิดอยู่");
                tvDeviceInfo.setText("อุปกรณ์: -");
                break;
            case BluetoothAdapter.STATE_TURNING_ON:
                tvStatus.setText("🟡 Bluetooth: กำลังเปิด...");
                break;
            case BluetoothAdapter.STATE_TURNING_OFF:
                tvStatus.setText("🟡 Bluetooth: กำลังปิด...");
                break;
            default:
                tvStatus.setText("❓ Bluetooth: Unknown State");
        }
    }

    private void checkConnectedDevices() {
        // ตรวจสอบอุปกรณ์ที่เชื่อมต่ออยู่แล้ว (Profile: A2DP, Headset, HID, etc.)
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
            tvDeviceInfo.setText("อุปกรณ์: ต้องการ Permission BLUETOOTH_CONNECT");
            return;
        }

        // Classic Bluetooth Profiles
        int[] profiles = {
            BluetoothProfile.A2DP,        // Audio Streaming
            BluetoothProfile.HEADSET,     // Headset/Handsfree
            BluetoothProfile.HEALTH,      // Health Device
            BluetoothProfile.HID_DEVICE,  // HID (Keyboard/Mouse)
            BluetoothProfile.GATT_SERVER  // BLE GATT Server
        };

        for (int profile : profiles) {
            if (bluetoothAdapter.getProfileConnectionState(profile) == BluetoothProfile.STATE_CONNECTED) {
                // ได้อุปกรณ์ที่เชื่อมต่อ แต่ต้องใช้ Profile Proxy เพื่อดูรายละเอียด
            }
        }

        // bonded (paired) devices
        // bluetoothAdapter.getBondedDevices() -> Set
    }

    // ==================== Modern Bluetooth LE (API 21+) ====================
    
    /**
     * สำหรับ Bluetooth Low Energy (BLE) ใช้ BluetoothLeScanner
     * ต้อง Permission: BLUETOOTH_SCAN, BLUETOOTH_CONNECT (Android 12+)
     */
    private void scanBleDevices() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12+
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
        }
        
        // BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();
        // scanner.startScan(callback);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (bluetoothReceiver != null) unregisterReceiver(bluetoothReceiver);
        if (connectionReceiver != null) unregisterReceiver(connectionReceiver);
    }
}

ขั้นตอนที่ 4: AndroidManifest.xml - Permissions ตาม Version

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.com.sample">

    <!-- Legacy Permissions (Android 11 และก่อน) -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

    <!-- Android 12+ (API 31+) - Runtime Permissions -->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

    <!-- Features -->
    <uses-feature android:name="android.hardware.bluetooth" android:required="true" />
    <uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

ตาราง Permission ตาม Android Version

Android VersionAPI LevelPermissions Requiredประเภท
≤ 10 (Q)≤ 29BLUETOOTH, BLUETOOTH_ADMINInstall-time
11 (R)30BLUETOOTH, BLUETOOTH_ADMIN + Location PermissionRuntime (Location)
12 (S)31BLUETOOTH_CONNECT, BLUETOOTH_SCAN, BLUETOOTH_ADVERTISERuntime
13+ (T+)33+BLUETOOTH_CONNECT, BLUETOOTH_SCAN, BLUETOOTH_ADVERTISE + NEARBY_DEVICESRuntime

Action Intents สำคัญ

ActionExtraคำอธิบาย
BluetoothAdapter.ACTION_STATE_CHANGEDEXTRA_STATE, EXTRA_PREVIOUS_STATEBluetooth เปิด/ปิด
BluetoothDevice.ACTION_ACL_CONNECTEDEXTRA_DEVICE, EXTRA_CLASSClassic BT เชื่อมต่อสำเร็จ
BluetoothDevice.ACTION_ACL_DISCONNECTEDEXTRA_DEVICEClassic BT ตัดการเชื่อมต่อ
BluetoothDevice.ACTION_BOND_STATE_CHANGEDEXTRA_DEVICE, EXTRA_BOND_STATE, EXTRA_PREVIOUS_BOND_STATEPair/Unpair
BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGEDEXTRA_CONNECTION_STATE, EXTRA_DEVICEProfile Connection (A2DP, Headset)

Best Practices

  • Runtime Permission: ขอ Permission ก่อน Scan/Connect (Android 12+)
  • Foreground Service: สำหรับ BLE Scanning ยาวนาน ต้องใช้ Foreground Service Type "connectedDevice"
  • Battery Optimization: ขอความยินยอม Ignore Battery Optimization สำหรับ Background BLE
  • Companion Device Manager: ใช้สำหรับ Pairing อุปกรณ์เฉพาะ (Smartwatch, Earbuds) ไม่ต้อง Location Permission
  • Unregister Receiver: ต้อง Unregister ใน onDestroy() กัน Memory Leak