You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 10:15:34