失信企业查询_在不丢失信标的情况下找到信标:我进入Android低功耗蓝牙领域的旅程... - Go语言中文社区

失信企业查询_在不丢失信标的情况下找到信标:我进入Android低功耗蓝牙领域的旅程...


失信企业查询

Recently I’ve been playing with my own beacon monitoring library. I was curious if I could build something that would fit my needs while being significantly simpler than the AltBeacon solution from Radius Networks.

最近,我一直在玩自己的信标监视库 。 我想知道是否可以构建出满足我的需求的产品,同时又比 Radius Networks的AltBeacon解决方案简单得多。

While I was working on it, I’ve found several unexpected quirks (from bugs that lead to crashes, to bugs that lead to the necessity of the device’s factory resets) that I want to share with you today. That is not the full list of Bluetooth/BLE peculiarities, but it will save you an hour or two in the future.

当我在处理它时,我发现了一些今天要与您分享的异常现象(从导致崩溃的错误到导致设备恢复出厂设置的错误)。 那不是蓝牙/ BLE特性的完整列表,但是它将在未来节省一两个小时。

Also, I’m going to concentrate on foreground BLE scans only, and I assume the person that reads that article already has some knowledge of what BLE is (although the article should be straightforward even without any previous knowledge of a subject).

另外,我将只专注于前景BLE扫描,并且假设阅读该文章的人已经对BLE是什么有所了解(尽管即使没有任何主题的了解,该文章也应该很简单)。

With that being said, let’s jump into that rabbit hole.

话虽如此,让我们跳入那个兔子洞。

第0部分:API级别 (Part 0: API levels)

The first thing you need to consider while working with BLE APIs is which API level do you want to support. On the one hand, Android supports BLE starting from OS version 4.3, so you might want to use it as a starting point.

使用BLE API时,首先要考虑的是您要支持的API级别。 一方面,Android从OS版本4.3开始支持BLE,因此您可能希望将其用作起点。

On the other hand, you need to be aware of one nasty bug that was present in Android 4.3–4.4 and was finally fixed in Android 5.0. Long story short — if you start low-energy scans on the device which is exposed to too many BLE devices*, the app will eventually crash deep in the native code.

另一方面,您需要知道Android 4.3–4.4中存在并最终在Android 5.0中修复的一个讨厌的错误 。 长话短说-如果您在暴露于太多BLE设备*的设备上开始低能耗扫描,该应用程序最终将在本机代码中崩溃。

You might consider looking at AltBeacon’s CrashResolver class to get some ideas on how to fix that particular issue. Or you can follow the advice of Twitter’s half-joking, half-serious user called minSdkVersion and consider everything below Android 5.0 outdated. The choice is yours, make a wise one.

您可以考虑查看AltBeacon的CrashResolver类,以获取有关解决特定问题的一些想法。 或者,您可以遵循Twitter的半开玩笑,半认真的用户minSdkVersion的建议,并考虑Android 5.0以下的所有版本已过时。 选择是您的,明智的选择。

第1部分:开始LE扫描时可能出错的事情 (Part 1: Things that can go wrong when you start a LE scan)

Ok, so now that you know what API level you’re going to support, let’s talk about the things that can go wrong when you want to start the low-energy scan. Ultimately, you’re going to end up writing something like that for your very first attempt (you can also specify the scan settings and the list of scan filters, but let’s keep it simple):

好的,现在您知道将要支持的API级别,让我们来谈谈开始低能耗扫描时可能出错的地方。 最终,您将第一次尝试编写类似的内容(您也可以指定扫描设置和扫描过滤器列表,但让我们保持简单):

fun startScans() {
        BluetoothAdapter
            .getDefaultAdapter()
            .bluetoothLeScanner
            .startScan(
                object : ScanCallback() {
                    override fun onScanFailed(errorCode: Int) {
                        // todo handle scan failure
                    }


                    override fun onScanResult(callbackType: Int, result: ScanResult?) {
                        // todo handle scan result
                    }
                }
            )
    }

