Android BLE 被动扫描

2024-02-16

我想在我的 Android 应用程序中被动扫描 BLE 广告商。
但我找不到如何做到这一点。

  • 根据蓝牙4.0核心规范,存在被动扫描模式。
    第6卷:核心系统包[低能耗控制器卷],
    D 部分:4.1 被动扫描
    https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=282159 https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=282159
    “设备可以使用被动扫描来查找该区域的广告设备。”

  • 而且,android 有确定扫描类型的参数。 (主动/被动)
    http://androidxref.com/4.3_r2.1/xref/external/bluetooth/bluedroid/stack/btm/btm_ble_gap.c#555 http://androidxref.com/4.3_r2.1/xref/external/bluetooth/bluedroid/stack/btm/btm_ble_gap.c#555
    “scan_type:主动扫描或被动扫描”

  • 同时,iOS可以被动扫描ble广告商。 (遗憾的是,仅限后台模式)
    http://lists.apple.com/archives/bluetooth-dev/2012/May/msg00041.html http://lists.apple.com/archives/bluetooth-dev/2012/May/msg00041.html
    “当应用程序在后台运行时,iOS 会执行被动扫描。”

但我不知道是否可以使用被动扫描模式。

Question:可以在 Android 上使用“被动扫描”吗?如果可以的话,如何使用这个功能?


和...之间的不同active and passive扫描是这样的active扫描请求SCAN_RESPONSE来自广告商的数据包。这是通过发送一个来完成的SCAN_REQUEST检测到广告后发送数据包。两者的信息(有效负载)将在scanRecord设备发现回调的参数。

来自核心规格 https://www.bluetooth.org/en-us/specification/adopted-specifications:

设备可以使用主动扫描来获取更多信息关于设备 这对于填充用户界面可能有用。主动扫描涉及更多 链路层广告消息。

因此,对于任何用例,这两种扫描类型都没有必要有所不同。

但是,如果您想在后台收听广告,那么您需要自己创建一个Service- 没有内置功能(从 Android 4.4 开始)。


对于后台扫描,请以此为例。但扫描将在您的应用程序被系统终止(或被用户停止)时结束。

通过 AlarmManager 启动 PendingIntent(在应用程序的任何位置,必须至少运行一次才能启动服务......)

