进入区域时在前台开始信标测距
Start beacon ranging on foreground when entering region
目标
应用程序在后台时的 EddyStone EID 信标检测。
一旦用户进入信标区域,应用程序应开始测距并向我的服务器发出 http 调用,以便我收到检测通知。即使是短期访问。
由于 Android 的后台扫描限制,我正在考虑使用区域 bootstrap 并在进入信标区域后立即启动前台服务。这让我更喜欢直接使用前台服务,所以我不会一直看到通知。
问题
我的应用程序基于 AltBeacon 参考应用程序。我尝试在用户进入该区域后立即启动前台服务。前台服务启动,但测距通知程序未显示任何信标检测。我尝试的替代方法是启动 foregroundservice 并在 didDetermineState 方法回调中进行测距,但这不起作用,因为我必须启用和禁用区域 bootstrap 才能这样做,这将再次触发 didDetermineState 回调方法.
如何在后台检测信标(无延迟)并开始测距而不一直使用前台服务?
代码+日志
public class AppController extends MultiDexApplication implements BootstrapNotifier,
RangeNotifier, BeaconConsumer {
private static final String TAG = "BEACON:";
private static AppComponent appComponent;
private static AppController instance;
private RegionBootstrap regionBootstrap;
private BackgroundPowerSaver backgroundPowerSaver;
private boolean isScanningOnForeground = false;
private BeaconManager beaconManager;
@Override
public void onCreate() {
super.onCreate();
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.setDebug(true);
beaconManager.getBeaconParsers().clear();
beaconManager.getBeaconParsers()
.add(new BeaconParser().setBeaconLayout(BeaconParser.EDDYSTONE_UID_LAYOUT));
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
backgroundPowerSaver = new BackgroundPowerSaver(this);
instance = this;
this.getAppComponent().inject(this);
}
public AppComponent getAppComponent() {
if (appComponent == null) {
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
}
return appComponent;
}
public static AppController getInstance() {
return instance;
}
@Override
public void didEnterRegion(Region region) {
Log.d(TAG, "(didEnterRegion) Beacon detected in region!");
startMonitoringOnForeground();
}
public void disableMonitoring() {
if (regionBootstrap != null) {
regionBootstrap.disable();
regionBootstrap = null;
}
}
public void enableMonitoring() {
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
}
private void enableRanging() {
Log.d(TAG, "Enable ranging");
beaconManager.removeAllRangeNotifiers();
beaconManager.addRangeNotifier(this);
try {
beaconManager.startRangingBeaconsInRegion(new Region("rangingRegion",
null, null, null));
Log.d(TAG, "Ranging started..");
} catch (RemoteException e) {
Log.d(TAG, ">>>>>>>>>>> START RANGING EXCEPTION!");
e.printStackTrace();
}
}
private void disableRanging() {
Log.d(TAG, "Disable ranging.");
try {
beaconManager.stopRangingBeaconsInRegion(new Region("rangingRegion",
null, null, null));
} catch (RemoteException e) {
Log.d(TAG, ">>>>>>>>>>> START RANGING EXCEPTION!");
e.printStackTrace();
}
beaconManager.removeAllRangeNotifiers();
}
private void startMonitoringOnForeground() {
if (isScanningOnForeground) {
Log.d(TAG, "Ignore method call, already scanning in foreground");
return;
}
isScanningOnForeground = true;
disableMonitoring();
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.drawable.app_icon);
builder.setContentTitle("Scanning for Beacons");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("My Notification Channel ID",
"My Notification Name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("My Notification Channel Description");
NotificationManager notificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
builder.setChannelId(channel.getId());
}
beaconManager.enableForegroundServiceScanning(builder.build(), 456);
// For the above foreground scanning service to be useful, you need to disable
// JobScheduler-based scans (used on Android 8+) and set a fast background scan
// cycle that would otherwise be disallowed by the operating system.
//
beaconManager.setEnableScheduledScanJobs(false);
beaconManager.setBackgroundBetweenScanPeriod(30000);
beaconManager.setBackgroundScanPeriod(1100);
enableRanging();
enableMonitoring();
}
private void stopMonitoringOnForeground() {
if (!isScanningOnForeground) {
Log.d(TAG, "Not stopping since foreground scanning isn't active.");
return;
}
isScanningOnForeground = false;
disableMonitoring();
beaconManager.disableForegroundServiceScanning();
enableMonitoring();
disableRanging();
}
@Override
public void didExitRegion(Region region) {
Log.d(TAG, "(didExitRegion) No beacons anymore");
stopMonitoringOnForeground();
}
@Override
public void didDetermineStateForRegion(int state, Region region) {
Log.d(TAG,
"Determine state for region " + (state == 1 ? "INSIDE" : "OUTSIDE (" + state + ")"));
}
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> collection, Region region) {
Log.d(TAG, "Did range beacons in region");
for (Beacon beacon : collection) {
Log.d(TAG, "Beacon: " + beacon.getId1().toString());
}
}
@Override
public void onBeaconServiceConnect() {
Log.d(TAG, "OnBeaconServiceConnect");
}
}
D/BEACON:: Determine state for region OUTSIDE (0)
D/BEACON:: Determine state for region INSIDE
D/BEACON:: (didEnterRegion) Beacon detected in region!
D/BEACON:: Enable ranging
D/BEACON:: Ranging started..
D/BEACON:: Determine state for region OUTSIDE (0)
D/BEACON:: Determine state for region INSIDE
D/BEACON:: (didEnterRegion) Beacon detected in region!
D/BEACON:: Ignore method call, already scanning in foreground
D/BEACON:: (didEnterRegion) Beacon detected in region!
D/BEACON:: Ignore method call, already scanning in foreground
您展示的技术可能接近工作。主要问题是在前台服务 "bound".
之前你无法开始测距
前台服务启动时间较短,启动后才能开始测距。可以尝试的几个选项:
您可以在禁用 RegionBootstrap 之前开始测距。然后将其余代码保持原样,并且在服务启动时它应该仍然处于打开状态。 (不肯定这会起作用。)
如果上述方法不起作用,请尝试仅在获得 isScanningOnForeground 为 true 的 didDeermineState 回调后才开始测距。这将确保服务已绑定。
解决此问题的最好和最干净的方法是停止使用 RegionBootstrap,因为它在绑定服务时不提供直接回调。 BeaconManager.bind(this) 调用确实提供了一个 onBeaconServiceConnect 回调,可用于启动监控和测距。但是,此选项需要最多的代码更改。
您可能还希望设置信标 manager.setDebug(true) 并在测距开始后查找消息,为您提供有关其为何不工作的线索。
目标
应用程序在后台时的 EddyStone EID 信标检测。 一旦用户进入信标区域,应用程序应开始测距并向我的服务器发出 http 调用,以便我收到检测通知。即使是短期访问。
由于 Android 的后台扫描限制,我正在考虑使用区域 bootstrap 并在进入信标区域后立即启动前台服务。这让我更喜欢直接使用前台服务,所以我不会一直看到通知。
问题
我的应用程序基于 AltBeacon 参考应用程序。我尝试在用户进入该区域后立即启动前台服务。前台服务启动,但测距通知程序未显示任何信标检测。我尝试的替代方法是启动 foregroundservice 并在 didDetermineState 方法回调中进行测距,但这不起作用,因为我必须启用和禁用区域 bootstrap 才能这样做,这将再次触发 didDetermineState 回调方法.
如何在后台检测信标(无延迟)并开始测距而不一直使用前台服务?
代码+日志
public class AppController extends MultiDexApplication implements BootstrapNotifier,
RangeNotifier, BeaconConsumer {
private static final String TAG = "BEACON:";
private static AppComponent appComponent;
private static AppController instance;
private RegionBootstrap regionBootstrap;
private BackgroundPowerSaver backgroundPowerSaver;
private boolean isScanningOnForeground = false;
private BeaconManager beaconManager;
@Override
public void onCreate() {
super.onCreate();
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.setDebug(true);
beaconManager.getBeaconParsers().clear();
beaconManager.getBeaconParsers()
.add(new BeaconParser().setBeaconLayout(BeaconParser.EDDYSTONE_UID_LAYOUT));
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
backgroundPowerSaver = new BackgroundPowerSaver(this);
instance = this;
this.getAppComponent().inject(this);
}
public AppComponent getAppComponent() {
if (appComponent == null) {
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
}
return appComponent;
}
public static AppController getInstance() {
return instance;
}
@Override
public void didEnterRegion(Region region) {
Log.d(TAG, "(didEnterRegion) Beacon detected in region!");
startMonitoringOnForeground();
}
public void disableMonitoring() {
if (regionBootstrap != null) {
regionBootstrap.disable();
regionBootstrap = null;
}
}
public void enableMonitoring() {
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
}
private void enableRanging() {
Log.d(TAG, "Enable ranging");
beaconManager.removeAllRangeNotifiers();
beaconManager.addRangeNotifier(this);
try {
beaconManager.startRangingBeaconsInRegion(new Region("rangingRegion",
null, null, null));
Log.d(TAG, "Ranging started..");
} catch (RemoteException e) {
Log.d(TAG, ">>>>>>>>>>> START RANGING EXCEPTION!");
e.printStackTrace();
}
}
private void disableRanging() {
Log.d(TAG, "Disable ranging.");
try {
beaconManager.stopRangingBeaconsInRegion(new Region("rangingRegion",
null, null, null));
} catch (RemoteException e) {
Log.d(TAG, ">>>>>>>>>>> START RANGING EXCEPTION!");
e.printStackTrace();
}
beaconManager.removeAllRangeNotifiers();
}
private void startMonitoringOnForeground() {
if (isScanningOnForeground) {
Log.d(TAG, "Ignore method call, already scanning in foreground");
return;
}
isScanningOnForeground = true;
disableMonitoring();
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.drawable.app_icon);
builder.setContentTitle("Scanning for Beacons");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("My Notification Channel ID",
"My Notification Name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("My Notification Channel Description");
NotificationManager notificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
builder.setChannelId(channel.getId());
}
beaconManager.enableForegroundServiceScanning(builder.build(), 456);
// For the above foreground scanning service to be useful, you need to disable
// JobScheduler-based scans (used on Android 8+) and set a fast background scan
// cycle that would otherwise be disallowed by the operating system.
//
beaconManager.setEnableScheduledScanJobs(false);
beaconManager.setBackgroundBetweenScanPeriod(30000);
beaconManager.setBackgroundScanPeriod(1100);
enableRanging();
enableMonitoring();
}
private void stopMonitoringOnForeground() {
if (!isScanningOnForeground) {
Log.d(TAG, "Not stopping since foreground scanning isn't active.");
return;
}
isScanningOnForeground = false;
disableMonitoring();
beaconManager.disableForegroundServiceScanning();
enableMonitoring();
disableRanging();
}
@Override
public void didExitRegion(Region region) {
Log.d(TAG, "(didExitRegion) No beacons anymore");
stopMonitoringOnForeground();
}
@Override
public void didDetermineStateForRegion(int state, Region region) {
Log.d(TAG,
"Determine state for region " + (state == 1 ? "INSIDE" : "OUTSIDE (" + state + ")"));
}
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> collection, Region region) {
Log.d(TAG, "Did range beacons in region");
for (Beacon beacon : collection) {
Log.d(TAG, "Beacon: " + beacon.getId1().toString());
}
}
@Override
public void onBeaconServiceConnect() {
Log.d(TAG, "OnBeaconServiceConnect");
}
}
D/BEACON:: Determine state for region OUTSIDE (0)
D/BEACON:: Determine state for region INSIDE
D/BEACON:: (didEnterRegion) Beacon detected in region!
D/BEACON:: Enable ranging
D/BEACON:: Ranging started..
D/BEACON:: Determine state for region OUTSIDE (0)
D/BEACON:: Determine state for region INSIDE
D/BEACON:: (didEnterRegion) Beacon detected in region!
D/BEACON:: Ignore method call, already scanning in foreground
D/BEACON:: (didEnterRegion) Beacon detected in region!
D/BEACON:: Ignore method call, already scanning in foreground
您展示的技术可能接近工作。主要问题是在前台服务 "bound".
之前你无法开始测距前台服务启动时间较短,启动后才能开始测距。可以尝试的几个选项:
您可以在禁用 RegionBootstrap 之前开始测距。然后将其余代码保持原样,并且在服务启动时它应该仍然处于打开状态。 (不肯定这会起作用。)
如果上述方法不起作用,请尝试仅在获得 isScanningOnForeground 为 true 的 didDeermineState 回调后才开始测距。这将确保服务已绑定。
解决此问题的最好和最干净的方法是停止使用 RegionBootstrap,因为它在绑定服务时不提供直接回调。 BeaconManager.bind(this) 调用确实提供了一个 onBeaconServiceConnect 回调,可用于启动监控和测距。但是,此选项需要最多的代码更改。
您可能还希望设置信标 manager.setDebug(true) 并在测距开始后查找消息,为您提供有关其为何不工作的线索。