Ok, so far, so good. What can go wrong?

好,到目前为止,很好。 有什么问题吗?

First of all, we need to make sure that getDefaultAdapter returns an adapter and not a null reference. Yes, there’re some devices without Bluetooth chip out there in the wild, and you don’t want to learn it the hard way.

首先,我们需要确保getDefaultAdapter返回一个适配器,而不是空引用。 是的,有些设备在野外没有蓝牙芯片,并且您不想学习困难的方法。

Second, even if it’s non-null, we need to check if Bluetooth is enabled or not. The reason is that the behavior of getBluetoothLeScanner is different depending on the platform:

其次,即使它不是非null,我们也需要检查蓝牙是否启用。 原因是getBluetoothLeScanner的行为因平台而异:

  • Starting from Android 5.1, this method returns null reference if the Bluetooth is disabled at the moment, and it returns non-null reference otherwise.

    从Android 5.1开始,如果当前禁用了蓝牙,则此方法返回null引用,否则返回non-null引用。

  • But on Android 5.0, this method always returns non-null reference. Later on, the BluetoothLeScanner politely lets you handle the exception that it throws when you try to call startScan or stopScan .

    但是在Android 5.0上,此方法总是返回non-null reference 。 稍后, BluetoothLeScanner可以礼貌地让您处理尝试调用startScanstopScan时引发的异常。

Third, your code might be running in a secure environment, like Samsung Knox. These environments allow the device admins to disable and enable certain device services, including Bluetooth, remotely. So the fact that Bluetooth adapter is non-null and enabled does not guarantee you that it won’t throw a nasty SecurityException when you try to start the scan. Yes, this is what happens if the Bluetooth is disabled due to the device policy.

第三,您的代码可能在安全的环境中运行,例如Samsung Knox。 这些环境允许设备管理员远程禁用和启用某些设备服务,包括蓝牙。 因此,Bluetooth适配器非空并已启用的事实并不能保证您在尝试开始扫描时不会抛出讨厌的SecurityException 。 是的,如果由于设备策略禁用了蓝牙,就会发生这种情况。

So you end up writing something like this instead. It’s not nearly as short, but it’s much safer.

因此,您最终将改为编写类似的内容 。 它不那么短,但是更安全。

第二部分:成为一个好公民并不总是足够好 (Part 2: Being a good citizen is not always good enough)

Being a good citizen means many things, but on mobile devices, it often means respecting the fact that the battery is not infinite. You want to start the scan, get some results, and then you want to stop it so as not to drain the battery too much.

成为好公民意味着很多事情,但是在移动设备上,通常意味着要尊重电池不是无限的事实。 您想要开始扫描,获得一些结果,然后想要停止扫描,以免消耗过多电池。

You might even decide that you want it to work in a cycled fashion — scan, rest, scan, rest, scan.. you get the idea. And that approach will work just fine until you run it on the device that sports Android 7.0 or newer.

您甚至可能决定让它以循环的方式工作-扫描,休息,扫描,休息,扫描..您明白了。 在您在搭载Android 7.0或更高版本的设备上运行该方法之前,该方法将行之有效。

And the reason is that particular change. Starting from Android 7.0, you’re not allowed to start/stop scans more than five times within a 30-second window. Once you violate that restriction, Android will silently wait for 30 seconds, without callingonScanFailed method (remember ScanCallback interface?), and when that period is over, it’ll send you the scan results you were waiting for.

原因是这种特殊的变化 。 从Android 7.0开始,您不得在30秒的时间内将扫描开始/停止超过5次。 一旦您违反了该限制,Android会静默等待30秒,而不会调用onScanFailed方法(还记得ScanCallback接口吗?),当这段时间结束后,它将向您发送您正在等待的扫描结果。

What I ended up doing is setting both the scan and rest durations to exactly 6 seconds as it gives us precisely five starts/stops within that 30-second window.

我最终要做的是将扫描和休息时间都设置为准确的6秒,因为它在30秒的时间范围内为我们提供了精确的五次启动/停止。

