嗨,您好! 我们是Navigine团队。 8年来,我们一直在提供集成的定位移动技术,以实现先进的室内导航和邻近解决方案。 今天,我们决定为我们的技术敞开大门,并讨论如何在Android应用程序被杀死并处于后台模式下时扫描BLE设备。
根据用于安排后台任务的机制,针对Android设备的后台BLE扫描可以分为两个不同的组。 在过去的几年中,Android更改了后台处理并添加了Doze模式,限制了隐式广播,并限制了背景行为和。 但是大多数新解决方案都不适用于旧版Android,并且所有旧解决方案都将在新版本中被Android淘汰。 因此,在本文中,我们将介绍两种在后台扫描的不同策略-API 26(奥利奥)之前和之后的版本。
API级别低于26(Oreo)的Android设备中的后台任务
对于Android Oreo之前的所有旧android版本,有一种可行的方法可使用警报管理器在后台进行扫描,并通过广播接收器提供服务。 广播接收器将收到唤醒警报并启动服务,其中Navigine SDK将被初始化并启动10秒钟。 在此时间之后,SDK将完成,服务将被终止。 可以至少间隔一分钟设置一次此警报,并且该警报将在旧设备中不受任何限制地工作。
public class NavigineReceiverExample extends BroadcastReceiver {
// here you need to receive some action from intent and depending on this action start service or set repeating alarm
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
try {
if (action.equals( "START" )) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0 ,
new Intent( "WAKE" ),
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmService = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
if (alarmService != null )
alarmService.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 5000 , 60 * 1000 , pendingIntent);
} else if (action.equals( "WAKE" )) {
context.startService( new Intent(context, NavigineService.class));
}
} catch (Throwable e) {
Logger.d(TAG, 1 , Log.getStackTraceString(e));
}
}
}
NavigineReceiverExample.java用于API级别小于26的设备
在服务类内部,您可以从广播接收器声明处理意图,并启动Navigine SDK或您要执行的其他操作。 此解决方案在版本从26开始的Android设备中将无法使用,因为在您的应用被杀死时Android不允许启动该服务,因此我们需要更好的解决方案。
从Android Oreo开始的后台任务
从API 26开始,Android在设备被杀死时开始阻止服务运行,并提供了一些解决方案来在后台运行任务。 但是主要的困难是,这些解决方案也无法在打ze模式下工作(如果用户在断开屏幕电源的情况下将设备拔下电源并保持静止一段时间,则设备会进入打ze模式)。 这是应用程序进入打ze模式时的限制列表:
Google开发者网站的屏幕截图
此外,我们将讨论找到的每个解决方案,并提供代码示例。 如果您决定不打Do睡模式,则可以使用它们中的每个。 由于我们需要每隔几分钟扫描一次BLE设备,因此我们需要运行定期的后台任务。 此外,我们将提供另外两个解决方案,这些解决方案将忽略打ze模式,但将需要更多资源或具有额外的通知。
定期作业 调度程序 。
可以从Android API级别23开始使用Job Scheduler,并替换Service + Alarm Manager来运行后台任务。 定期作业调度程序可能至少每15分钟唤醒一次,如果您需要一些作业调度程序条件,例如要求仅在设备已连接互联网,必须充电等情况下才运行,则它会带来很多机会。您应该扩展JobService类并重写onStartJob方法以启动任务。 并启动Job Scheduler运行定期作业。 不要忘记将服务添加到清单文件中。 以下是如何启动定期作业计划程序的代码示例:
JobScheduler jobShedulerService = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobShedulerService.schedule( new JobInfo.Builder(ID, new ComponentName(context, NavigineJobService.class.getName()))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) // any network type
.setPeriodic( 15 * 60 * 1000 ) // every 15 minutes
.setRequiresCharging( false ) // if device should be charging
.build());
定期工作计划
当设备未处于打not模式时,此解决方案可以完美运行,否则,它将跳过作业并开始新的时间段。 在下面的屏幕截图中,您可以看到它的运行情况。 在这种特殊情况下,我们将弹性时间添加到了定期作业中,并且它比预期的时间更早开始。
定期作业调度程序的Logcat屏幕截图。
报警。 setAndAllowWhileIdle() 和JobScheduler。
带有.set()或.setRepeating()的通用警报管理器仅在设备未处于打ze模式时才能正常工作,因为Android会忽略它们并在下一次重新计划。 使用。 setAndAllowWhileIdle()将允许调用警报,但是即使在这种情况下,作业调度程序也将被忽略。
但是,您可以添加。 setOverrideDeadline(time of毫秒)方法到您的作业计划程序,当设备退出打ze模式时它将运行此计划的作业。 这是我们的BroadcastReceiver扩展的onReceive方法的代码示例:
public class NavigineBroadcastReceiverExample extends BroadcastReceiver {
// here you need to receive some action from intent and depending on this action start service or set repeating alarm
public void onReceive (Context context, Intent intent) {
String action = intent.getAction();
try {
if (action.equals( "START" )) {
Intent wakeIntent = new Intent( "WAKE" );
wakeIntent.setClass(context, YourReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0 ,
wakeIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmService = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
if (alarmService != null )
alarmService.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 15 * 60 * 1000 , pendingIntent);
} else if (action.equals( "WAKE" )) {
JobScheduler jobShedulerService = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobShedulerService.schedule( new JobInfo.Builder( 1 , new ComponentName(context, YourJobClass.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setMinimumLatency( 0L )
.setOverrideDeadline( 15 * 60 * 1000 )
.build());
Intent wakeIntent = new Intent( "WAKE" );
wakeIntent.setClass(context, YourReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0 ,
wakeIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmService = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
if (alarmService != null )
alarmService.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + Default.NAVIGINE_JOB_SERVICE_WAKEUP_TIME * 1000 , pendingIntent);
}
} catch (Throwable e) {
Logger.d(TAG, 1 , Log.getStackTraceString(e));
}
}
}
使用警报管理器和作业计划程序的广播接收器示例
在我们的案例中,我们决定在计划了30分钟以上之后取消这些作业。 此解决方案也不能解决在打ze模式下运行后台任务的问题,但是不会像在定期作业调度程序中那样跳过作业。 请记住,即使在打ze模式下,如果您尝试每9分钟调用一次以上,这些警报也会被忽略。
使用警报管理器每5分钟醒来一次,然后启动Job Scheduler。 (不在打ze模式下)
不幸的是,这些方法都不允许您在打Do模式下执行后台任务,即使WorkManager也无法帮助您完成此任务。 但是您可以使用其他解决方案。
正在运行的前台服务。
前景服务将在不取消任何操作且不会进入打ze模式的情况下运行,因此您的后台任务将无限运行。 但是此解决方案有一个缺点,您的应用将始终在设备状态栏中固定通知。
应用程序被杀死后运行的前台服务的屏幕截图。
如您在此屏幕快照中所见,用户将始终看到无法取消的通知。 使用前台服务将增加删除应用程序的机率。 另外,值得注意的是,您的应用程序将使用更多的电源和设备资源。
这是前台服务代码示例:
public class ForegroundService extends Service {
@Override
public int onStartCommand (Intent intent, int flags, int startId) {
String input = intent.getStringExtra( "inputExtra" );
createNotificationChannel();
PendingIntent pendingIntent = PendingIntent.getActivity( this , 0 , new Intent( this , YourActivity.class), 0 );
Notification notification = new NotificationCompat.Builder( this , CHANNEL_ID)
.setContentTitle( "Foreground Service" )
.setContentText(input)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentIntent(pendingIntent)
.build();
startForeground( 1 , notification);
// here is the code you wanna run in background
return START_NOT_STICKY;
}
private void createNotificationChannel () {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel" ,
NotificationManager.IMPORTANCE_DEFAULT);
getSystemService(NotificationManager.class).createNotificationChannel(serviceChannel);
}
}
前台服务示例
使用推送通知。
甚至在“打ze”模式下运行后台任务的另一种方法,也就是从服务器向您的应用程序发送通知。 这些通知将迫使您的应用程序退出打ze模式,并且您的工作可以完成。 但是在这种情况下,您将需要在运行和维护服务上花费更多的资源。 您可以通过此链接找到有关使用Firebase Cloud Messaging运行后台服务的更多信息。
结论
随着时间的流逝,Google会增强运行后台任务的能力,因此开发人员需要提出新的解决方案和技巧,例如发送推送通知以将设备从打ze模式中唤醒。 为了捍卫android,我们可以说他们正在使用其操作来保护用户免于在后台进行不需要的和不必要的操作,这些操作将使用手机的资源并执行不需要的,有时是恶意的任务。 顺便说一句,上述解决方案将帮助您尊重Android的要求或绕过打ze模式。
From: https://hackernoon.com/background-ble-scan-in-doze-mode-on-android-devices-mx8o37my