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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 16:06:30