android doze_在Android设备上以DOZE模式进行后台BLE扫描

android doze

嗨,您好! 我们是Navigine团队。 八年来,我们一直在提供集成的定位移动技术,以实现先进的室内导航和邻近解决方案。 今天,我们决定为我们的技术敞开大门,并讨论在Android应用程序被杀死并处于后台模式时如何扫描BLE设备。

根据用于安排后台任务的机制,针对Android设备的后台BLE扫描可以分为两个不同的组。 在过去的几年中,Android更改了后台处理并添加了Doze模式,限制了隐式广播,并限制了背景行为和。 但是大多数新解决方案都不适用于旧版Android,并且所有旧解决方案都将在新版本中被Android淘汰。 因此,在本文中,我们将回顾两种不同的后台扫描策略-API 26(Oreo)之前和之后的版本。

API级别低于26(Oreo)的Android设备中的后台任务

对于Android Oreo之前的所有旧android版本,有一种可行的方法可使用警报管理器在后台进行扫描并与广播接收器一起使用。 广播接收器将收到唤醒警报并启动服务,其中Navigine SDK将被初始化并启动10秒钟。 在此之后,SDK将完成,服务将被终止。 可以至少间隔一分钟设置一次此警报,并且该警报将在旧设备中不受任何限制地工作。

publicclass 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在设备被杀死时开始阻止运行的服务,并提供了一些在后台运行任务的解决方案。 但是主要的困难在于,这些解决方案在“打Do”模式下也不起作用(如果用户在断开屏幕电源的情况下将设备拔下电源并保持静止一段时间,则设备会进入“打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 in毫秒)方法到您的作业计划程序,它将在设备退出打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模式下)

不幸的是,这些方法都不允许您在打ze模式下执行后台任务,即使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会增强运行后台任务的能力,因此开发人员需要提出新的解决方案和技巧,例如发送推送通知以将设备从打Do模式唤醒。 为了捍卫android,我们可以说他们正在使用其操作来保护用户免于在后台进行不需要的和不必要的操作,这些操作将使用手机的资源并执行不需要的,有时是恶意的任务。 顺便说一句,上述解决方案将帮助您尊重Android的要求或绕过打ze模式。

翻译自: https://hackernoon.com/background-ble-scan-in-doze-mode-on-android-devices-mx8o37my

android doze

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值