第三部分:成为坏公民也不是明智的选择 (Part 3: Being a bad citizen is not a wise choice either)

Ok, I’m not gonna be a good citizen, I’m gonna scan like it’s 1998.

好吧,我不会成为一个好公民,我会像1998年那样进行扫描。

Bad idea.

馊主意。

Because that changeset that I mentioned above (the one that imposes no-more-than-5-starts-or-stops-within-30-second-window restriction) also changes the behavior of long-lived scans. If you’re scanning for more than 30 minutes without any stops, Android turns your scans into opportunistic ones. An opportunistic scan gets the scan result only if another non-opportunistic scan with the same scan filter sees the result matching this filter.

因为我上面提到的变更集(在30秒的窗口限制内施加超过5个开始或停止的变更)也改变了长期扫描的行为。 如果您扫描超过30分钟而没有任何停止,Android会将您的扫描变成机会扫描。 当具有相同扫描筛选器的另一个非机会扫描看到与该筛选器匹配的结果时,机会扫描才获得扫描结果

If you’ve worked with Location APIs in Android, you can think of it being something similar to PRIORITY_NO_POWER. Most likely, this is not what you want, so you need to keep that restriction in mind.

如果您在Android中使用过Location API,则可以认为它类似于PRIORITY_NO_POWER 。 最有可能的是,这不是您想要的,因此您需要牢记该限制。

Gotcha, I’m gonna scan for 29 minutes and 59 seconds exactly, what you’re gonna do to me, hahahahhaha

知道了,我要扫描29分59秒,你要对我做什么,哈哈哈哈哈

第4部分:您是否尝试过将其关闭然后再次打开? (Part 4: Have you tried turning it off and on again?)

(Ok, sorry, that was an homage to the famous sitcom called The IT Crowd — I definitely recommend it).

(好吧,对不起,这是对著名的情景喜剧《 IT人群》的致敬,我肯定会推荐它)。

Another thing you need to keep in mind while starting long-lived scans is that your user might turn Bluetooth off and then on again for whatever reason. As innocent as it sounds, this can make your beacon scans stuck in the weird state where the scan is started, but there are no detections until you restart your scans.

开始长期扫描时,您还需要牢记的另一件事是,您的用户可能出于任何原因关闭蓝牙然后再打开。 尽管听起来很清白,但是这会使信标扫描陷入开始扫描的怪异状态,但是只有在重新启动扫描后才能检测到。

I’ve found the ticket describing that behavior some time ago in the AltBeacon repository, and it seems like modern-day Android devices act the same. At least my Pixel 2 XL with Android 10 can’t detect any scan results if I start a long-lived scan and then turn Bluetooth off and on again from the quick settings menu.

我已经在AltBeacon信息库中找到了描述这种行为的票证 ,似乎现代的Android设备具有相同的作用。 如果我开始长时间扫描,然后从快速设置菜单中关闭并重新打开蓝牙,至少我的Android 10 Pixel 2 XL无法检测到任何扫描结果。

You can mitigate it by listening to the ACTION_STATE_CHANGED broadcast event and acting accordingly, or you can simply keep your scans short and restart them from time to time.

您可以通过侦听ACTION_STATE_CHANGED广播事件并采取相应的措施来缓解这种情况,或者可以使扫描时间较短并不时重新启动它们。

第5部分:恢复出厂设置为唯一选项时 (Part 5: When the factory reset is the only option)

I was thinking for a long time whether I have to put this issue in the list or not because it has been fixed since at least 2016 (Android 6.0.1 and Android N Preview seem to be the first ones that got that update). On the other hand, you might still have some users with outdated devices, and it’s better to be able to explain to them why Bluetooth does not work anymore.

我一直在考虑是否要将此问题列入列表,因为至少自2016年以来已修复此问题(Android 6.0.1和Android N Preview似乎是最早进行此更新的问题)。 另一方面,您可能仍有一些用户使用过时的设备,最好向他们解释为什么蓝牙不再起作用。