AlarmManager alarmMgr = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getActivity(), BleScanService.class);
PendingIntent scanIntent = PendingIntent.getService(getActivity(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime(), intervalMillis, scanIntent);

BleScan服务

public class BleScanService extends Service implements LeScanCallback {

private final static String TAG = BleScanService.class.getSimpleName();

private final IBinder mBinder = new LocalBinder();

private BluetoothManager mBluetoothManager;

private BluetoothAdapter mBluetoothAdapter;

public class LocalBinder extends Binder {
        public BleScanService getService() {
            return BleScanService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        initialize();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        long timeToScan = preferences.scanLength().get();
        startScan(timeToScan);

        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * Initializes a reference to the local bluetooth adapter.
     * 
     * @return Return true if the initialization is successful.
     */
    public boolean initialize() {
        // For API level 18 and above, get a reference to BluetoothAdapter
        // through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                Log.e(TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }

        if (mBluetoothAdapter == null) {
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            if (mBluetoothAdapter == null) {
                Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
                return false;
            }
        }

        Log.d(TAG, "Initialzed scanner.");
        return true;
    }

    /**
     * Checks if bluetooth is correctly set up.
     * 
     * @return
     */
    protected boolean isInitialized() {
        return mBluetoothManager != null && mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
    }

    /**
     * Checks if ble is ready and bluetooth is correctly setup.
     * 
     * @return
     */
    protected boolean isReady() {
        return isInitialized() && isBleReady();
    }

    /**
     * Checks if the device is ble ready.
     * 
     * @return
     */
    protected boolean isBleReady() {
        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
    }

    @Override
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
        Log.d(TAG, "Found ble device " + device.getName() + " " + device.getAddress());
        broadcastOnDeviceFound(device, scanRecord);
    }

    /**
     * Broadcasts a message with the given device.
     * 
     * @param device
     * @param scanRecord 
     */
    protected void broadcastOnDeviceFound(final BluetoothDevice device, byte[] scanRecord) {
        assert device != null : "Device should not be null.";

        Intent intent = new Intent(BleServiceConstants.ACTION_DEVICE_DISCOVERED);
        intent.putExtra(BleServiceConstants.EXTRA_DEVICE_DISCOVERED_DEVICE, device);
        intent.putExtra(BleServiceConstants.EXTRA_DEVICE_DISCOVERED_SCAN_RECORD, scanRecord);
        sendBroadcast(intent);
    }

    /**
     * Starts the bluetooth low energy scan It scans at least the
     * delayStopTimeInMillis.
     * 
     * @param delayStopTimeInMillis
     *            the duration of the scan
     * @return <code>true</code> if the scan is successfully started.
     */
    public boolean startScan(long delayStopTimeInMillis) {
        if (!isReady())
            return false;

        if (preferences.shouldScan().get()) {
            if (delayStopTimeInMillis <= 0) {
                Log.w(TAG, "Did not start scanning with automatic stop delay time of " + delayStopTimeInMillis);
                return false;
            }

            Log.d(TAG, "Auto-Stop scan after " + delayStopTimeInMillis + " ms");
            getMainHandler().postDelayed(new Runnable() {

                @Override
                public void run() {
                    Log.d(TAG, "Stopped scan.");
                    stopScan();
                }
            }, delayStopTimeInMillis);
        }
        return startScan();
    }

    /**
     * @return an handler with the main (ui) looper.
     */
    private Handler getMainHandler() {
        return new Handler(getMainLooper());
    }

    /**
     * Starts the bluetooth low energy scan. It scans without time limit.
     * 
     * @return <code>true</code> if the scan is successfully started.
     */
    public boolean startScan() {
        if (!isReady())
            return false;

        if (preferences.shouldScan().get()) {
            if (mBluetoothAdapter != null) {
                Log.d(TAG, "Started scan.");
                return mBluetoothAdapter.startLeScan(this);
            } else {
                Log.d(TAG, "BluetoothAdapter is null.");
                return false;
            }
        }
        return false;
    }

    /**
     * Stops the bluetooth low energy scan.
     */
    public void stopScan() {
        if (!isReady())
            return;

        if (mBluetoothAdapter != null)
            mBluetoothAdapter.stopLeScan(this);
        else {
            Log.d(TAG, "BluetoothAdapter is null.");
        }
    }

    @Override
    public void onDestroy() {
        preferences.edit().shouldScan().put(false).apply();
        super.onDestroy();
    }
}

这些常量只是用于分配意图操作和额外名称的字符串。还有另一个首选项存储存储扫描阶段应该持续多长时间...您可以根据需要轻松替换它。

然后,您必须使用与上述操作名称匹配的意图过滤器来注册广播接收器(BleServiceConstants.ACTION_DEVICE_DISCOVERED)

public class DeviceWatcher extends BroadcastReceiver {

  @Override
    public void onReceive(Context context, Intent intent) {
        BluetoothDevice device =  intent.getParcelableExtra(BleServiceConstants.EXTRA_DEVICE_DISCOVERED_DEVICE);

  // do anything with this information

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

Android BLE 被动扫描 的相关文章

  • 如何使用 ProGuard 将所有方法保留在类中

    我使用 ProGuard 来优化我的 Android 应用程序 然而 对于 Android 仪器测试 我需要一些 但不是全部 类来保留所有成员 我尝试了各种方法 最后一个是 keepclassmembers public class com
  • “_加载小部件时出现问题”消息

    加载小部件时 如果找不到资源或其他内容 则会显示 加载小部件时出现问题 就这样 惊人的 此消息保留在主屏幕上 甚至没有说明加载时遇到问题的小部件 我通过反复试验弄清楚了这一点 但我想知道发生这种情况时是否有任何地方可以找到错误消息 Andr
  • Android 中用虚拟键盘捕捉按键按下的情况?

    使用物理键盘 您可以使用按键监听器 http developer android com reference android text method KeyListener html 就像是 myEditText setOnKeyListe
  • v7 RecyclerView NullPointerException AccessibilityDelegateCompat.getBridge()

    运行我的 recyclerview 列表片段时出现此错误 java lang NullPointerException Attempt to invoke virtual method java lang Object android su
  • 如何在 Android 中使用 Assets 中预加载的 SQLite 数据库

    我想用preloaded database在我的应用程序中意味着尝试在安装 apk 时获取数据库 以便可以使用已保存在其中的数据 我复制了 成分 db 文件位于资产文件夹中 并使用以下代码 但这会出现错误 从资源文件复制数据库时出现问题 我
  • 在 Anko DSL 中创建自定义 View/ViewGroup 类

    我想创建一个自定义视图 它只是一些 Android 视图的包装 我考虑创建一个自定义 ViewGroup 来管理其子视图的布局 但我不需要这么复杂 我基本上想做的是 class MainActivity verticalLayout tex
  • Android 应用被 Google Play 拒绝

    我最近向 Google Play 商店提交了一个 Android 应用程序 但收到一条消息说我的应用程序已被拒绝 我不确定问题是什么 也找不到确切的解决方案 拒绝原因 违反了禁止行为条款 内容政策 经过定期审核后 我们确定您的应用程序支持
  • Firestore - RecycleView - 图像持有者

    我不知道如何编写图像的支架 我已经设置了 2 个文本 但我不知道图像的支架应该是什么样子 你能帮我告诉我图像的文字应该是什么样子才能正确显示吗 holder artistImage setImageResource model getArt
  • Android:应用内计费V3超时返回哪个响应码?

    出现网络超时情况时 Google Play 应用内结算服务 ice er V3 将返回哪些响应状态代码 它的所有功能都是统一的吗 我将在这里描述我的发现 我通过拔掉主机插头 在安装了全功能 GP GP Store V3 10 10 GP S
  • 如何使用 Firebase UI 在 recyclerView 中显示时从 Firebase 数据库中的子级引用父级

    我目前正在引用 quote text 子项 这些数据显示在recyclerview using firebase用户界面 我想获取 quote text 的父名称 如何获取 quote text 孩子的父母姓名 When user clic
  • Firebase ANR - 甚至不使用 firebase

    随着 GMS 的最新更新 引入了新的 ANR 如果您退出应用程序 然后返回 则 ANR 就会初始化 线程跟踪似乎指向 Firebase 但我什至从未开始使用 Firebase 我猜它一定是新的 GMS 包的一部分 06 11 00 34 0
  • 以编程方式将 TextView 添加到主屏幕小部件

    我想以编程方式将文本视图控件添加到我的主屏幕小部件 在下面的示例中 我使用 TextView 填充 Linearlayout 但是这里应该如何使用 RemoteView 它只接受 xml 资源布局作为参数 public class MyWi
  • onClick 未在带有子项的 LinearLayout 上触发

    我有一个自定义的 LinearLayout 和一个较小的 TextView 子级 我希望能够单击 TextView 未覆盖的区域 因此我将 clickable true 和 onclicklistener 设置为 LinearLayout
  • Android - 如何更改 TimePicker 中的文本颜色?

    我正在使用 TimePicker 到 LinearLayout 中 背景颜色 黑色 但是 我看不到 TimePicker 中的数字 并且我需要在布局中将背景颜色设置为黑色 如何更改 TimePicker 中的 textColor 我已经尝试
  • 在为 Android 实现 Google 登录时,任务“:app:transformClassesWithDexForDebug”执行失败

    我正在尝试为 Android 实现 Google 登录 并且我正在按照以下说明进行操作 https developers google com identity sign in android start integrating https
  • 在Android Studio中更改项目主题?

    我使用浅色主题创建了一些项目 现在我想将其更改为深色 但我不知道该怎么做 顺便说一句 我不是问如何在代码中做到这一点 只是问如何更改项目的默认主题 在 AndroidManifest xml 的 application 标签下 您可以设置您
  • NoClassDefFoundError:无法解析:Landroid/support/v7/appcompat/R$styleable

    新手尝试完成 Google 提供的我的第一个应用程序教程 在这个致命异常的过程中 我确实导入了很多随机包来消除许多事情的 无法解析 错误 例如 ActionBarActivity EditText Fragment LayoutInflat
  • 无法将类型“System.IO.Stream”隐式转换为“Java.IO.InputStream”

    我提到了一些类似的问题 但没有一个涉及IO 当我使用时 我在java中使用了相同的代码Eclipse 那次就成功了 但现在我尝试在中使用这段代码Mono for Android C 它不起作用 我正在尝试运行此代码来创建一个InputStr
  • Android - 9 补丁

    我正在尝试使用 9 块图片创建一个新的微调器背景 我尝试了很多方法来获得完美的图像 但都失败了 s Here is my 9 patch 当我用Draw 9 patch模拟时 内容看起来不错 但是带有箭头的部分没有显示 或者当它显示时 这部
  • Android AutoCompleteTextView 带芯片

    我不确定我是否使用了正确的词语来描述此 UI 功能 但我已附上我希望在我的应用程序中实现的目标的快照 它由 Go SMS 使用 用户在编辑文本中键入联系人 在用户从完成下拉列表中选择联系人后 该联系人将被插入到编辑文本中 如附图所示 编辑文

随机推荐

  • 使用更少的代码进行脑筋急转弯

    这是我尝试编写的 R 问题的一个小脑筋急转弯 假设桌子上有 15 支蜡烛 在三个不同的回合中 您将根据蜡烛是否已经点燃来点燃或熄灭给定的蜡烛 因此 如果给定的蜡烛已经点燃 那么您的行动就是将其熄灭 另一方面 如果蜡烛没有点燃 那么你的行动就
  • CreateDC() 导致 glutInit() 失败?

    我编写了一段代码来创建一个窗口并在其中绘制一个形状 include
  • Jquery - 克隆表行时禁用 select2 下拉列表

    我有一个带有四个 select2 下拉列表的表 当我克隆该行以复制它时 新行的下拉列表被禁用 我无法单击它们 我必须在代码中添加什么才能激活它们 HTML 表格 table width 100 tbody tr th class tab h
  • 如何匹配不包含单词的行[重复]

    这个问题在这里已经有答案了 我想知道如何使用Python风格的正则表达式来匹配不包含特定单词的行 只需使用正则表达式 不涉及Python函数 Example PART ONE OVERVIEW 1 Chapter 1 Introductio
  • Postman中如何自动获取token

    我使用 Postman 桌面应用程序进行 Web API 测试 我有很多控制器 每个控制器都需要一个令牌 首先我得到Bearer token然后将其复制到其他请求中 该令牌有时间限制 我可以自动获取令牌 然后自动将其设置为所有其他请求吗 好
  • 生成的 JsonResult 中的属性名称大小写不一致

    我已经为此摸不着头脑有一段时间了 并决定让 SO 社区来尝试一下 我有一些响应客户端 POST 的操作 这些操作执行简单的任务并返回JsonResult从具有简单布尔 Success 属性的匿名类构建 如果成功 或者返回PartialVie
  • 错误:sonar.sources 的值无效

    尝试运行 sonartest 但失败了 我的詹金斯控制台输出 Started by user Badal Singh Building on master in workspace C Program Files x86 Jenkins w
  • 从设备获取屏幕截图时出现意外错误:EOF

    当我尝试在 Android Studio 中从我的某些项目中截取屏幕截图 通过按相机图标 时 我收到以下消息 Unexpected error while obtaining screenshot from device EOF 如果我在打
  • 如何比较java中的多个类?

    现在 我已经编写了对整数和字符串数组进行排序的比较器 从代码中可以看出 如果两个类不相同 则 String 类将采用大于值 但是 这仅允许两个类 如果我想向数组添加另一个基本类型 例如 Float 怎么办 我必须向 if else 语句添加
  • x64 上类方法崩溃的地址

    The AddressOf运算符仅适用于标准 bas 模块内的方法 我使用以下代码来检索类方法的地址 Option Explicit If VBA7 Then Private Declare PtrSafe Function DispCal
  • 如何在 Delphi 中格式化 Unix 时间戳?

    I have var timestamp Longint timestamp Round Now 25569 0 Unix start date in Delphi terms 86400 我在一些 MySql 中使用它作为主键 但我也想格
  • Material UI 组件上的 CSS

    我在尝试使用 Material UI 时不断遇到麻烦 我有一个 Material UI 表 我正在尝试将文本居中对齐 Material UI 标准 CSS 有一个名为 MuiTableCell root 60 的元素 它有一个带有 text
  • 在笑话测试中将通用数据放在哪里

    我不知道如何组织笑话测试的代码 我所有的测试都在 tests 和我所有的嘲笑 mocks 现在我有一些想要在测试之间共享的数据 它们不是现有函数的模拟 它们只是我想在不同文件中使用的一些 javascript 对象 我应该创建一个 data
  • Pentaho j_spring_security_check 混合内容。 HTTPS 到 HTTP

    我已根据需要在 Tomcat 8 5 和 OpenJDK 1 8 上安装了 Pentaho 9 x 在它前面的是带有 mod proxy http 的 Apache 2 4 我的网站使用 HTTPS 提供服务 并且我有以下代理规则 Requ
  • 了解 OCR 的 Freeman 链码

    请注意 我确实在寻找问题的答案 我是not寻找一些源代码或一些学术论文的链接 我已经使用了源代码 并且我已经阅读了论文 但仍然没有弄清楚这个问题的最后部分 我正在研究一些快速屏幕字体 OCRing 并且取得了很好的进展 我已经找到基线 分离
  • 如何在浏览器刷新时清除会话存储,但这不应在单击浏览器后退按钮时清除

    我只想在页面刷新时清除会话存储 而不是在 单击浏览器后退按钮 或单击前进 想要使用 angularjs javascript 来实现这个 我在单击后退按钮时将数据保存在会话存储中 因此我想在单击浏览器刷新按钮时清除相同的数据 但不应清除后退
  • 如何在 ASP.NET MVC 应用程序的 Razor 引擎中使用 Html.Displar 渲染 ModelMetadata 对象?

    我正在尝试利用DisplayTemplatesrazor engine 中的功能可自动渲染我的显示视图 我扫描我的Model找到正确的ModelMetadata对于我想要显示的每个属性 但我无法使用以下方式渲染属性Html DisplayF
  • 如何在 docker 中启用/禁用 buildkit?

    我从文档中得到了这个命令 但我真的不知道如何使用它或者我应该从哪里开始移动 我是 docker 的新手 而且概念对我来说仍然很难消化 DOCKER BUILDKIT 1 docker build 如何使用此命令在 docker 引擎中启用
  • 如何循环遍历特定标签内的每个标签?

    我正在创建一个函数 它有一个 for 循环 该循环遍历特定 div 中的每个标签 例如 这里是 div div div
  • Android BLE 被动扫描

    我想在我的 Android 应用程序中被动扫描 BLE 广告商 但我找不到如何做到这一点 根据蓝牙4 0核心规范 存在被动扫描模式 第6卷 核心系统包 低能耗控制器卷 D 部分 4 1 被动扫描 https www bluetooth or