无Android蓝牙开发经验,求BLE扫描-连接-读特征值可运行示例代码
Android BLE 扫描、连接与特征值读取完整示例
下面是一套可直接运行的Android BLE代码,涵盖扫描目标设备、建立连接、读取指定特征值的核心流程,适配Android 6.0及以上版本,无经验开发者可直接复用:
1. 配置权限(AndroidManifest.xml)
BLE操作需要蓝牙权限和定位权限(Android 6.0+扫描必须),先在清单文件中添加:
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
2. 核心功能实现(MainActivity.java)
完整Activity代码,包含所有逻辑:
import android.Manifest; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanResult; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import java.util.UUID; public class MainActivity extends AppCompatActivity { private static final String TAG = "BLEDemo"; private static final int REQUEST_PERMISSIONS = 1001; private static final int REQUEST_ENABLE_BT = 1002; private BluetoothAdapter mBluetoothAdapter; private BluetoothLeScanner mLeScanner; private BluetoothGatt mBluetoothGatt; private TextView mStatusText; private Button mScanBtn; // 替换为你的设备信息 private static final String TARGET_DEVICE_NAME = "你的BLE设备名称"; private static final UUID SERVICE_UUID = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"); private static final UUID CHARACTERISTIC_UUID = UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mStatusText = findViewById(R.id.status_text); mScanBtn = findViewById(R.id.scan_btn); BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); mLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); mScanBtn.setOnClickListener(v -> { if (checkPermissions()) { startScan(); } else { requestPermissions(); } }); } private boolean checkPermissions() { return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED; } private void requestPermissions() { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.BLUETOOTH_CONNECT}, REQUEST_PERMISSIONS); } private void startScan() { if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); return; } mStatusText.setText("扫描中..."); mScanBtn.setEnabled(false); mLeScanner.startScan(mScanCallback); // 5秒后自动停止扫描,避免耗电 new android.os.Handler().postDelayed(() -> { mLeScanner.stopScan(mScanCallback); mStatusText.setText("扫描结束"); mScanBtn.setEnabled(true); }, 5000); } private final ScanCallback mScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); BluetoothDevice device = result.getDevice(); Log.d(TAG, "发现设备: " + device.getName() + " | " + device.getAddress()); if (TARGET_DEVICE_NAME.equals(device.getName())) { mLeScanner.stopScan(mScanCallback); mStatusText.setText("连接目标设备中..."); if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { return; } mBluetoothGatt = device.connectGatt(MainActivity.this, false, mGattCallback); } } }; private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (newState == BluetoothGatt.STATE_CONNECTED) { runOnUiThread(() -> mStatusText.setText("连接成功,发现服务中...")); if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { return; } gatt.discoverServices(); } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { runOnUiThread(() -> mStatusText.setText("设备断开连接")); gatt.close(); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattService service = gatt.getService(SERVICE_UUID); if (service != null) { BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID); if (characteristic != null) { if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { return; } gatt.readCharacteristic(characteristic); } else { runOnUiThread(() -> mStatusText.setText("未找到目标特征值")); } } else { runOnUiThread(() -> mStatusText.setText("未找到目标服务")); } } else { runOnUiThread(() -> mStatusText.setText("发现服务失败,状态码: " + status)); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { // 按设备实际数据格式解析,这里假设是字符串 String value = new String(characteristic.getValue()); runOnUiThread(() -> mStatusText.setText("读取结果: " + value)); Log.d(TAG, "读取到特征值: " + value); } else { runOnUiThread(() -> mStatusText.setText("读取失败,状态码: " + status)); } } }; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_ENABLE_BT) { if (resultCode == RESULT_OK) { startScan(); } else { mStatusText.setText("蓝牙未开启,无法扫描"); } } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_PERMISSIONS) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { startScan(); } else { mStatusText.setText("缺少权限,无法操作BLE"); } } } @Override protected void onDestroy() { super.onDestroy(); if (mBluetoothGatt != null) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { return; } mBluetoothGatt.close(); mBluetoothGatt = null; } } }
3. 布局文件(activity_main.xml)
简单的UI布局,用于显示状态和触发扫描:
<?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" android:gravity="center"> <TextView android:id="@+id/status_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击按钮开始扫描" android:textSize="18sp" android:layout_marginBottom="24dp"/> <Button android:id="@+id/scan_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="开始扫描BLE设备"/> </LinearLayout>
关键提示
- 必须替换代码中的
TARGET_DEVICE_NAME、SERVICE_UUID、CHARACTERISTIC_UUID为你的BLE设备实际信息 - BLE回调运行在子线程,更新UI必须用
runOnUiThread - 断开连接或页面销毁时务必调用
BluetoothGatt.close()释放资源 - 不要长时间持续扫描,示例中设置5秒自动停止,减少耗电
内容的提问来源于stack exchange,提问作者Adam DOGLA
相关产品推荐
相关产品推荐

