我来帮您实现一个完整的Android BLE(低功耗蓝牙)通讯示例。
1. 权限配置 (AndroidManifest.xml)
<xml>
<uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /><uses-permission android:name="android.permission.BLUETOOTH_SCAN" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
2. BLE管理类
<java>
import android.bluetooth.BluetoothAdapter;import android.bluetooth.BluetoothDevice;import android.bluetooth.BluetoothGatt;import android.bluetooth.BluetoothGattCallback;import android.bluetooth.BluetoothGattCharacteristic;import android.bluetooth.BluetoothGattDescriptor;import android.bluetooth.BluetoothGattService;import android.bluetooth.BluetoothManager;import android.bluetooth.BluetoothProfile;import android.bluetooth.le.BluetoothLeScanner;import android.bluetooth.le.ScanCallback;import android.bluetooth.le.ScanResult;import android.content.Context;import android.os.Handler;import android.util.Log;import java.util.ArrayList;import java.util.List;import java.util.UUID;public class BLEManager { private static final String TAG = "BLEManager"; // 通用的UUID public static final String SERVICE_UUID = "0000fff0-0000-1000-8000-00805f9b34fb"; public static final String CHARACTERISTIC_UUID_TX = "0000fff1-0000-1000-8000-00805f9b34fb"; public static final String CHARACTERISTIC_UUID_RX = "0000fff2-0000-1000-8000-00805f9b34fb"; public static final String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; private Context context; private BluetoothManager bluetoothManager; private BluetoothAdapter bluetoothAdapter; private BluetoothLeScanner bluetoothLeScanner; private BluetoothGatt bluetoothGatt; private boolean isScanning = false; private boolean isConnected = false; private Handler handler = new Handler(); private static final long SCAN_PERIOD = 10000; // 扫描10秒 private BLECallback bleCallback; // 回调接口 public interface BLECallback { void onDeviceFound(BluetoothDevice device); void onConnected(); void onDisconnected(); void onDataReceived(byte[] data); void onError(String error); } public BLEManager(Context context) { this.context = context; initialize(); } public void setBLECallback(BLECallback callback) { this.bleCallback = callback; } // 初始化蓝牙 private boolean initialize() { if (bluetoothManager == null) { bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); if (bluetoothManager == null) { Log.e(TAG, "Unable to initialize BluetoothManager."); return false; } } bluetoothAdapter = bluetoothManager.getAdapter(); if (bluetoothAdapter == null) { Log.e(TAG, "Unable to obtain a BluetoothAdapter."); return false; } bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); return true; } // 检查蓝牙是否开启 public boolean isBluetoothEnabled() { return bluetoothAdapter != null && bluetoothAdapter.isEnabled(); } // 开始扫描 public void startScan() { if (!isBluetoothEnabled()) { if (bleCallback != null) { bleCallback.onError("蓝牙未开启"); } return; } if (isScanning) { return; } handler.postDelayed(new Runnable() { @Override public void run() { stopScan(); } }, SCAN_PERIOD); isScanning = true; bluetoothLeScanner.startScan(scanCallback); Log.d(TAG, "开始扫描BLE设备"); } // 停止扫描 public void stopScan() { if (isScanning && bluetoothLeScanner != null) { isScanning = false; bluetoothLeScanner.stopScan(scanCallback); Log.d(TAG, "停止扫描BLE设备"); } } // 扫描回调 private ScanCallback scanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); BluetoothDevice device = result.getDevice(); if (bleCallback != null && device.getName() != null) { bleCallback.onDeviceFound(device); } } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); if (bleCallback != null) { bleCallback.onError("扫描失败: " + errorCode); } } }; // 连接设备 public void connect(String address) { if (bluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return; } final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); if (device == null) { Log.w(TAG, "Device not found. Unable to connect."); return; } // 如果已经连接,先断开 if (bluetoothGatt != null) { bluetoothGatt.close(); bluetoothGatt = null; } bluetoothGatt = device.connectGatt(context, false, gattCallback); Log.d(TAG, "Trying to create a new connection."); } // GATT回调 private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { isConnected = true; Log.i(TAG, "Connected to GATT server."); bluetoothGatt.discoverServices(); if (bleCallback != null) { handler.post(new Runnable() { @Override public void run() { bleCallback.onConnected(); } }); } } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { isConnected = false; Log.i(TAG, "Disconnected from GATT server."); if (bleCallback != null) { handler.post(new Runnable() { @Override public void run() { bleCallback.onDisconnected(); } }); } } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { Log.w(TAG, "onServicesDiscovered received: " + status); // 启用通知 enableNotification(); } else { Log.w(TAG, "onServicesDiscovered received: " + status); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { final byte[] data = characteristic.getValue(); if (bleCallback != null && data != null) { handler.post(new Runnable() { @Override public void run() { bleCallback.onDataReceived(data); } }); } } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { final byte[] data = characteristic.getValue(); if (bleCallback != null && data != null) { handler.post(new Runnable() { @Override public void run() { bleCallback.onDataReceived(data); } }); } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { Log.d(TAG, "Characteristic written successfully"); } else { Log.e(TAG, "Characteristic write failed"); } } }; // 启用通知 private void enableNotification() { if (bluetoothGatt == null) { return; } BluetoothGattService service = bluetoothGatt.getService(UUID.fromString(SERVICE_UUID)); if (service == null) { Log.e(TAG, "Service not found"); return; } BluetoothGattCharacteristic characteristic = service.getCharacteristic( UUID.fromString(CHARACTERISTIC_UUID_RX)); if (characteristic == null) { Log.e(TAG, "Characteristic not found"); return; } bluetoothGatt.setCharacteristicNotification(characteristic, true); BluetoothGattDescriptor descriptor = characteristic.getDescriptor( UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG)); if (descriptor != null) { descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); bluetoothGatt.writeDescriptor(descriptor); } } // 发送数据 public boolean sendData(byte[] data) { if (bluetoothGatt == null || !isConnected) { Log.e(TAG, "BluetoothGatt not initialized or not connected"); return false; } BluetoothGattService service = bluetoothGatt.getService(UUID.fromString(SERVICE_UUID)); if (service == null) { Log.e(TAG, "Service not found"); return false; } BluetoothGattCharacteristic characteristic = service.getCharacteristic( UUID.fromString(CHARACTERISTIC_UUID_TX)); if (characteristic == null) { Log.e(TAG, "Characteristic not found"); return false; } characteristic.setValue(data); return bluetoothGatt.writeCharacteristic(characteristic); } // 发送字符串 public boolean sendData(String data) { return sendData(data.getBytes()); } // 断开连接 public void disconnect() { if (bluetoothGatt == null) { return; } bluetoothGatt.disconnect(); } // 关闭资源 public void close() { if (bluetoothGatt == null) { return; } bluetoothGatt.close(); bluetoothGatt = null; } // 获取连接状态 public boolean isConnected() { return isConnected; }}
3. 使用示例 (MainActivity.java)
<java>
import android.Manifest;import android.bluetooth.BluetoothDevice;import android.content.pm.PackageManager;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.AdapterView;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget.EditText;import android.widget.ListView;import android.widget.TextView;import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import androidx.core.app.ActivityCompat;import androidx.core.content.ContextCompat;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity implements BLEManager.BLECallback { private static final String TAG = "MainActivity"; private static final int PERMISSION_REQUEST_CODE = 1; private BLEManager bleManager; private ArrayAdapter<String> deviceAdapter; private List<BluetoothDevice> deviceList = new ArrayList<>(); private Button btnScan, btnConnect, btnDisconnect, btnSend; private ListView listDevices; private EditText editMessage; private TextView textStatus, textReceived; private BluetoothDevice selectedDevice; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); checkPermissions(); bleManager = new BLEManager(this); bleManager.setBLECallback(this); deviceAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1); listDevices.setAdapter(deviceAdapter); setupListeners(); } private void initViews() { btnScan = findViewById(R.id.btn_scan); btnConnect = findViewById(R.id.btn_connect); btnDisconnect = findViewById(R.id.btn_disconnect); btnSend = findViewById(R.id.btn_send); listDevices = findViewById(R.id.list_devices); editMessage = findViewById(R.id.edit_message); textStatus = findViewById(R.id.text_status); textReceived = findViewById(R.id.text_received); } private void setupListeners() { btnScan.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startScan(); } }); btnConnect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { connectDevice(); } }); btnDisconnect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { bleManager.disconnect(); } }); btnSend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sendMessage(); } }); listDevices.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { selectedDevice = deviceList.get(position); Toast.makeText(MainActivity.this, "选择了: " + selectedDevice.getName(), Toast.LENGTH_SHORT).show(); } }); } private void checkPermissions() { String[] permissions = { Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION }; List<String> permissionsNeeded = new ArrayList<>(); for (String permission : permissions) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { permissionsNeeded.add(permission); } } if (!permissionsNeeded.isEmpty()) { ActivityCompat.requestPermissions(this, permissionsNeeded.toArray(new String[0]), PERMISSION_REQUEST_CODE); } } private void startScan() { if (!bleManager.isBluetoothEnabled()) { Toast.makeText(this, "请先开启蓝牙", Toast.LENGTH_SHORT).show(); return; } deviceList.clear(); deviceAdapter.clear(); bleManager.startScan(); textStatus.setText("正在扫描..."); } private void connectDevice() { if (selectedDevice == null) { Toast.makeText(this, "请先选择设备", Toast.LENGTH_SHORT).show(); return; } bleManager.stopScan(); bleManager.connect(selectedDevice.getAddress()); textStatus.setText("正在连接..."); } private void sendMessage() { String message = editMessage.getText().toString(); if (message.isEmpty()) { Toast.makeText(this, "请输入消息", Toast.LENGTH_SHORT).show(); return; } if (bleManager.sendData(message)) { editMessage.setText(""); Toast.makeText(this, "发送成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "发送失败", Toast.LENGTH_SHORT).show(); } } @Override public void onDeviceFound(BluetoothDevice device) { if (!deviceList.contains(device)) { deviceList.add(device); deviceAdapter.add(device.getName() + "\n" + device.getAddress()); } } @Override public void onConnected() { textStatus.setText("已连接"); btnConnect.setEnabled(false); btnDisconnect.setEnabled(true); btnSend.setEnabled(true); } @Override public void onDisconnected() { textStatus.setText("已断开"); btnConnect.setEnabled(true); btnDisconnect.setEnabled(false); btnSend.setEnabled(false); } @Override public void onDataReceived(byte[] data) { String message = new String(data); textReceived.setText("收到: " + message); Log.d(TAG, "Received: " + message); } @Override public void onError(String error) { Toast.makeText(this, "错误: " + error, Toast.LENGTH_SHORT).show(); Log.e(TAG, "Error: " + error); } @Override protected void onDestroy() { super.onDestroy(); bleManager.close(); }}
4. 布局文件 (activity_main.xml)
<xml>
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <TextView android:id="@+id/text_status" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="未连接" android:textSize="18sp" android:textStyle="bold" android:padding="8dp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/btn_scan" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="扫描"/> <Button android:id="@+id/btn_connect" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="连接"/> <Button android:id="@+id/btn_disconnect" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="断开" android:enabled="false"/> </LinearLayout> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="设备列表:" android:textStyle="bold" android:padding="8dp"/> <ListView android:id="@+id/list_devices" android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginBottom="16dp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/edit_message" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="输入消息"/> <Button android:id="@+id/btn_send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发送" android:enabled="false"/> </LinearLayout> <TextView android:id="@+id/text_received" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="接收数据:" android:padding="8dp" android:layout_marginTop="16dp"/> </LinearLayout>
使用说明
权限处理:应用会在启动时请求必要的权限扫描设备:点击"扫描"按钮开始搜索附近的BLE设备选择设备:从列表中点击选择要连接的设备连接设备:选择设备后点击"连接"按钮发送数据:连接成功后,在输入框输入消息并点击"发送"接收数据:接收到的数据会显示在界面底部
注意事项
需要根据实际的BLE设备修改UUIDAndroid 12及以上需要额外的蓝牙权限某些操作需要在UI线程执行记得在不使用时关闭连接以节省电量
这个实现提供了完整的BLE通讯功能,包括扫描、连接、数据收发等基本操作。您可以根据具体需求进行修改和扩展。