Anyway, somewhere in between Android 5.0 and Android 6.0, there was a bug that could corrupt the state of Bluetooth during BLE scans so severely that the users had to hard-reset the device. All the details of that particular bug are outlined in Google’s issue tracker, the critical thing to remember here is that you need to look for that specific logline:

无论如何,在Android 5.0和Android 6.0之间的某个地方,存在一个漏洞,该漏洞可能在BLE扫描期间严重破坏蓝牙的状态,以至于用户不得不硬重置设备。 该特定错误的所有详细信息都在Google的问题跟踪器中进行了概述,此处要记住的关键是您需要查找该特定的日志行:

10-27 16:56:18.014: E/BluetoothAdapterState(3784): Error enabling Bluetooth (enable timeout)

The problem itself is very similar to the one described in Part 0 — there is a file called bt_config.conf where the OS stores the hardware addresses of all BLE devices it sees nearby. At some point, that file grows out of proportion, so that the OS can’t load it within a dedicated time window. It looks like this is an error that for some reason can not be ignored, so the system refuses to enable BluetoothAdapter . And the only way to an up that file is — you guessed it — factory reset.

问题本身与第0部分中描述的问题非常相似-存在一个名为bt_config.conf的文件,操作系统在其中存储它在附近看到的所有BLE设备的硬件地址。 在某个时候,该文件会不成比例地增长,因此操作系统无法在专用时间窗口内加载该文件。 看来这是一个由于某些原因而不能忽略的错误,因此系统拒绝启用BluetoothAdapter 。 升级该文件的唯一方法是-您猜对了-恢复出厂设置。

Image for post
Me, thinking about the ways one can explain to the user why they should factory-reset their device after using your app (photo by Jens Johnsson on Unsplash)
我,思考着可以向用户解释为什么在使用您的应用程序后应将设备恢复出厂设置的方式( Jens JohnssonUnsplash拍摄 )

第6部分:Frag-men-ta-ti-on (Part 6: Frag-men-ta-ti-on)

Ah, fragmentation, the source of love and inspiration for so many Android developers out there.

啊,碎片化,是众多Android开发人员的爱与灵感之源。

The fragmentation issue that pops to mind is somewhat associated with the ScanCallback interface that we saw above (check the Part 2). Sometimes the scan returns you an error instead of a ScanResult (see onScanFailed method).

让人想到的碎片问题与我们在上面看到的ScanCallback接口有关(请参阅第2部分)。 有时,扫描会向您返回错误而不是ScanResult (请参见onScanFailed方法)。

There’re different kinds of errors, but there is at least one error which can be mitigated by the developer, and that is SCAN_FAILED_APPLICATION_REGISTRATION_FAILED . The way to cope with that error is by power-cycling the BluetoothAdapter — you disable it, you wait for some time until the adapter gets completely turned off, then you enable it again, and the new results start popping up.

错误的类型不同,但开发人员可以缓解至少一个错误,即SCAN_FAILED_APPLICATION_REGISTRATION_FAILED 。 解决该错误的方法是关闭BluetoothAdapter适配器的电源,然后关闭它,等待一段时间直到适配器完全关闭,然后再次启用它,然后开始弹出新结果。

Since we’re developers, we don’t want to do things manually; we want to automate stuff, so we write some code to make that recovery transparent to the user. And all goes nice until the user having the device with EMUI 9 from Huawei installs your app. The thing is, Huawei devices (I suspect Samsung devices are acting similarly) show the dialog window when we try to disable/enable the BluetoothAdapter .

