如何在 Android 中正确传递扫描到的蓝牙设备并建立连接

千宇同学_2087

千宇同学_2087

2026-08-17

617人浏览

原创

如何在 Android 中正确传递扫描到的蓝牙设备并建立连接

本文详解如何避免因重复创建 recyclerview adapter 导致的空指针异常,通过事件回调机制将扫描到的蓝牙设备安全、高效地传递至其他组件,并完成后续连接逻辑。重点涵盖适配器设计优化、线程安全更新、设备去重与点击事件安全分发。

本文详解如何避免因重复创建 recyclerview adapter 导致的空指针异常,通过事件回调机制将扫描到的蓝牙设备安全、高效地传递至其他组件,并完成后续连接逻辑。重点涵盖适配器设计优化、线程安全更新、设备去重与点击事件安全分发。

在 Android 蓝牙开发中,一个常见却易被忽视的问题是:BroadcastReceiver 中频繁重建 RecyclerView.Adapter 并调用 setAdapter(),不仅造成性能浪费,更会导致 BluetoothDevice 对象在跨组件传递时为 null——这正是你遇到 NullPointerException 的根本原因。问题不在于设备本身不可用,而在于数据流设计不合理:ScanActivity 每次发现新设备都新建 Bluetooth_adapter,使旧实例持有的 data 列表与 UI 状态脱节,且未提供安全的设备选择出口。

✅ 正确的数据流设计:单例 Adapter + 回调驱动

核心原则是 “Adapter 只初始化一次,数据由其内部统一管理,选择行为通过接口回调通知宿主”。我们需重构两部分:

1. ScanActivity:精简生命周期,专注事件响应

  • 移除全局 ArrayList<bluetoothdevicemodel> data</bluetoothdevicemodel> 和重复 new Bluetooth_adapter(...)
  • onCreate()一次性创建适配器并设置给 RecyclerView
  • 实现 DeviceSelectListener 接口,在 onDeviceSelected() 中获取选中设备,并启动后续流程(如跳转连接页)。
// ScanActivity.java(关键片段)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_scan);

    RecyclerView recyclerViewSD = findViewById(R.id.Scan_devices_RV);
    recyclerViewSD.setLayoutManager(new LinearLayoutManager(this));

    // ✅ 仅在此处创建一次适配器
    DeviceSelectListener listener = new DeviceSelectListener() {
        @Override
        public void onDeviceSelected(BluetoothDeviceModel deviceModel) {
            // ✅ 安全获取设备信息(非 null!)
            String address = deviceModel.getAddress();
            String name = deviceModel.getName();

            // ? 启动连接 Activity 或执行连接逻辑
            Intent intent = new Intent(ScanActivity.this, ConnectActivity.class);
            intent.putExtra("device_address", address);
            intent.putExtra("device_name", name);
            startActivity(intent);
        }
    };

    BluetoothScanListAdapter adapter = new BluetoothScanListAdapter(listener);
    recyclerViewSD.setAdapter(adapter); // ✅ 仅设一次

    // 其他初始化...
}

2. BluetoothScanListAdapter:职责单一,线程安全更新

  • 将设备列表 devices 私有化并由 Adapter 自行维护;
  • 提供 addDevice() 方法,内部校验地址去重 + notifyItemInserted()
  • 关键:onBindViewHolder 中使用 holder.getAbsoluteAdapterPosition() 替代参数 position,避免因列表异步更新导致位置错乱;
  • 添加健壮性检查:索引越界、deviceModel == null
// BluetoothScanListAdapter.java
public class BluetoothScanListAdapter extends RecyclerView.Adapter<bluetoothviewholder> {

    public interface DeviceSelectListener {
        void onDeviceSelected(BluetoothDeviceModel deviceModel);
    }

    private final List<bluetoothdevicemodel> devices;
    private final DeviceSelectListener selectListener;

    public BluetoothScanListAdapter(DeviceSelectListener selectListener) {
        this.devices = new ArrayList();
        this.selectListener = selectListener;
    }

    @Override
    public void onBindViewHolder(@NonNull BluetoothViewHolder holder, int position) {
        BluetoothDeviceModel device = devices.get(position);
        holder.deviceNamePD.setText(device.getName() != null ? device.getName() : device.getAddress());
        holder.dropdownImgPD.setImageResource(device.getImageresouce());

        holder.cardViewPD.setOnClickListener(v -> {
            int index = holder.getAbsoluteAdapterPosition(); // ✅ 安全获取实时位置
            if (index = devices.size()) return;

            BluetoothDeviceModel selected = devices.get(index);
            if (selected == null) return; // 防御性检查

            if (selectListener != null) {
                selectListener.onDeviceSelected(selected); // ✅ 安全回调
            }
        });
    }

    @Override
    public int getItemCount() {
        return devices.size();
    }

    // ✅ 线程安全添加设备(需在主线程调用)
    public void addDevice(BluetoothDeviceModel device) {
        // 地址去重:避免重复显示同一设备
        for (BluetoothDeviceModel d : devices) {
            if (d.getAddress().equals(device.getAddress())) {
                return;
            }
        }
        devices.add(device);
        notifyItemInserted(devices.size() - 1);
    }
}</bluetoothdevicemodel></bluetoothviewholder>

3. 在 BroadcastReceiver 中安全更新 UI

