Android(Java)开发之获取BLE广播包(扫描后获取:广播数据+扫描应答数据+RSSI)

2023-11-01

一、安卓BLE的广播包数据从哪获取?

通常,安卓APP读写BLE设备的数据都是建立连接后通过GATT获取或修改。但是,BLE设备向外广播时本身会携带一部分有用信息,如将传感数据存放到广播包的自定义数据段,最近接触的一个iBeacon/EddyStone整合项目便是类似,因此为了提取广播包进行解析,首要问题就是安卓APP如何获取广播数据。

      其实,安卓蓝牙在扫描设备后,回调方法 onLeScan(...)中的参数 scanRecord 就是广播数据,这里同时包含广播数据扫描应答数据(均为31字节),所以长度一般就是 62 字节,BLE4.0规定,如果广播包和扫描应答包不足字节,则以0补齐。如下:

 private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

        @Override
        public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
                 ...//scanRecord存放广播和扫描应答数据,rssi存放扫描获取的rssi值
        }
    };


二、如何存储广播包数据并解析?

1.目标:扫描的设备显示List中除了显示设备名、Mac地址外,还加入完整的scanRecord数据包显示和rssi的显示。

2.步骤:(基于官方BLE Demo)

(1)在ListView的适配器创建中加入RSSI和Record列表

 //ListView Adapter,用于在listview里管理扫描到的设备
    private class LeDeviceListAdapter extends BaseAdapter {
        private ArrayList<BluetoothDevice> mLeDevices;
        private ArrayList<Integer> mRSSIs;//新加
        private ArrayList<byte[]> mRecords;//新加
        private LayoutInflater mInflator;

        public LeDeviceListAdapter() {
            super();

            mLeDevices = new ArrayList<BluetoothDevice>();
            mRSSIs = new ArrayList<Integer>();//新加
            mRecords = new ArrayList<byte[]>();//新加

            mInflator = DeviceScanActivity.this.getLayoutInflater();
        }
        ...
}

(2)改写LeDeviceListAdapter适配器中的addDevice方法

        public void addDevice(BluetoothDevice device,int rssi,byte[] scanRecord) {
            if(!mLeDevices.contains(device)) {
                mLeDevices.add(device);
                mRSSIs.add(rssi);//新加
                mRecords.add(scanRecord);//新加
            }
        }


(3)BluetoothAdapter.LeScanCallback回调方法onLeScan()中调用新的addDevice方法

    // Device scan callback.
    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

        @Override
        public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mLeDeviceListAdapter.addDevice(device,rssi,scanRecord);
                    mLeDeviceListAdapter.notifyDataSetChanged();

                }
            });
        }
    }


(4)ViewHolder和XML布局中加入对应的TextView,并改写 LeDeviceListAdapter适配器中的getView()方法:用于支持scanRecord和rssi的列表化显示

    static class ViewHolder {
        TextView deviceName;
        TextView deviceAddress;
        TextView deviceBroadcastPack;//加入广播包数据
        TextView deviceRssi;//加入RSSI
    }

       @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
            ViewHolder viewHolder;
            // General ListView optimization code.
            if (view == null) {
                view = mInflator.inflate(R.layout.listitem_device, null);
                viewHolder = new ViewHolder();
                viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
                viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
                viewHolder.deviceBroadcastPack = (TextView) view.findViewById(R.id.device_broadcastPack);
                viewHolder.deviceRssi = (TextView) view.findViewById(R.id.device_rssi);
                
                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }
            //获取设备列表、RSSI列表和广播包列表项目
            BluetoothDevice device = mLeDevices.get(i);
            int rssi = mRSSIs.get(i);
            byte[] scanRecord = mRecords.get(i);
  
            //提取信息
            final String deviceName = "设备名:" + device.getName();
            final String deviceAddr = "Mac地址:" + device.getAddress();
            final String broadcastPack ="广播包:" + bytesToHex(scanRecord);//此处调用了格式转换方法bytesToHex()将十六进制序列转String
            final String rssiString = "RSSI:" + String.valueOf(rssi) + "dB";//此处调用了String的格式转换方法valueOf()将数值类型转String
         
            //显示数据
            if (deviceName != null && deviceName.length() > 0)//显示设备名
                viewHolder.deviceName.setText(deviceName);
            else
                viewHolder.deviceName.setText(R.string.unknown_device);
            viewHolder.deviceAddress.setText(deviceAddr);//显示设备地址
            viewHolder.deviceBroadcastPack.setText(broadcastPack);//显示广播包
            viewHolder.deviceRssi.setText(rssiString);//显示RSSI
           
            return view;
        }

(5)实现scanRecord的格式转换方法:bytesToHex()---广播和扫描应答数据以十六进制格式存储在bytes[]数组中,需要转换为String后显示

    //scanRecords的格式转换
    static final char[] hexArray = "0123456789ABCDEF".toCharArray();
    private static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

三、效果展示

实现的基本效果如图,扫描到的设备列表加入了RSSI和完整广播包的显示