由于我们是开发人员,因此我们不想手动执行操作; 我们希望使内容自动化,因此我们编写了一些代码以使恢复对用户透明。 一切顺利,直到拥有华为EMUI 9设备的用户安装了您的应用程序。 问题是,当我们尝试禁用/启用BluetoothAdapter时,华为设备(我怀疑三星设备的行为与此类似)会显示对话框窗口。

  • First of all, this irritates the user since they might be in the middle of some operation, and they don’t want to be disturbed.

    首先,这会激怒用户,因为他们可能正在执行某些操作,并且不想被打扰。

  • Second, any logic based on hand-picked delays will most likely fail, since the user will never accept the proposal to turn Bluetooth off so fast. (On the other hand, the timeframe of 0.5–1 second seems to be more than enough to me when I see a modal window having some lengthy license agreement).

    其次,任何基于手动延迟的逻辑都极有可能会失败,因为用户将永远不会接受如此快地关闭蓝牙的建议。 (另一方面,当我看到一个具有较长许可协议的模态窗口时,0.5-1秒的时间范围对我来说似乎绰绰有余)。

So you can either ignore that recovery technique or you can keep it on unless the user’s device is from Huawei.

因此,您可以忽略该恢复技术,也可以继续使用它,除非用户的设备来自华为。

第7部分:线程 (Part 7: Threading)

The last thing I wanted to mention is the one related to threading. Even though you most likely won’t see it in modern-day devices, especially the ones running stock Android from Google (like Pixel devices), you should access the BluetoothAdapter from the background thread.

我要提到的最后一件事是与线程有关的事情。 即使您很可能在现代设备中看不到它,尤其是那些运行来自Google的普通Android的设备(例如Pixel设备),也应该从后台线程访问BluetoothAdapter

And the reason is that the attempts to start and stop low energy scans used to put so much burden on the main thread, it could lead to ANR errors (see the discussion behind that particular pull-request in the AltBeacon repository. I didn’t experience it myself, but it’s better to be safe than sorry.

原因是尝试启动和停止低能耗扫描曾经给主线程造成了沉重负担,这可能导致ANR错误(请参阅AltBeacon存储库中特定拉请求的讨论。我没有自己体验一下,但是安全要比后悔好。

Ok, that was a long ride, and I hope I’ve managed to keep you entertained. :)

好的,那是一段漫长的旅程,我希望我设法让您开心。 :)

I thought about how to end that story, and then I remembered that not long ago, I was talking to my friend, who used to work with Bluetooth/BLE APIs in Android for a couple of years. He was telling me stories about how unusable Bluetooth was in Android 5.0 because the entire Bluetooth stack was reimplemented from scratch between Android 4.4 and 5.0.

我想过如何结束这个故事,然后我想起不久前,我和我的朋友聊天,他曾经在Android中使用Bluetooth / BLE API工作了几年。 他告诉我有关Android 5.0中蓝牙无法使用的故事,因为整个蓝牙堆栈是在Android 4.4和5.0之间从头开始重新实现的。

And so at some point, I told him, “Oh, you will probably love to hear that in Android 11, there will be a new stack called Gabeldorsche; do you think it will solve all the problems that you had?”

因此,在某个时候,我告诉他:“哦,您可能会想知道,在Android 11中,将会有一个名为Gabeldorsche的新堆栈; 您认为它将解决您遇到的所有问题吗?”

He fell silent.

他沉默了。

Then he looked at me with the deepest pain and sadness one can only imagine.

然后他以一种只能想象的最深的痛苦和悲伤看着我。

I knew the answer.

我知道答案。

笔记 (Notes)

* That’s not entirely correct; it’s enough to have a single BLE beacon, which periodically changes its hardware address for this problem to pop up. One developer has mentioned that they were able to reproduce it with a single Gimbal beacon, which rotates its hardware address once in every 0.78s.

*这并不完全正确; 拥有一个BLE信标就足够了,该信标会定期更改其硬件地址,以弹出此问题。 一位开发人员提到,他们能够用一个Gimbal信标对其进行重现,该信标每0.78秒旋转一次其硬件地址。

翻译自: https://proandroiddev.com/to-find-a-beacon-without-losing-it-my-journey-into-a-realm-of-bluetooth-low-energy-on-android-aa3e2c2923aa

失信企业查询

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_26739079/article/details/108498895
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-05-19 02:49:19
  • 阅读 ( 820 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