发现设备后,必须在主线程操作 UI(如调用 addDevice())。原代码中直接在 onReceive() 里操作 RecyclerView 是危险的:

Android
Android

Android 构建系统与部署模式

下载
// ScanActivity.java 中 BroadcastReceiver 片段
private final BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) {
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            if (device == null) return;

            String name = device.getName();
            String address = device.getAddress();
            int logoRes = getLogoResource(device.getBluetoothClass()); // 封装逻辑

            // ✅ 使用 Handler.post 切换到主线程更新 Adapter
            handler.post(() -> {
                // 假设 scanListAdapter 已初始化
                scanListAdapter.addDevice(new BluetoothDeviceModel(name, address, logoRes));
            });
        }
    }
};

⚠️ 关键注意事项

  • 权限适配:Android 12+(API 31+)需动态申请 BLUETOOTH_SCANBLUETOOTH_CONNECT 等权限,且 targetSdkVersion >= 31registerReceiver() 必须指定 Context.RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED
  • 设备去重:蓝牙扫描可能多次返回同一设备,addDevice() 中的地址校验可避免列表重复。
  • 空值防御BluetoothDevice.getName() 可能返回 null,UI 展示时务必判空。
  • 资源释放onPause()unregisterReceiver()cancelDiscovery() 不可省略,防止内存泄漏与后台扫描耗电。

✅ 总结

解决 NullPointerException 的本质,是摒弃“临时对象传递”的思维,转向状态托管 + 事件驱动架构:
? ScanActivity 不持有设备数据,只响应选择事件;
? BluetoothScanListAdapter 独立管理设备列表,提供安全的增删查接口;
? 所有 UI 更新严格限定在主线程,通过 Handler.post()runOnUiThread() 保证线程安全。

如此设计,既消除了空指针隐患,又为后续连接、配对、数据传输等复杂逻辑提供了清晰、可扩展的入口。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

android

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
android开发三大框架
android开发三大框架

android开发三大框架是XUtil框架、volley框架、ImageLoader框架。本专题为大家提供android开发三大框架相关的各种文章、以及下载和课程。

2023.08.14

862

4

android是什么系统
android是什么系统

Android是一种功能强大、灵活可定制、应用丰富、多任务处理能力强、兼容性好、网络连接能力强的操作系统。本专题为大家提供android相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.22

2447

5

android权限限制怎么解开
android权限限制怎么解开

android权限限制可以使用Root权限、第三方权限管理应用程序、ADB命令和Xposed框架解开。详细介绍:1、Root权限,通过获取Root权限,用户可以解锁所有权限,并对系统进行自定义和修改;2、第三方权限管理应用程序,用户可以轻松地控制和管理应用程序的权限;3、ADB命令,用户可以在设备上执行各种操作,包括解锁权限;4、Xposed框架,用户可以在不修改系统文件的情况下修改应用程序的行为和权限。

2023.09.19

4437

7

android重启应用的方法有哪些
android重启应用的方法有哪些

android重启应用有通过Intent、PendingIntent、系统服务、Runtime等方法。本专题为大家提供Android相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.18

545

5

Android语音播放功能实现方法
Android语音播放功能实现方法

实现方法有使用MediaPlayer实现、使用SoundPool实现两种。可以根据具体的需求选择适合的方法进行实现。想了解更多语音播放的相关内容,可以阅读本专题下面的文章。

2024.03.01

904

5

Aionclaw智能助手介绍
Aionclaw智能助手介绍

本专题汇总了AionClaw(AI龙虾助手)的功能介绍与在线使用入口。AionClaw是杭州趣猿人工智能有限公司推出的桌面级AI智能体,能直接在电脑上读写文件、运行脚本、操作浏览器,自动交付Word、PPT、Excel等成品。

2026.09.20

0

13

AionClaw AI智能体与电脑自动化任务执行功能使用教程
AionClaw AI智能体与电脑自动化任务执行功能使用教程

AionClaw专题整理AI智能体与电脑自动化相关功能使用教程,涵盖安装部署、AI任务执行、Skills技能、文件处理、浏览器控制、电脑操作、持久记忆、聊天工具连接以及办公、编程和内容创作等功能,帮助用户快速掌握AionClaw的实际使用方法。

2026.09.20

0

15

AI视频生成软件推荐
AI视频生成软件推荐

本专题汇总了当前主流的AI视频生成软件推荐与排行榜单,涵盖seko、AniShort、剧云、Lovart、LiblibAI及立刻mv等热门工具。同时整理了各软件在文生视频、图生视频、时长限制、画质表现及免费额度等方面的差异对比,助您快速选对适合创作需求的AI视频生成工具。

2026.09.16

160

9

ai生成视频的工具免费版合集
ai生成视频的工具免费版合集

本专题汇总了当前免费AI生成视频工具的排行榜与推荐清单,涵盖seko、讯飞智作、AniShort及剧云、Lovart等多模型集成平台。同时整理了各工具的免费额度、输出时长、水印政策及适用场景差异,助您快速选择合适工具开启AI视频创作。

2026.09.16

60

10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Excel 教程
Excel 教程

共162课时 | 42.9万人学习

Java 教程
Java 教程

共578课时 | 185.5万人学习

Uniapp从零开始实现新闻资讯应用
Uniapp从零开始实现新闻资讯应用

共64课时 | 9.2万人学习