当前位置: 首页 > 知识库问答 >
问题:

即使设备在信标附近静止时,didEnterRegion()和didExitRegion()之间的应用程序循环

严令秋
2023-03-14

我正在使用AltBeacon Android库(我在V2.9.2和V2.11中复制了问题)来与Onyx和Kontact.io提供的iBeacon设备集成。

以下是我如何使用AltBeacon库以及这个问题的更多细节:

>

  • 设备固定在信标附近
  • 蓝牙打开
  • 应用程序在前台运行
  • 将BeaconManager配置为在前台模式下进行扫描,设置如下:

    BeaconManager.setRegionExitPeriod(30000L);
    beaconManager.setBackgroundBetweenScanPeriod(120000L);
    beaconManager.setForegroundScanPeriod(5000L);
    beaconManager.setForegroundBetweenScanPeriod(10000L);
    beaconManager.getBeaconParsers().add(
    new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
    

    应用程序将BeaconManager设置为前台模式

    beaconManager.setBackgroundMode(false);
    
    beaconManager.bind(…)
    
    beaconManager.startMonitoringBeaconsInRegion(region);
    
    beaconManager.startRangingBeaconsInRegion(region);
    

    应用程序将信标扫描切换到后台模式:

    beaconManager.setBackgroundMode(true);
    

    几分钟后,didexitregion()将被调用,即使设备和信标没有移动并且应用程序仍然处于相同的状态。

    我发现了两个Stackoverflow问题,它们描述了相同的问题:

    >

  • Altbeacon对Onyxbeacon不稳定,反复循环通过didEnterRegion和didExitRegion

    http://stackoverflow.com/questions/40835671/altbeacon-reference-app-and-multiple-exit-entry-calls

      null

    一旦频率增加,一切似乎都很好,但解决方案是不可接受的,因为灯塔的电池寿命急剧下降。

    所有信标扫描都在后台执行(即不使用任何活动):

    import android.Manifest;
    import android.bluetooth.BluetoothAdapter;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.content.pm.PackageManager;
    import android.os.Build;
    import android.os.RemoteException;
    import android.support.annotation.NonNull;
    import org.altbeacon.beacon.Beacon;
    import org.altbeacon.beacon.BeaconConsumer;
    import org.altbeacon.beacon.BeaconManager;
    import org.altbeacon.beacon.BeaconParser;
    import org.altbeacon.beacon.Identifier;
    import org.altbeacon.beacon.MonitorNotifier;
    import org.altbeacon.beacon.RangeNotifier;
    import org.altbeacon.beacon.Region;
    import org.altbeacon.beacon.powersave.BackgroundPowerSaver;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    
    public class BeaconDataProvider implements BeaconConsumer, RangeNotifier, MonitorNotifier {
    
      private final Logger LOGGER = LogFactory.get(this);
      private final Context applicationContext;
      private final BeaconIdentifierFactory beaconIdentifierFactory;
      private final BeaconScanningListener beaconScanningListener;
    
      private BeaconManager beaconManager;
      private Collection<Region> targetedRegions;
    
      /**
       * This field is used for improving battery consumption. Do not remove it.
       */
      @SuppressWarnings({"unused", "FieldCanBeLocal"})
      private BackgroundPowerSaver backgroundPowerSaver;
    
      public BeaconDataProvider(Context applicationContext, BeaconIdentifierFactory beaconIdentifierFactory,
          BeaconScanningListener beaconScanningListener) {
        LOGGER.v("BeaconDataProvider - new instance created.");
        this.applicationContext = applicationContext;
        this.beaconIdentifierFactory = beaconIdentifierFactory;
        this.beaconScanningListener = beaconScanningListener;
        beaconManager = BeaconManager.getInstanceForApplication(applicationContext);
        LOGGER.v("BeaconManager hashCode=%s", beaconManager.hashCode());
        BeaconManager.setRegionExitPeriod(30000L);
        beaconManager.setBackgroundBetweenScanPeriod(120000L);
        beaconManager.setForegroundScanPeriod(5000L);
        beaconManager.setForegroundBetweenScanPeriod(10000L);
        beaconManager.getBeaconParsers().add(
            new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
        backgroundPowerSaver = new BackgroundPowerSaver(applicationContext);
      }
    
      public void setBackgroundMode() {
        LOGGER.i("setBackgroundMode()");
        beaconManager.setBackgroundMode(true);
      }
    
      public void setForegroundMode() {
        LOGGER.i("setForegroundMode()");
        beaconManager.setBackgroundMode(false);
      }
    
      public boolean checkAvailability() {
        return android.os.Build.VERSION.SDK_INT >= 18 && applicationContext.getPackageManager()
            .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
    
      }
    
      public boolean isBluetoothEnabled() {
        BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        boolean result = mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
        LOGGER.i("isBluetoothEnabled() -> %s", result);
        return result;
      }
    
      public boolean isLocationPermissionGranted(Context context) {
        return (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
            && context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
            == PackageManager.PERMISSION_GRANTED);
      }
    
      public void startScanning(Collection<BeaconIdentifier> targetedBeacons) {
        LOGGER.i("startScanning()");
        if (!beaconManager.isBound(this)) {
          this.targetedRegions = getRegionsForTargetedBeacons(targetedBeacons);
          beaconManager.bind(this);
        }
        else {
          LOGGER.i("Scanning already started.");
        }
      }
    
      @NonNull
      private List<Region> getRegionsForTargetedBeacons(Collection<BeaconIdentifier> beaconIdentifiers) {
        List<Region> regions = new ArrayList<>();
        for (BeaconIdentifier beaconIdentifier : beaconIdentifiers) {
          try {
            Region region = new Region(beaconIdentifier.getRegionId(), Identifier.parse(beaconIdentifier.getUuid()),
                Identifier.parse(String.valueOf(beaconIdentifier.getMajor())),
                Identifier.parse(String.valueOf(beaconIdentifier.getMinor())));
            regions.add(region);
          }
          catch (Exception e) {
            LOGGER.e("Caught exception.", e);
            LOGGER.w("Failed to create region for beaconIdentifier=%s", beaconIdentifier.getCallParamRepresentation());
          }
        }
        return regions;
      }
    
      public void stopScanning() {
        LOGGER.i("stopScanning()");
        if (beaconManager.isBound(this)) {
          for (Region region : targetedRegions) {
            try {
              beaconManager.stopMonitoringBeaconsInRegion(region);
            }
            catch (RemoteException e) {
              LOGGER.e("Caught exception", e);
            }
          }
          beaconManager.unbind(this);
        }
      }
    
      @Override
      public void didEnterRegion(Region region) {
        LOGGER.v("didEnterRegion(region=%s)", region);
        beaconScanningListener.onEnterRegion(region.getUniqueId());
        try {
          beaconManager.startRangingBeaconsInRegion(region);
        }
        catch (RemoteException e) {
          LOGGER.e("Caught Exception", e);
        }
      }
    
      @Override
      public void didExitRegion(Region region) {
        LOGGER.v("didExitRegion(region=%s)", region);
        beaconScanningListener.onExitRegion(region.getUniqueId());
        try {
          beaconManager.stopRangingBeaconsInRegion(region);
        }
        catch (RemoteException e) {
          LOGGER.e("Error", e);
        }
      }
    
      @Override
      public void didDetermineStateForRegion(int state, Region region) {
        LOGGER.v("didDetermineStateForRegion(state=%s, region=%s)", state, region);
      }
    
      @Override
      public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
        LOGGER.v("didRangeBeaconsInRegion(size=%s, region=%s, regionUniqueId=%s)", beacons.size(), region,
            region.getUniqueId());
        if (beacons.size() > 0) {
          beaconScanningListener.onBeaconsInRange(beaconIdentifierFactory.from(beacons, region.getUniqueId()));
        }
      }
    
      @Override
      public void onBeaconServiceConnect() {
        LOGGER.v("onBeaconServiceConnect()");
        beaconManager.addRangeNotifier(this);
        beaconManager.addMonitorNotifier(this);
        for (Region region : targetedRegions) {
          try {
            beaconManager.startMonitoringBeaconsInRegion(region);
          }
          catch (RemoteException e) {
            LOGGER.e("Caught exception", e);
          }
        }
      }
    
      @Override
      public Context getApplicationContext() {
        return applicationContext;
      }
    
      @Override
      public void unbindService(ServiceConnection serviceConnection) {
        LOGGER.v("unbindService()");
        applicationContext.unbindService(serviceConnection);
      }
    
      @Override
      public boolean bindService(Intent intent, ServiceConnection serviceConnection, int i) {
        LOGGER.v("bindService()");
        return applicationContext.bindService(intent, serviceConnection, i);
      }
    }
    
    public class BeaconIdentifier {
    
      private final String uuid;
      private final int major;
      private final int minor;
      private String regionId;
    
      public BeaconIdentifier(String uuid, int major, int minor) {
        this.uuid = uuid;
        this.major = major;
        this.minor = minor;
      }
    
      public int getMinor() {
        return minor;
      }
    
      public int getMajor() {
        return major;
      }
    
      public String getUuid() {
        return uuid;
      }
    
      public String getCallParamRepresentation() {
        return (uuid + "_" + major + "_" + minor).toUpperCase();
      }
    
      public String getRegionId() {
        return regionId;
      }
    
      public void setRegionId(String regionId) {
        this.regionId = regionId;
      }
    
      @Override
      public boolean equals(Object o) {
        if (o != null) {
          if (o instanceof BeaconIdentifier) {
            BeaconIdentifier other = (BeaconIdentifier) o;
            return this == other || (this.uuid.equalsIgnoreCase(other.uuid)
                && this.major == other.major && this.minor == other.minor);
          }
          else {
            return false;
          }
        }
        else {
          return false;
        }
      }
    
      @Override
      public int hashCode() {
        int result = 17;
        result = 31 * result + (uuid != null ? uuid.toUpperCase().hashCode() : 0);
        result = 31 * result + major;
        result = 31 * result + minor;
        return result;
      }
    
      @Override
      public String toString() {
        return "BeaconIdentifier{" +
            "uuid='" + uuid + '\'' +
            ", major=" + major +
            ", minor=" + minor +
            ", regionId='" + regionId + '\'' +
            '}';
      }
    }
    

    BeaconDataProvider作为每个应用程序的单个实例使用;在创建Android应用程序时,Dagger2实例化了它。它具有@ApplicationScope生命周期。

        beaconDataProvider.setForegroundMode();    
        beaconDataProvider.startScanning(targetedBeacons);
    
        beaconDataProvider.setBackgroundMode();    
    

    我错过了使用AltBeacon Android库吗?

    谢谢,阿林

  • 共有1个答案

    卫仲卿
    2023-03-14

    调用didexitregion()的根本原因是,Android bluetooth堆栈在前10秒内没有收到与该区域匹配的BLE信标广告包。(注意:此值可通过BeaconManager.SetRegionExitPeriod.配置。)

    有几个原因可能导致这些虚假的didexitregion()调用:

    1. 灯塔广告不够频繁。
    2. 一个信标正在用很低的无线电信号做广告。
    3. 附近无线电噪声太多,无法进行可靠探测。
    4. 接收设备的蓝牙天线设计不佳,导致检测不到较弱的信号。
    5. 接收设备距离太远,无法可靠地检测到信标。
    6. foregroundScanPeriod或backgroundScanPeriod设置得太短,无法保证检测

    如果您希望保留电池寿命,但不关心获取didExitRegion回调所需的时间,那么您可能希望将BeaconManager.setRegionExitPeriod.修改为30,000毫秒或更长时间,直到问题消失为止。

    以上讨论是针对Android信标库的配置,同样的理论思想适用于包括iOS核心定位在内的任何信标检测框架。您有时也会在该框架中看到虚假的退出事件。

     类似资料:
    • 我们还在不同的设备(三星手机)上使用不同的(Onyx)信标测试了这一点。问题仍然存在。请注意,这只发生在Onyx信标上(信标一)。万向节信标(系列21)在射程内仅触发一次。 我是否使用了正确的信标解析器字符串?我执行这个正确吗?我真的没有想法,我想支持这两个灯塔。 基应用程序 信标监测服务 原木

    • 我已经在Wildfly 8.2上的JEE6 Web应用程序中实现了Spring SAML SSO,以便使用ADFS2 / 3进行autenticing,但目前我无法成功进入授权过程。这是请求/响应乒乓球/乒乓球: 回应: 当我在过去两分钟内达到 6 个以上的请求时,ADFS 会断开连接,并且我会收到一个错误。可能的错误是什么?我已将所有必需的密钥添加到我的密钥库中,为什么即使状态代码响应的字段已成

    • 问题内容: 我正在寻找一种以编程方式列出我的设备所找到的附近任何蓝牙设备(可发现)的方法。我无法找到有关在Swift3.0中执行此调用的任何信息或教程。这篇QA帖子讨论了使用Swift 1.0查找这些设备并在Xcode 6(而不是最新版本8)中进行构建。 我尽力将代码从1.0转换为3.0语法,但是在运行以下代码时,Playground不返回任何内容: 问题答案: 正确使用IOBluetooth 下

    • 我试图弄清楚为什么我正在开发的应用程序在我的设备上运行时立即开始崩溃(运行iOS6.1的苹果4)。我已经在这个应用程序上工作了大约8周,这个问题似乎是突然出现的。 当我在模拟器上运行应用程序时,它运行良好。当我尝试在我的设备上运行它时,它会崩溃,并在以下位置中断: 崩溃发生在App委托方法之前 有人打电话来。 我已经看过了设备崩溃日志,但我没有看到任何关于发生了什么的线索。有人知道我可以从哪里开始

    • 在android API 29模拟器上运行,如果您最小化一个应用程序并查看应用程序的最小化列表,它们每个都有一个与其相关联的图标: 在我的例子中,在卡片的顶部有一个与google chrome相关联的圆形chrome图标。 我的问题是:你如何设置这个图标,它叫什么,什么时候引入的(我基本上是在找关于它的文档) 非常感谢任何帮助