后注:获取到scanRecord后就能根据协议解析广播包了,根据需求提取对应字段。scanRecord的数据必须进行格式转换,否则列表显示会出现乱码,格式不匹配!



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android(Java)开发之获取BLE广播包(扫描后获取:广播数据+扫描应答数据+RSSI) 的相关文章

  • 在android 10中设置通知操作的背景颜色

    我已经做到了这一点图片链接 https i stack imgur com o0Cel jpg通过关注这篇文章https medium com dcostalloyd90 show incoming voip call notificati
  • GLSurfaceView onDrawFrame 清除行为

    我在使用 GLSurfaceView 时遇到了不同的行为 据我所知 程序有责任清除每帧的缓冲区 颜色和深度 这意味着 如果我不清除缓冲区 我将获得最后一帧的内容 或双缓冲的前一帧 似乎无论在某些设备上如何 缓冲区都会被清除 我在一些测试设备
  • 自定义相机预览问题(拉伸)

    我的相机应用程序出现问题 我的应用程序有 1 CameraActivity class和 2 CameraPreview class CameraPreview实施一个surfaceView它是从哪里调用的CameraActivity以进行
  • ViewPager 中的按钮滚动到特定页面

    我的 ViewPager 内的布局之一有一个按钮 R layout add site 我想要点击按钮的选项 它会滚动到我的特定页面 我已经可以选择滑动到特定页面 但我想同时拥有这两个选项 现在我确信有办法做到这一点 但由于某种原因 我无法弄
  • 在 Android 应用程序中动态检查权限

    继我之前的question https stackoverflow com questions 18420994 multiple permissions in androidpermisson关于运行时的 android 权限强制执行 我
  • 如何将本地主机 IP 地址转发到 Android 模拟器?

    我知道可以将端口从我的开发机器转发到 Android 模拟器 但是这是如何完成的呢 我在 android developers 网站上找到了解决方案 但我不明白他们的说明是什么意思 有人对此有明确的指示吗 我的开发机器运行的是 Window
  • 如何更改Android应用程序的页面?

    我是安卓新手 我已经设计了由许多页面组成的应用程序 任何人都有源代码或任何可以让我知道如何构建它的东西 例如 当我单击按钮时 我希望它将页面更改为下一页 谢谢你卡 运行通过第一个应用程序教程 https developer android
  • ANDROID:Webview 和 httpclient 之间共享会话

    我的 WebView 中实际上有一个记录的会话 但我也使用 httpclient 从网络发送和获取数据 我在互联网上看到不可能获取WebView的内容 所以我需要使用我的httpclient从Web服务获取数据 问题是这个Web服务使用会话
  • 如何使用 Gson 跳过空条目

    使用 Gson 反序列化 JSON 时 有没有办法跳过 JSON 数组中的空条目 text adsfsd title asdfsd null text adsfsd title asdfsd 生成的 List 有 3 个条目 第二个条目为空
  • 自定义溢出图标太大(使用 android.support.v7 和 AppCompat)

    我的目标是让操作栏为红色 但由于默认 Android 库中我的唯一选项是白色和黑色 因此我尝试对其进行自定义 我通过在 gimp 上使用油漆桶来填充白色图标并以不同的名称重新保存它 从而制作了一些自定义红色图标 我对溢出图标做了同样的事情
  • 如何禁用列表视图中的项目?

    我有一个列表视图 它是通过数据库中的记录填充的 现在我必须使一些记录可见但不可选择 我怎样才能做到这一点 这是我的代码 public class SomeClass extends ListActivity private static L
  • android - 定期轮询服务器并将响应显示为通知[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在构建一个Android应用程序 我需要每三个小时从服务器获取一些通知数据 文本 并使用NotificationManager将其
  • 减少 android studio 2.1 构建时间

    我之前使用的是 android studio 1 5 刚刚升级到 2 1 但是更新后我的构建花费了更多时间 从 15 秒到现在 2 分钟 我已经尝试过的事情如上所述 在 设置 中启用离线工作 org gradle daemon true o
  • 如何检测 Android 设备是否与 Android Wear 手表配对

    我正在创建一个 Android Wear 应用程序来扩展推送通知 当收到推送通知时 我的应用程序会从服务器下载大约 10 张图像 并在手表上显示这些附加图像 这些图像特定于 Android Wear 应用程序 不会显示在手持设备上 如何判断
  • 如何从 Android 应用程序调试共享库 [单独的项目]

    我正在开发一个 Android 应用程序并在项目中使用密集的 C 代码 首先 我将c 源代码放入项目中 并使用以下指南在Android应用程序中具有本机调试的NDK功能 http tools android com recent using
  • 如何从代码中检测 Android 设备上的双核 cpu?

    我遇到了一个似乎仅影响双核的问题Android设备运行Android 2 3 姜饼 或更大 我想就这个问题进行一次对话 但仅限于符合该标准的用户 我知道如何检查OS级别 但还没有找到任何可以明确告诉我该设备正在使用多核的信息 有任何想法吗
  • Android 基础知识:在 UI 线程中运行代码

    从在 UI 线程中运行代码的角度来看 以下两者之间有什么区别 MainActivity this runOnUiThread new Runnable public void run Log d UI thread I am the UI
  • 如何添加不确定的进度条

    我的应用程序 UI 是使用 Android 支持库构建的 但目前没有我的应用程序真正需要的 不确定的 进度条的 AppCompat 版本 我宁愿不使用任何第三方库来实现材料设计进度条 所以我想知道是否有人坐在有关为什么它不包含在支持库中的信
  • android中使用xml的新自定义字体方法

    随着 android 发布 O 预览版 添加了一个新功能 称为xml 中的字体 它很容易实现 但我没有什么疑问 让我首先添加最好的方法1 右键单击资源文件夹并前往新建 gt Android 资源目录 新的出现资源目录窗口 2 在资源类型列表
  • 错误:找不到 com.google.firebase.storage.UploadTask.TaskSnapshot 类型的符号方法 getDownloadUrl()

    在 gradle update 之前一切正常 但后来弹出了这个错误 我参考了官方文档 它提供了相同的代码 不接受 getDownloadUrl 方法 https i stack imgur com YLC2x png 我已经添加了最新的正确

随机推荐