API 19下MainActivity实现Arduino BLE连接读取方案咨询
API 19 平台Google Glass BLE连接实现方案
核心问题答复
- 你需要的自动连接指定UUID设备、建立GATT连接、读取数据并展示到TextView的全部逻辑,完全可以直接写在MainActivity中,对于单页面的简单BLE应用不需要额外拆分独立服务类。
注意:你当前导入的
android.bluetooth.le.*包是API 21才新增的接口,在API 19(KitKat)上无法使用,必须使用BluetoothAdapter自带的startLeScan()/stopLeScan()方法实现扫描逻辑。 - 必须为BLE操作单独分配非UI线程,不能直接在主线程执行GATT相关操作:API 19的BLE GATT接口会阻塞调用线程,直接在主线程调用极易触发ANR,部分厂商固件还会直接抛出线程访问异常。你不需要手写复杂的线程管理,用
Handler绑定后台工作线程的Looper处理所有BLE操作即可。 - 不建议在
onCreate()中直接启动BLE扫描/连接逻辑,正确的生命周期管理方式是:- 在
onCreate()中做权限检查、蓝牙可用性检查、初始化BLE相关常量和回调 - 在
onStart()中启动扫描、发起连接 - 在
onStop()中停止扫描、断开GATT连接,避免后台耗电和内存泄漏
- 在
适配API 19的可直接复用基础代码框架
package com.example.flyin_glass; import android.app.Activity; 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.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.widget.TextView; import android.widget.Toast; import java.util.List; import java.util.UUID; public class MainActivity extends Activity { // 替换为你Arduino设备实际的Service、特征UUID private static final UUID TARGET_SERVICE_UUID = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"); private static final UUID TARGET_CHAR_UUID = UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"); private static final int REQUEST_ENABLE_BT = 1; private static final long SCAN_PERIOD = 10000; // 扫描超时10秒 private BluetoothAdapter mBluetoothAdapter; private BluetoothGatt mBluetoothGatt; private Handler mBleHandler; private Handler mUiHandler; private TextView mDataTv1, mDataTv2, mDataTv3; // 替换为你布局里的TextView控件 private boolean mScanning = false; // BLE扫描回调,仅匹配携带目标UUID的设备 private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { // 找到目标设备后停止扫描,发起连接 mBleHandler.post(() -> { if (mScanning) { mBluetoothAdapter.stopLeScan(mLeScanCallback); mScanning = false; connectToDevice(device); } }); } }; // GATT连接回调,所有回调运行在mBleHandler绑定的线程 private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothGatt.STATE_CONNECTED) { // 连接成功后启动服务发现 gatt.discoverServices(); } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { mUiHandler.post(() -> Toast.makeText(MainActivity.this, "设备已断开", Toast.LENGTH_SHORT).show()); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattService targetService = gatt.getService(TARGET_SERVICE_UUID); if (targetService != null) { BluetoothGattCharacteristic targetChar = targetService.getCharacteristic(TARGET_CHAR_UUID); // 开启特征通知,接收Arduino主动上报的数据 gatt.setCharacteristicNotification(targetChar, true); } } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { // 收到设备上报的数据,切到主线程更新UI final byte[] data = characteristic.getValue(); mUiHandler.post(() -> { // 这里根据你的Arduino数据协议解析内容,更新到对应TextView String receivedStr = new String(data); mDataTv1.setText(receivedStr); }); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化控件 mDataTv1 = findViewById(R.id.tv_data1); mDataTv2 = findViewById(R.id.tv_data2); mDataTv3 = findViewById(R.id.tv_data3); // 初始化线程Handler mUiHandler = new Handler(Looper.getMainLooper()); HandlerThread bleThread = new HandlerThread("BleWorkerThread"); bleThread.start(); mBleHandler = new Handler(bleThread.getLooper()); // 检查设备是否支持BLE if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, "设备不支持BLE", Toast.LENGTH_SHORT).show(); finish(); return; } // 初始化蓝牙适配器 final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } } @Override protected void onStart() { super.onStart(); if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled() && !mScanning) { startBleScan(); } } @Override protected void onStop() { super.onStop(); // 页面不可见时停止扫描、断开连接 if (mScanning) { mBluetoothAdapter.stopLeScan(mLeScanCallback); mScanning = false; } if (mBluetoothGatt != null) { mBluetoothGatt.close(); mBluetoothGatt = null; } } private void startBleScan() { mScanning = true; // 扫描超时自动停止 mBleHandler.postDelayed(() -> { if (mScanning) { mBluetoothAdapter.stopLeScan(mLeScanCallback); mScanning = false; mUiHandler.post(() -> Toast.makeText(MainActivity.this, "未找到目标设备", Toast.LENGTH_SHORT).show()); } }, SCAN_PERIOD); // 启动扫描,过滤目标UUID的设备 mBluetoothAdapter.startLeScan(new UUID[]{TARGET_SERVICE_UUID}, mLeScanCallback); } private void connectToDevice(BluetoothDevice device) { mBluetoothGatt = device.connectGatt(this, false, mGattCallback); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_OK) { startBleScan(); } } }
注意事项
- 记得在AndroidManifest.xml中声明以下权限,API 19不需要动态申请BLE相关权限:
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
- 所有GATT的读写、订阅操作都要post到
mBleHandler对应的线程执行,不要直接在主线程调用 - 收到BLE数据后必须切回主线程再更新TextView,否则会触发UI访问异常
- Google Glass定制固件对AppCompat兼容性较差,直接继承原生Activity比继承AppCompatActivity稳定性更高
内容的提问来源于stack exchange,提问作者Gaubert36
相关产品推荐
相关产品推荐

